AccessTokenError: Could not retrieve an access token with scopes "openid profile email"

I’ve created a NextJS app and API and have used Auth0 to both manage users and secure the API endpoints. I am only using Email/Password authentication. I had everything working properly but now I am getting the error “AccessTokenError: Could not retrieve an access token with scopes “openid profile email”. The user will need to sign in again.” Signing in again doesn’t work of course. It seems I am only getting a token with the scope “openid” but “profile” and “email” suddenly aren’t available.

The documentation has been a bit confusing but from what I can tell, it seems my Auth0 Application has the necessary scopes available but my Auth0 API does not.

Has anyone run into this error before? I can provide code examples but I am unsure if this is an Auth0 configuration issue or a code issue.

Hi @brandon.bachrach,

Welcome to the Community!

In order to help troubleshoot this could you please answer the following?:

  • Which Auth0 SDK and version are you using in your project?
  • When the error is thrown, do you see an error message in your tenant logs in your Auth0 dashboard?
  • Are you integrating the SDK into an existing app or are you using a Quickstart or article?

Thank you!

Im using nextjs-auth0 and I followed similar steps to Auth0 Next.js SDK Quickstarts: Login . I followed this article too How to add Auth0 to Nextjs- the Ultimate Guide | Code Mochi.

I am not seeing the error in the Auth0 logs. I did have everything working properly and then the access token stopped providing me with the profile and email scopes. Im unsure what changed in that time.

Thanks for this info! Do you have a call to the initAuth0 method in your code? If so, could you please send a code snippet of the configs?

@stephanie.chamblee
In /utils/auth0.js I have:

import { initAuth0 } from "@auth0/nextjs-auth0";
export default initAuth0();

I noticed most documentation included a config object in the initAuth0 call, but I was getting an error for every value saying that they were not allowed for some reason.
example: [1] TypeError: "session.cookieSecret" is not allowed

This was my old initAuth0 call

export default initAuth0(
  {
  domain: process.env.AUTH0_BASE_URL,
  clientId: process.env.AUTH0_CLIENT_ID,
  clientSecret: process.env.AUTH0_CLIENT_SECRET,
  scope: "openid profile",
  redirectUri: process.env.AUTH0_BASE_URL,
  postLogoutRedirectUri: process.env.AUTH0_BASE_URL,
  session: {
    cookieSecret: process.env.AUTH0_SECRET,
    cookieLifetime: 60 * 60 * 8,
    storeIdToken: false,
    storeAccessToken: false,
    storeRefreshToken: false,
  },
  oidcClient: {
    httpTimeout: 2500,
    clockTolerance: 10000,
  },
}
);

After looking through the source code for this function, I found it is just pulling process.env values, so I just made sure to name my env variables accordingly. Here are the variables I have defined.

AUTH0_AUDIENCE = [my api indentifier]
AUTH0_BASE_URL = http://localhost:3000
AUTH0_CALLBACK_URL = http://localhost:3000/api/auth/callback
AUTH0_CLIENT_ID =
AUTH0_CLIENT_SECRET =
AUTH0_ISSUER_BASE_URL = 
AUTH0_SCOPE = "openid profile email"
AUTH0_SECRET = [random string]

Have you tried using this to initialize auth instead as shown in the quickstart?:

import { handleAuth } from '@auth0/nextjs-auth0';

export default handleAuth();

Also, do you have values set for AUTH0_CLIENT_ID, AUTH0_CLIENT_SECRET and AUTH0_ISSUER_BASE_UR?

I did find a Github issue that could be related: AccessTokenError When Re-fetching User Profile · Issue #145 · auth0/nextjs-auth0 · GitHub

I do have those values set for AUTH0_CLIENT_ID , AUTH0_CLIENT_SECRET and AUTH0_ISSUER_BASE_URL but wasn’t sure if I should share them here. And I do have handleAuth() in /pages/api/auth/[...auth0].js. If I’m not mistaken that just exposes the /login /callback and /logout endpoints right?

1 Like

That’s correct, those values shouldn’t be posted here. Thank you for clarifying!

Hm, yes, you are correct. I am having trouble recreating the issue. When you inspect the JWT at jwt.io are you seeing the scopes claim?

The JWT being returned doesn’t include the proper scopes. But interestingly enough, I created a stripped-down duplicate of my app, with none of my components, just the login button and user page. And I am seeing the proper scopes represented there. So it must be something that I’m doing in the app that’s causing the issue. I will be adding and removing features to see what causes it.

I am suspecting it’s something to do with my getInitailProps function in certain components that I’m using to pass the AccessToken to my API.

Example:

Index.getInitialProps = async ({ req, res }) => {
  try {
    const { accessToken } = await auth0.getAccessToken(req, res, {
      scopes: [process.env.AUTH0_SCOPE],
    });

    const headers = {
      headers: {
        Authorization: `Bearer ${accessToken}`,
      },
    };

    const customerRes = await axios.get(
      "http://localhost:3001/[API ENDPOINT HERE]",
      headers
    );
    const activeSubscribers = customerRes.data.activeSubscribers;
    return { activeSubscribers };
  } catch (err) {
    console.error(err);
    return {};
  }
};

Could you let me know if you see any apparent issues with my API endpoint protection.

Thanks!

That is interesting!

I was finally able to recreate the Could not retrieve an Access Token with scopes... error. It seems to occur when I request a scope that has not been set up for my external API.

