auth0 / nextjs-auth0

Next.js SDK for signing in with Auth0
MIT License
2.04k stars 384 forks source link

Unknown or invalid refresh token #498

Closed adikari closed 3 years ago

adikari commented 3 years ago

Description

I am using nextjs auth0 in api routes and also protecting some pages on the client side using the hoc. I also have a API proxy to my graphql server where I call getAccessToken to add Authorization header. The graphql proxy API is protected with withAPIAuthRequired.

Both my client side protected pages and the api routes are working as expected. I am running into a weird scenario where I seem to have an active session as the protected pages and api routes are still accessible. However, when the getAccessToken is invoked I am receiving Unknown or invalid refresh token error from Auth0.

I am currently configuring the sdk using environment variables. I also pass the offline_access scope along with other required ones using the environment variable. In auth0 configuration I have refresh token rotation enabled. I have verified the refresh tokens are working by setting the token expiry to about 1 minute then checking the logs in auth0. I can confirm that I can see the token getting refreshed via the log.

However, in some scenarios (I am not sure how this gets triggered), I am receiving the specified error when I call the getAccessToken which fails to renew the refresh token. If I redirect user to the login page then the token is refreshed. I am under impression that the getAccessToken would automatically refresh token and I do not need to perform this step.

I am not entirely sure if I am missing some configuration in the SDK or not. Upon browsing through similar issues from the past, I noticed there is an option to storeRefreshToken. I am suspecting it could be related to it but not entirely sure. I went ahead and checked the source code for configuration and I do not see any option for this.

I have also checked the example repository which is using initAuth0 which uses some extra session configuration that I am not currently using.

Also, even though I am getting the invalid refresh token error, my auth0 session seem to still be valid and the user is still logged in. What should happen in this scenario? Should the user be logged out automatically or is this something I will need to handle in the application?

I would appreciate any help in solving this issue.

Widcket commented 3 years ago

Hi @adikari, thanks for raising this.

I am not entirely sure if I am missing some configuration in the SDK or not. Upon browsing through similar issues from the past, I noticed there is an option to storeRefreshToken. I am suspecting it could be related to it but not entirely sure. I went ahead and checked the source code for configuration and I do not see any option for this.

The storeRefreshToken option was removed on v1.0.0:

Screen Shot 2021-09-17 at 00 16 15

Are you using initAuth0? If so, you need to make sure to always use that custom instance instead of the default instance created by the SDK.

adikari commented 3 years ago

@Widcket i am not using initAuth0. I am configuring the client via environment variables. I can confirm the refresh tokens works sometimes as I can see it in the auth0 logs. It's usually after about 12 hours that I get this error.

The other thing I can think of is, currently I am using this only on the api and don't plan to use this in the client. Hence I am not wrapping my app with the auth0 provider. I am not sure it that will make any difference. In surface it does not look like it will as everything but this is working as expected.

Widcket commented 3 years ago

@adikari see https://github.com/auth0/nextjs-auth0/issues/333#issuecomment-794066748. What is your Refresh Token inactivity lifetime? If it's too short you will run into issues.

adikari commented 3 years ago

@Widcket It is a fairly large value so I don't think it's the same issue. Attached is the screenshot with the configuration

image

Widcket commented 3 years ago

@adikari, are you revoking the refresh token?

One important detail is that when you revoke a token, for security reasons the grants associated with that token are deleted. This means that all other refresh tokens issued to the same combination of application, user and audience effectively become invalid. If you are revoking refresh tokens as part of your flow, this might explain it.

See https://community.auth0.com/t/invalid-refresh-token/10335

adikari commented 3 years ago

I am not really doing anything fancy here. Just using the library as it is with the recommended settings and configuration. Recently I have not seen the error on production and is only happening in local host.

karlisl commented 3 years ago

We noticed the same behavior today, some sessions with supposedly valid refresh token expiry dates started throwing this error. Last login performed 10 days ago, last successful token exchange 12 hours ago when it started failing. Absolute refresh token lifetime set to 30 days (2592000), inactivity to 15 days (1296000).

