6

I am learning how to use Auth0 with our Next.js application. Using the nexjs-auth0 SDK. I have successfully authenticated with Auth0 and receive an ID Token and access token (for use against the Auth0 management api). This seems to be the default configuration of things. We have an API we have setup which will use Auth0 authentication as well. What I can't figure out after hours of reading through documentation is how to get an access_token to use against the API.

I keep reading that you don't use an ID Token (profile info) for an API, use an Access Token. In addition it reads to get an Access Token when you authenticate the user(login). I have not been able to figure this out.

I have defined the application and API end point in Auth0. There is a user setup and assigned a role. This role has a few scopes I through on it.

Am I able to get both the ID Token and the access token for our api in the same login call or do I request the API access token after the user is logged in? I am assuming the API access token will be a bearer jwt type-token.

3 Answers 3

10

It's now 2021, and Auth0 has taken the nextjs-auth0 library out of beta. The example setup doesn't mention initAuth0() at this point, but if you call getAccessToken() in a Next API route, you still get a short token that looks like this: vVuzVl6e0O-ydWeJXtvweASvhnWoav7w. This is because Auth0 uses the audience parameter to determine whether to provide an opaque token or a JWT.

To get a JWT bearer token to put in the Authorization header, you can just add the audience and scope as parameters to the handleAuth() call inside pages/api/auth/[...auth0].js:

// pages/api/auth/[...auth0].js
import { handleAuth, handleLogin } from '@auth0/nextjs-auth0';

export default handleAuth({
  async login(req, res) {
    try {
      await handleLogin(req, res, {
        authorizationParams: {
          audience: 'https://api.example.com/products', // or AUTH0_AUDIENCE
          // Add the `offline_access` scope to also get a Refresh Token
          scope: 'openid profile email read:products' // or AUTH0_SCOPE
        }
      });
    } catch (error) {
      res.status(error.status || 400).end(error.message);
    }
  }
});

Then the call to getAccessToken() from within your API route will return a JWT, which you can use to call your API:

// pages/api/widget/[id].js
import { getAccessToken, withApiAuthRequired } from '@auth0/nextjs-auth0';

export default withApiAuthRequired(async function widget(req, res) {
  try {
    const { accessToken } = await getAccessToken(req, res);
    const { id } = req.query

    const response = await fetch(`https://MY_API_DOMAIN/widget/${id}`, {
        headers: {
            Authorization: `Bearer ${accessToken}`
        }
    })
    const widget = await response.json()

    res.status(200).json(widget)
  } catch(error) {
    console.error(error)
    res.status(error.status || 500).end(error.message)
  }
});
3
  • I am literally trying to solve this issue for a couple of hours now. No matter what I do, I am not getting JWTs. I tried your answer and it got me the furthest, because now rather than bad JWT, the error is "The user does not have an active session or is not authenticated". It's still not clear to me what I need on the Auth0 side, I have an application, and an API...and I have no idea what connects what. They really couldn't include a 2 minute example with instructions, huh? And in the end all I want is to be able to get the user's sub so I could use it as an ID in my DB Commented May 30, 2021 at 22:19
  • Now I am half facepalming and half hating on there not being an example. I guess I fixed the issue at some point (some permission or other setting? no clue). The next problem was that I was testing stuff from getServerSideProps and calling a Vercel API route from it. This is supposedly supposed to work...but probably only if you use it from the client side, duh! I'll see later if your code works on the client side correctly, hopefully it will Commented May 30, 2021 at 23:04
  • 1
    Just wanted to drop off and mention that this answer indeed works for the client side calls. The server-side functions need to use getAccessToken/getSession directly rather than calling the API routes. Thanks :) Commented May 31, 2021 at 1:00
5

After much pain and question-asking it turns out that you need to include an audience to the API in your request. For the Next.js-Auth0 SDK, our scope for this question, this can be done when the Auth0 is initialized.

In the file lib/auth0.js:

import { initAuth0 } from '@auth0/nextjs-auth0'

export default initAuth0({
  clientId: process.env.AUTH0_CLIENT_ID,
  clientSecret: process.env.AUTH0_CLIENT_SECRET,
  scope: process.env.AUTH0_SCOPE,
  audience: 'http://api.example.com/v1',
  domain: process.env.AUTH0_DOMAIN,
  redirectUri: process.env.REDIRECT_URI,
  postLogoutRedirectUri: process.env.POST_LOGOUT_REDIRECT_URI,
  session: {
    cookieSecret: process.env.SESSION_COOKIE_SECRET,
    cookieLifetime: process.env.SESSION_COOKIE_LIFETIME,
  },
})

I added the audience parameter to that file (audience: 'http://api.example.com/v1',), setting it to the API Identifier from the Auth0 dashboard.

This was another point of confusion as when you setup a custom API in Auth0 it has both and Id and Identifier. You can find the Identifier toward the top, by the title. From what I have seen, the Identifier has been the uri of the endpoint.

After adding the audience parameter, when a user logs in, the accessToken received is now a JWT bearer token which can be used with my API. It only contains the security bits, needed, not profile bits.

To add permissions to the accessToken, make the following changes in the Auth0 dashboard for that API:

  • Enable RBAC - true/on
  • Add Permissions in the Access Token - true/on
4

As the chosen reply already answered the reason, I will also add another solution for those who are on a more default nextjs setup, where you just followed the tutorial on the management page and just using handleAuth() in your api/auth/[...auth0].js.

Add AUTH0_AUDIENCE=<YOUR AUDIENCE> to your .env.local file and don't forget to add it to your vercel dashboard in Environment variables.

You can look up the audience for your Auth0 API in the management dashboard -> Applications -> APIs

Alternatively, you can append default audience to every request in that tenant. Go to Settings -> API Authorization Settings section -> Default Audience. The drawback is it's not as flexible. The benefit, less env variables floating about.

Not the answer you're looking for? Browse other questions tagged or ask your own question.