The getAccessToken method seems to take an array of strings: https://auth0.github.io/nextjs-auth0/interfaces/session_get_access_token.accesstokenrequest.html

However, your env variable is one string with spaces separating each scope. In your code, instead of requesting the “openid”, “profile”, and “email” scopes, the getAccessToken method is looking for one scope called “openid profile email”, which it cannot find.

Your env variable is correct, though. When you decode the JWT, you should see two audiences, for example:

  "aud": [
    "https://example.com/health-api",
    "https://my-domain.auth0.com/userinfo"
  ],

The email, profile, and openid scopes are used for the /userinfo endpoint, not your external API. Since the scopes listed in the getAccessToken method seem to be intended for an external API, I would try removing the scopes optional param:

const { accessToken } = await auth0.getAccessToken(req, res)

That seems to have solved it! I also tested this and it worked too: scopes:["openid","profile","email"]. Thank you for clearing that up, I didn’t realize it was looking for one scope when I used that env variable. But it makes sense looking at it now!

I’m still having an issue where routing to a page is resulting in an empty getInitialProps (ie. the catch (err) { block running. Once I refresh everything loads properly though. I’m unsure if that’s Auth0 related or not. I’d assume it means the client side routing is causing an error with auth0.getAccessToken but Im unsure why.

1 Like

Ah I see the issue.

I removed the try catch block and saw this error.
Error: The getAccessToken method can only be used from the server side

Is there another way to retrieve the access token so I can use it in my API calls?

1 Like

I’m going to try following these steps to proxy the API request so the getAccessToken function only runs on the backend.

1 Like

That looks like a good plan. Glad you were able to get past the initial error message! You may have read this article, but just in case, I’d recommend this one as well: How to Authenticate with Next.js and Auth0: A Guide for Every Deployment Model

I tried this method, and I am running into one more issue. I am getting a 401 error when I try to access the page that is calling my API. But I can actually access the API endpoint directly in browser and see the data show properly, so I believe I am authenticated properly. When I remove withApiAuthRequired I get a 500 error.

Here’s the getInitialProps of the page that is calling my API

Index.getInitialProps = async ({ req, res }) => {
  console.log("test");
  const activeSubscribers = await axios.get(
    "http://localhost:3000/api/get-active-subscribers"
  );
  return { activeSubscribers };
};

And here’s the API endpoint that is being used as a proxy to call my external API with an access token.

import { getAccessToken, withApiAuthRequired } from "@auth0/nextjs-auth0";
import axios from "axios";
export default withApiAuthRequired(async function getActiveSubscribers(
  req,
  res
) {
  // If your Access Token is expired and you have a Refresh Token
  // `getAccessToken` will fetch you a new one using the `refresh_token` grant
  const { accessToken } = await getAccessToken(req, res);
  const headers = {
    headers: {
      Authorization: `Bearer ${accessToken}`,
    },
  };
  const customerRes = await axios.get(
    "http://localhost:3001/get-active-subscribers",
    headers
  );
  const activeSubscribers = customerRes.data.activeSubscribers;
  res.status(200).json(activeSubscribers);
});

Hi @brandon.bachrach,

Is the request to your API made client-side? Since it is working directly, but not on the webpage, I’m wondering if it might be a CORS issue. If you look at the network requests in dev tools, does it look like an OPTIONS request is failing?

I solved this issue using the SWR library that is mentioned in the nextjs-auth0 documentation. So it ends up I didn’t need getInitialProps at all.

I created a custom hook to handle my get requests to the internal NextJS API, which is then calling my external API.

/hooks/useFetchAPI.js

import { useState, useEffect } from "react";
import fetch from "unfetch";
import useSWR from "swr";

const useFetchAPI = (endpoint) => {
  const [loading, setLoading] = useState(true);
  const [response, setResponse] = useState([]);

  const { data, error } = useSWR(
    `${process.env.AUTH0_BASE_URL}${endpoint}`,
    fetcher
  );

  if (error) {
    return error;
  }

  useEffect(() => {
    if (!loading && data) {
      return;
    }
    if (data) {
      setResponse(data);
      setLoading(false);
    }
  }, [data]);

  return [response, loading];
};

const fetcher = (url) => fetch(url).then((r) => r.json());

export default useFetchAPI;

Then I call it from one of my components:

const Customer = () => {
  const router = useRouter();
  const custID = router.query.custid;
  const [customerDetails, customerLoading] = useFetchAPI(
    `/api/getCustomerDetails?custID=${custID}`
...........
  );

And here’s what one of my NextJS API endpoints looks like:
/pages/api/getCustomerDetails.js

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

export default withApiAuthRequired(async function getCustomerDetails(req, res) {
  try {
    const { accessToken } = await getAccessToken(req, res);
    const headers = {
      headers: {
        Authorization: `Bearer ${accessToken}`,
      },
    };

    const custID = req.query.custID;
    const response = await axios.get(
      `http://localhost:3001/get-customer-details?custID=${custID}`,
      headers
    );
    const customerDetails = response.data.customerDetails;

    res.status(200).json(customerDetails);
  } catch (error) {
    console.error("error", error);
    res.status(error.status || 500).json({
      code: error.code,
      error: error.message,
    });
  }
});

And this is now working perfectly! Thanks for all the help! Hopefully this helps someone else too.

1 Like

That’s great! Thanks for sharing your solution!

A post was split to a new topic: AccessTokenError when requesting offline_access scope

This topic was automatically closed 15 days after the last reply. New replies are no longer allowed.