adamjmcgrath commented 3 years ago

Hi @adikari - have you set your Auth0 application up as a "Single Page App" or a "Regular Web Application"? It should be a "Regular Web Application"

I see you have Refresh Token Rotation (RTR) enabled, which is fine, but for Regular Web Applications we default this to off (we only recommend RTR for public clients like mobiles or SPAs)

Your intermittent failed RT exchanges could be caused by Rotated Tokens being reused and triggering breach detection (You can search for "ferrt" errors in your tenant logs to confirm)

If it's not that, we can investigate if you share your client id and tenant domain.

adikari commented 3 years ago

@adamjmcgrath interestingly I have not seen any errors for about a week now. The application is set to be SPA. I did update my nextjs app and wrapped the app component with the Provider eventhough I am not using the useUser hook. I am not sure if this is related. My use case is, all my pages require login so all pages are wrapped with withPageAuthRequired and I use the user loaded in the backend to pass down to components as a prop. I don't really need to use the useUser hook except in the profile page so thinking of just wrapping that component with the Provider. I will make a change tonight and remove the Provider tonight and observe for couple of days to see if the issue comes back. I will also go through the auth0 logs and search for the keyword you recommended to confirm if it was that issue.

I observed that if I wrap my app with the Provider, for every page changes it performs a call to /me endpoint even though the user would have already been loaded in the server by withPageAuthRequired. Is this the expected behaviour? If yes would be good to understand why we do the call to load the user eventhough the client is not using the useUser hook.

Thank you

adikari commented 3 years ago

Also a general question about the usage of the sdk. In case of the invalid token error, user session is still valid so they are logged in to the client. However, because of the invalid token, all my graphql queries would fail. Should the sdk invalidate the session and force the user to login again in such scenario? Is there a recommended way to invalidate the user session? This leaves the client in a weird spot where the user is logged in but none of the api endpoints would work.

adikari commented 3 years ago

@adamjmcgrath As you suggested, it seems like it's the token reuse detection invalidating the request token. Looking at the logs seems like there are multiple requests being made to get the refresh token. The first one failed then there was another one that was successfully made then another one failed which would have probably invalidated the successfully refreshed token?

image

adamjmcgrath commented 3 years ago

The application is set to be SPA.

The application should be set to "Regular Web Application"

@adamjmcgrath As you suggested, it seems like it's the token reuse detection invalidating the request token.

You should set your application to "Regular Web Application" and disable Refresh Token Rotation. Then you wont get issues with reuse detection.

If you really want to have Rotating Refresh Token's you may have to live with intermittent reuse errors (You can increase the "Reuse interval" to reduce the chance of these happening)

adikari commented 3 years ago

@adamjmcgrath i have made the necessary changes and will monitor for a bit. But that still does not explain what or how my refresh tokens are invalidated. Wouldn't it be more secure to use the token rotation?

adamjmcgrath commented 3 years ago

But that still does not explain what or how my refresh tokens are invalidated

Concurrent requests can trigger the reuse detection. Imagine a request for a new Access Token is issued and before it responds with a rotated Refresh Token another request is issued using the same Refresh Token, the first request will succeed and rotate the Refresh Token so the second request will fail because it will be using a Refresh Token that’s already been used.

Wouldn't it be more secure to use the token rotation?

Potentially yes - but there is a trade of with reliability and reuse detection, which is why we tend to default to non rotated for confidential clients

Closing this, feel free to ping me to reopen if you're still seeing issues

adikari commented 3 years ago

Thank you for helping out. I will monitor it for few days and ping you if I have further issues.

Alarid commented 1 year ago

@adikari Did you find the solution? I'm having the same issue, and I find it really hard to understand. NextJS app configured as a Regular Web App, token rotation disabled, offline_access specified in the scope, refresh token enabled, pretty much the same configuration as you. When I log in, I usually start seeing the issue after a few days: when I request the access token with getAccessToken(req, res), I get the error "Unknown or invalid refresh token.".

