Nextjs-auth0: The user does not have a valid access token via getAccessToken

Created on 21 Mar 2020  路  16Comments  路  Source: auth0/nextjs-auth0

Description

What does it mean when a user does not have a valid access token? I looked outside of this library package and was able to find the access token via /oauth/token endpoint via POST but not sure why this function does not work out of the box?

Reproduction

In my pages/account page I do the search for access token

export async function getServerSideProps({ req, res }) {
  const tokenCache = await auth0.tokenCache(req, res);
  try {
    const { accessToken } = await tokenCache.getAccessToken();
    console.log(tokenCache);
  } catch (e) {
    console.log(e.message); // error message gets generated here.
  }
}

Environment

// auth0 configs
export default initAuth0({
  clientId: config.AUTH0_CLIENT_ID,
  clientSecret: config.AUTH0_CLIENT_SECRET,
  scope: config.AUTH0_SCOPE,
  domain: config.AUTH0_DOMAIN,
  redirectUri: config.REDIRECT_URI,
  postLogoutRedirectUri: config.POST_LOGOUT_REDIRECT_URI,
  session: {
    cookieSecret: config.SESSION_COOKIE_SECRET,
    cookieLifetime: config.SESSION_COOKIE_LIFETIME,
    storeIdToken: true,
    storeAccessToken: true,
    storeRefreshToken: true
  },
})
// package.json
{
  "dependencies": {
    "@auth0/nextjs-auth0": "^0.10.0",
    "@zeit/next-css": "^1.0.1",
    "@zeit/next-less": "^1.0.1",
    "@zeit/next-sass": "^1.0.1",
    "antd": "^4.0.3",
    "axios": "^0.19.2",
    "babel-plugin-import": "^1.13.0",
    "body-parser": "^1.19.0",
    "dotenv": "^8.2.0",
    "dotenv-webpack": "^1.7.0",
    "express": "^4.17.1",
    "isomorphic-unfetch": "^3.0.0",
    "less": "3.11.1",
    "less-vars-to-js": "1.3.0",
    "next": "latest",
    "next-compose-plugins": "^2.2.0",
    "node-sass": "^4.13.1",
    "nodemon": "^2.0.2",
    "null-loader": "3.0.0",
    "query-string": "^6.11.1",
    "react": "^16.13.0",
    "react-dom": "^16.13.0",
    "react-responsive": "^8.0.3",
    "uid-safe": "^2.1.5"
  },
  "license": "ISC"
}

Most helpful comment

This is happening on every request. Even though the proxy is working.

try {
    const tokenCache = await auth0.tokenCache(req, res);
    const { accessToken } = await tokenCache.getAccessToken();
    console.log(accessToken);
    const apiResponse = await callAPI(req.body, {
      authorization: accessToken ? `Bearer ${accessToken}` : '',
      'content-type': req.headers['content-type'],
    });
    forwardResponse(res, apiResponse);
  } catch (error) {
    console.error(error);
    res.status(error.status || 400).send({ message: error.message });
  }

The error is,

AccessTokenError: The user does not have a valid session.
    at SessionTokenCache.<anonymous> (
  name: 'AccessTokenError',
  code: 'invalid_session'
}

Our config does include the audience and refresh token.

export default initAuth0({
  audience: config.AUTH0_AUDIENCE,
  clientId: config.AUTH0_CLIENT_ID,
  clientSecret: config.AUTH0_CLIENT_SECRET,
  scope: config.AUTH0_SCOPE,
  domain: config.AUTH0_DOMAIN,
  redirectUri: config.REDIRECT_URI,
  postLogoutRedirectUri: config.POST_LOGOUT_REDIRECT_URI,
  session: {
    cookieSecret: config.SESSION_COOKIE_SECRET,
    cookieLifetime: config.SESSION_COOKIE_LIFETIME,
    storeIdToken: false,
    storeRefreshToken: true,
    storeAccessToken: true,
  },
});

All 16 comments

It looks like you have not set an audience in you config.

See calling an API section.

Correct. @kkomaz you'll first need to create an API in the Auth0 dashboard and then use the identifier of the API as the audience when configuring the SDK: https://github.com/auth0/nextjs-auth0/blob/master/examples/api-call-example/lib/auth0.js#L5

@sandrinodimattia Can you help me understand how scopes work?

Doing the following below returns me a access token but I get a UnauthorizedError: jwt audience invalid.

    const { accessToken } = await tokenCache.getAccessToken({
      scopes: ['update:blogs']
    });

I believe I added the appropriate privileges in the dashboard via below and authorized machine to machine specific to my app. (High level I am trying to update a blog)

My configs

AUTH0_SCOPE=openid profile read:blogs create:blogs delete:blogs update:blogs offline_access
API_AUDIENCE=https://api/blogs

