InvalidTokenError: Invalid URL Nextjs Server Request to External ExpressJs API

I followed this and set up a Nextjs app with the Auth0 Universal Login.

I created an endpoint in this NextJs app that creates an accessToken and sends it along to an external API I have control over (different domain):

import { getAccessToken, withApiAuthRequired } from '@auth0/nextjs-auth0';

const { accessToken } = await getAccessToken(req, res, {
  scopes: ['create:foo'],
  authorizationParams: {
    audience: process.env.MY_AUDIENCE,
  },
});

await apiCall(
  `${process.env.MY_EXTERNAL_API_URL}/api/myendpoint`,
  {
    headers: {
      Authorization: `Bearer ${accessToken}`,
      'Content-Type': 'application/json',
    },
    body: JSON.stringify({ companyId }),
  },
);

The access token gets created as expected and everything works locally using my “local” Auth0 tenant. In production, however, it is failing 100% of the time on the Express API “auth” middleware with the error:

Authentication error on bearer token InvalidTokenError: Invalid URL
at /my-project-path/node_modules/express-oauth2-jwt-bearer/dist/index.js:300:19

THE EXTERNAL API (that I have control over)
It’s an ExpressJs app.

My router file looks like this:

import express from 'express';
import { requiredScopes } from 'express-oauth2-jwt-bearer';

const router = express.Router();
const createScopes = requiredScopes('create:foo');
const checkJwt = auth({
  issuerBaseURL: process.env.AUTH0_DOMAIN,
  audience: process.env.AUTH0_AUDIENCE,
});

router.post(
  '/',
  checkJwt,
  createScopes,
  createSomething, // The function we want to execute after authentication passes.
);

export default router;

Any help at all you can provide on this matter would be amazing as I have been spinning my wheels for weeks (months?) on this very simply task.

Hey @Fatedx welcome to the community!

Thanks for the detailed description :slight_smile:

Does the access token you are passing look good if you decode it at jwt.io? If the token is as expected then you can at least narrow it down to the middleware.

I would also just double check all configuration (audience, issuerBaseURL, etc.).

Hi @tyf,

Thanks for getting back to me so fast. :slight_smile:

I have verified that the access token looks the way I expect using jwt.io:

{
  "iss": "https://mydomain.us.auth0.com/",
  "sub": "auth0|64c370870b2e256e91070c42",
  "aud": [
    "https://secure.mydomain.com",
    "https://mydomain.us.auth0.com/userinfo"
  ],
  "iat": 1697584195,
  "exp": 1697843395,
  "azp": "6TyddTqVujacQV3MM7MVmtbBzdlHTKop",
  "scope": "openid profile email create:foo offline_access",
  "permissions": [
    "create:foo"
  ]
}

My configuration should all be correct as well, but I am grasping at straws here, so I will share everything and maybe the data will point to the problem.

The NextJs app I sign in from using Auth0 Universal Login has these .env values:

MY_AUDIENCE=https://secure.mydomain.com
AUTH0_BASE_URL=https://commission.mydomain.com
AUTH0_ISSUER_BASE_URL=https://mydomain.us.auth0.com
AUTH0_CLIENT_ID=xxx
AUTH0_CLIENT_SECRET=xxx
AUTH0_SCOPE=openid profile email offline_access create:foo

That configuration above allows us to sign in with Auth0, hit the protected Nextjs endpoint, and create an accessToken. So it’s probably all good from the Nextjs side.

The ExpressJs app contains the following .env values:

AUTH0_DOMAIN=https://mydomain.us.auth0.com
AUTH0_AUDIENCE=https://secure.mydomain.com

These values all seem to line up nicely, but there must be something else I am missing.

Thanks for sharing!

Is the AUTH0_DOMAIN here set as issuerBaseURL when configuring the express SDK?

Indeed it is. In my original post, I provided the details of the SDK configuration, but here it is again for reference. :slight_smile:

Apologies if I misunderstood your question. I am so thankful for the help you are providing.

import express from 'express';
import { requiredScopes } from 'express-oauth2-jwt-bearer';

const router = express.Router();
const createScopes = requiredScopes('create:foo');
const checkJwt = auth({
  issuerBaseURL: process.env.AUTH0_DOMAIN,
  audience: process.env.AUTH0_AUDIENCE,
});

router.post(
  '/',
  checkJwt,
  createScopes,
  createSomething, // The function we want to execute after authentication passes.
);

export default router;

Good afternoon @tyf,

Any direction you can provide on this at all would be greatly appreciated. Your company doesn’t offer free support, and this is my last chance to get this working, so you’re my lifeline here.

Eagerly awaiting your reply. Thanks in advance!

Hey there @Fatedx

Unfortunately I am out of ideas here :confused: I am wondering if you are successfully able to authorize against your API if you grab an access token from the “Test” tab in your API settings:

It might also be helpful to install the real-time webtask logs extension in an effort to troubleshoot.