If I try to get another refresh token using either the node-auth sdk with auth0Client.refreshToken({refreshToken: session.refresh_token}), I get the same error. I've been reading the docs for days, and I still can't find a solution to this. Any help appreciated

adikari commented 1 year ago

@Alarid start from here. It might help https://github.com/auth0/nextjs-auth0/issues/498#issuecomment-926011951

Alarid commented 1 year ago

@Alarid start from here. It might help #498 (comment)

Thanks for your quick reply! I disabled token rotation, I will see if it solves the issue. So, just to be sure: there's nothing in particular I need to do to refresh this "refresh token" with this setting disabled?

adikari commented 1 year ago

I haven't used the library for a while now but from memory when you get the access token using the SDK it will automatically refresh the token if the token has expired.

Alarid commented 1 year ago

It seems to be the case yes, thanks again @adikari

javagovindsharma commented 1 year ago

@adikari I am getting an invalid access token, in my Auth0 application have applied RS256 signature format. for validation, I used the JWT.io port but I found a blank head and payload,

punksta commented 5 months ago

Hi Guys,

Update created an issue:

https://github.com/auth0/nextjs-auth0/issues/1739

We are experiencing a similar problem.

We use Next.js on Vercel. Refresh token rotation is disabled. The application type is set to a Regular Web App

Login/logout works, but we are getting many invalid refresh token errors.

Screenshot 2024-04-27 at 12 58 32

Auth0 error event:

{
  "date": "2024-04-27T10:56:28.619Z",
  "type": "fertft",
  "description": "Unknown or invalid refresh token.",
  "connection_id": "",
  "client_id": "XXX",
  "client_name": "XXX",
  "ip": "XXX",
  "user_agent": "Other 0.0.0 / Other 0.0.0",
  "hostname": "XXX.auth0.com",
  "user_id": "",
  "user_name": "",
  "auth0_client": {
    "name": "nextjs-auth0",
    "version": "3.5.0",
    "env": {
      "node": "v18.20.0"
    }
  },
  "$event_schema": {
    "version": "1.0.0"
  },
  "log_id": "90020240427105628747791000000000000001223372040511347164",
  "_id": "90020240427105628747791000000000000001223372040511347164",
  "isMobile": false,
  "id": "90020240427105628747791000000000000001223372040511347164"
}

Vercel error log:

i [AccessTokenError]: The request to refresh the access token failed. CAUSE: invalid_grant (Unknown or invalid refresh token.)
    at h.refresh (/var/task/.next/server/chunks/9355.js:1:4131)
    at process.processTicksAndRejections (node:internal/process/task_queues:95:5)
    ... 7 lines matching cause stack trace ...
    at async Y (/var/task/node_modules/next/dist/compiled/next-server/server.runtime.prod.js:16:24556) {
  code: 'ERR_FAILED_REFRESH_GRANT',
  cause: o [Error]: invalid_grant (Unknown or invalid refresh token.)
      at h.refresh (/var/task/.next/server/chunks/9355.js:1:4248)
      at process.processTicksAndRejections (node:internal/process/task_queues:95:5)
      at async Object.getAccessToken (/var/task/.next/server/chunks/9355.js:1:56971)
      at async m (/var/task/.next/server/chunks/2144.js:17:2133)
      at async n (/var/task/.next/server/app/api/billing/verify/route.js:17:2335)
      at async p (/var/task/.next/server/app/api/billing/verify/route.js:1:1455)
      at async /var/task/node_modules/next/dist/compiled/next-server/app-route.runtime.prod.js:6:42484
      at async eI.execute (/var/task/node_modules/next/dist/compiled/next-server/app-route.runtime.prod.js:6:32486)
      at async eI.handle (/var/task/node_modules/next/dist/compiled/next-server/app-route.runtime.prod.js:6:43737)
      at async Y (/var/task/node_modules/next/dist/compiled/next-server/server.runtime.prod.js:16:24556) {
    error: 'invalid_grant',
    errorDescription: 'Unknown or invalid refresh token.'
  },
  status: undefined
}

Any advice?