Screenshot of Brave Browser (4-18-20, 7-00-28 PM)

Screenshot of Brave Browser (4-18-20, 7-00-34 PM)

Custom Server Side code

router.put('/:id/update', checkJwt, blogController.blogUpdate);

I resolved my issue. It looks like my audience value was a mismatch between my front-end and server api.

If i may ask, how does the scope associate to my custom endpoints? It doesnt look like restricting the getAccessToken by scopes is doing anything

for example if I do
app.put('/api/tools/:id/update') do I need a scope update:tools? I can't seem to wrap my head around the magic of associating the two together

@kkomaz could you elaborate on the mismatch? I'm having the same issue where I'm getting back an access token that is not a valid jwt (much shorter) and I've set my scopes and api_audience.

Actually I take that comment back. Don't forget that after you add an audience, the previously created users will have the wrong audience and retrieving their JWT will not work. Go under the "authorized applications" and you'll see the wrong audience listed with old users. Revoke that and reauthorize and you should be good to go

I had the same issue and appeared to be because of misreading/misunderstanding the documentation.

For this functionality to work correctly you'll need to persist the access token and refresh token in the session:

I thought that the above was referring to:

You can also require a scope to be present in the requested access token....

but apparently it is about the Getting an Access Token process in general. So until you set the option to persist the access token, you'll be getting the error.

I am not sure about the storeRefreshToken option. I got it working without it (for now).

This is happening on every request. Even though the proxy is working.

try {
    const tokenCache = await auth0.tokenCache(req, res);
    const { accessToken } = await tokenCache.getAccessToken();
    console.log(accessToken);
    const apiResponse = await callAPI(req.body, {
      authorization: accessToken ? `Bearer ${accessToken}` : '',
      'content-type': req.headers['content-type'],
    });
    forwardResponse(res, apiResponse);
  } catch (error) {
    console.error(error);
    res.status(error.status || 400).send({ message: error.message });
  }

The error is,

AccessTokenError: The user does not have a valid session.
    at SessionTokenCache.<anonymous> (
  name: 'AccessTokenError',
  code: 'invalid_session'
}

Our config does include the audience and refresh token.

export default initAuth0({
  audience: config.AUTH0_AUDIENCE,
  clientId: config.AUTH0_CLIENT_ID,
  clientSecret: config.AUTH0_CLIENT_SECRET,
  scope: config.AUTH0_SCOPE,
  domain: config.AUTH0_DOMAIN,
  redirectUri: config.REDIRECT_URI,
  postLogoutRedirectUri: config.POST_LOGOUT_REDIRECT_URI,
  session: {
    cookieSecret: config.SESSION_COOKIE_SECRET,
    cookieLifetime: config.SESSION_COOKIE_LIFETIME,
    storeIdToken: false,
    storeRefreshToken: true,
    storeAccessToken: true,
  },
});

I don't know if it's related: my login flow is working, but after some hours (probably after cookieLifetimes) I get the same The user does not have a valid session when trying to getAccessToken from tokenCache, but there is a valid session on Auth0, which is confirmed by trying to login with handleLogin, it just redirects to handleCallback and the session is renewed without going through the full Auth0 login flow again.

I don't know if it's related: my login flow is working, but after some hours (probably after cookieLifetimes) I get the same The user does not have a valid session when trying to getAccessToken from tokenCache, but there is a valid session on Auth0, which is confirmed by trying to login with handleLogin, it just redirects to handleCallback and the session is renewed without going through the full Auth0 login flow again.

same! So now i am commenting out cookieLifetime to see if the cookie expires. .... now to set a timer for 2 hours ;)

follow up, commenting out didnt work, default must be 2 hours. So far having success setting to 86400 * 30 (1 month) in that its been 12 hours and i'm still logged in / session is active.

A few days later, so far so good.

Hello,

I get the same problem, but I only have this problem ttryint to get the token from a api route (via a POST request). which from reading the docs looks like its how its intended to work? If I use for example apollo which needs the jwt client side it would have to do a post request to an Api route to get it?

any ideas how to advance this problem? if I do the request directly in getInitialProps it works server side but not client side (since req and resp dont exits)

After setting audience, storeRefreshToken and storeAccessToken access tokens are retrieved.
If storeRefreshToken and storeAccessToken are set to false I always get the: The user does not have a valid access token.

Hi everyone, can you please check if you still have issues with the new v1.0.0-beta.0 release? We've heavily reworked the session logic.

If so, please open a new issue.

@Widcket

We also provide a SPA React library auth0-react, which may also be suitable for your Next.js application.

I don't think auth0-react will work in an next.js app because it's rendered server-side and that uses local storage. Or is there a strategy that I'm missing?

Ahh, seems they have an SSR mode

https://github.com/auth0/auth0-react/pull/17

Was this page helpful?
0 / 5 - 0 ratings