supabase / auth-helpers

A collection of framework specific Auth utilities for working with Supabase.
https://supabase.github.io/auth-helpers/
MIT License
908 stars 232 forks source link

Invalid Refresh Token: Refresh Token Not Found #436

Open bukinoshita opened 1 year ago

bukinoshita commented 1 year ago

Bug report

Describe the bug

I'm currently getting Invalid Refresh Token: Refresh Token Not Found error in my Next.js middleware

const res = NextResponse.next();
  const supabase = createMiddlewareSupabaseClient({ req, res });

  const {
    data: { session },
    error,
  } = await supabase.auth.getSession();

  if (error) {
    throw new Error(error.message);
  }

A clear and concise description of what the bug is.

To Reproduce

I think the issue is pretty similar to this one:

https://github.com/supabase/gotrue-js/issues/323 Steps to reproduce the behavior, please provide code snippets or a repository:

  1. Go to '…'
  2. Click on '…'
  3. Scroll down to '…'
  4. See error

Expected behavior

To logout the user, or keep them signed in

A clear and concise description of what you expected to happen.

Screenshots

If applicable, add screenshots to help explain your problem.

System information

Additional context

Add any other context about the problem here.

j4w8n commented 1 year ago

One of the places I found this error is when calling getSession, it sees the session as expired, then calls the gotrue api to try and refresh the session. Part of that process is trying to find a user that has the refresh token. If something goes wrong when searching for a user, it'll return this error.

Can you look in your auth.refresh_tokens table and find an entry that matches the token?

bukinoshita commented 1 year ago

One of the places I found this error is when calling getSession, it sees the session as expired, then calls the gotrue api to try and refresh the session. Part of that process is trying to find a user that has the refresh token. If something goes wrong when searching for a user, it'll return this error.

Yeah, that exactly the call that it's giving me that error.

Can you look in your auth.refresh_tokens table and find an entry that matches the token?

What should I look here? Whenever I get the error, I try to find a refresh token that matches the current token?

Btw, a temporary "solution" was to increase the user session from 1 hour to 1 week, so it minimize the error occurrence.

j4w8n commented 1 year ago

What should I look here? Whenever I get the error, I try to find a refresh token that matches the current token?

Yes

What's the timeframe between when a user signs in and when this error occurs?

bukinoshita commented 1 year ago

Need to investigate, the application is production — what would be the best way to track this? Should I change the session time to 1 hour again and see if the error happens with more frequency?

thorwebdev commented 1 year ago

You could try increasing the reuse interval. The client and server might be refreshing fairly close to each other in with that invalidate the token. But yah, if you have a log drain would also be good to see what's happening within the middleware function.

image
yairhaimo commented 1 year ago

Did you ever solve this issue? Im running into the same problem

jdgamble555 commented 1 year ago

Not sure why, but this happened on my local dev server, and when I restarted, the problem was gone.

J

dsebastien commented 1 year ago

I am trying to refresh user data by calling auth.getUser() and am facing the same error.

nathantaal commented 1 year ago

+1 for this issue! I'm having it in the dart client ever since I cloned the user_management_example project and have to sign in using the magic link every time.

bukinoshita commented 1 year ago

Did you ever solve this issue? Im running into the same problem

Increasing the interval "solved" the issue. I don't think this is the ideal solution, but for me it stopped breaking the application at least.

RyanCarterTheDev commented 1 year ago

Did you ever solve this issue? Im running into the same problem

Increasing the interval "solved" the issue. I don't think this is the ideal solution, but for me it stopped breaking the application at least.

What did you increase this to?

I'm also running into this issue with auth.getSession() when using createRouteHandlerClient from @supabase/auth-helpers-nextjs

bukinoshita commented 1 year ago

If I recall correctly the workaround was to increase this to the max

CleanShot 2023-07-18 at 17 22 23@2x
nathantaal commented 1 year ago

But is this also a solution, or will it only make the issue very less like to happen? So in the example of this max seconds being a week, will will get the error again if we logon to the app only to open it 8 days later..?

MilesV64 commented 1 year ago

Also experiencing this

seho0808 commented 1 year ago

Ok, I have a reproducible Scenario where this happens on my server. If I load balance to multiple instance and when user logs in on browser to use my service, the load balanced instance can get changed and user's frontend server can be redirected to a different server than the one he or she logged in. Since the Next js app in a new instance does not have a refresh token for the cookie that it is getting, it returns "Refresh Token Not Found". This is fine if I can log the user out.

I can't seem to log the user out in a proper way, maybe like the signOut api await supabase.auth.signOut, since this also throws the token not found error...

I guess I have to automatically clear cookies of users every time I encounter this?

Please let me know if I'm wrong here. I need help.

EDIT: not really sure it is because of multiple instances at this moment. might just be other bug.

swyxio commented 1 year ago

@david-plugge is this similar to https://github.com/supabase/auth-helpers/issues/343 ?

titocosta commented 1 year ago

Now getting same problem. Not quite sure what triggered it in the first place as it was working ok until now. It might be that I logged out the user and since I logged back in, it started giving this error.

Increasing JWT expiry (to several values and up to maximum) and increasing reuse interval in settings/auth did not solve it for me.

This is all with local development in NextJS 13, logging in with Google OneTap.

silentworks commented 1 year ago

We haven't been able to reproduce this issue and will find it hard to fix since we have no way of seeing or testing the issue. Can one of you on the thread provide a reproducible repository with the issue so we can take a look at it.

jdgamble555 commented 1 year ago

@silentworks - One of the ways this has been triggered is by logging in to my website, then running supabase db reset (or stop and start). Since I'm still logged in, and the database is empty, it will result in this error. I believe logging out first before resetting the database fixes this problem, and probably what is expected.

There are other cases, and as I find them (may be randomly in the future), I will post a repo and try and replicate it.

I can't speak for anyone else.

J

nathantaal commented 1 year ago

@silentworks for me is at easy as checking out the Flutter user management example, logon once, waiting 24 hours and opening the app once again. The wait time is important here.

seho0808 commented 1 year ago

We haven't been able to reproduce this issue and will find it hard to fix since we have no way of seeing or testing the issue. Can one of you on the thread provide a reproducible repository with the issue so we can take a look at it.

I think I found a reproducible one which produces a similar result. Could you please try this scenario?

  1. set JWT expiry limit to lowest as possible (which is 1200 currently)
  2. login to supabase auth based app
  3. in developer console, get the document.cookie for our JWT token. keep this somewhere else.
  4. logout of supabase auth based app we logged in from step2.
  5. wait for expiry time. (1200 seconds in this case)
  6. on a fresh app website page that isn't logged in, inject the document.cookie from step3.
  7. refresh so that document.cookie takes effect.
  8. You get bunch of errors on server and client. Below statement prints multiple times in my server for next.js app.
    [AuthApiError: Invalid Refresh Token: Refresh Token Not Found] {
    1|vhub_fe  |   __isAuthError: true,
    1|vhub_fe  |   name: 'AuthApiError',
    1|vhub_fe  |   status: 400
    1|vhub_fe  | }
  9. The problem here is that none of supabase-js api works so I can't even await supabase.signOut().

Disclaimer here is that my bug happens to have same result, but I never log out of the app nor do I turn off my server. My server is always online, and "Refresh token not found" happens after some time has passed. Also not sure what the expiry was set in cookie for the actual error scenario, but in actual scenario, the error persisted even if I closed the browser and reentered the website.

My current solution to bypass Refresh Token Not Found in production - this simply logs out the user: (part of middleware.ts)

const res = NextResponse.next();
const supabase = createMiddlewareClient<Database>({ req, res });
const {
  data: { session },
  error,
} = await supabase.auth.getSession();

if (error) {
  res.cookies.delete("my-auth-token-name");
  return ["error", res];
}
acomanescu commented 1 year ago

@seho0808 Thank you for providing the solution. I'm facing the same issue.

silentworks commented 1 year ago

@seho0808 those steps don't make sense as that is intentionally creating an issue. The auth-helpers does auto-refreshing of the token so you logging out would remove the whole effect of the auto-refreshing happening. Please provide an example app repository simulating this issue whilst logged in. Intentionally breaking the flow to create an issue isn't a good way to fix an issue.

seho0808 commented 1 year ago

@seho0808 those steps don't make sense as that is intentionally creating an issue. The auth-helpers does auto-refreshing of the token so you logging out would remove the whole effect of the auto-refreshing happening. Please provide an example app repository simulating this issue whilst logged in. Intentionally breaking the flow to create an issue isn't a good way to fix an issue.

I apologize for a bad example. I couldn't find a way to reproduce it so I shared a closest example to what I can simulate. I will try not to provide such examples from now on. Thank you for the reply!

wdavidturner commented 1 year ago

I believe the issue is that you have info in your cookies that references a deleted user.

louishugens commented 1 year ago

the issue is still there. Any solution or workaround?

@wdavidturner I have the same issue, it happens when the auth token expires but the user is still logged in.

wojtekKrol commented 1 year ago

I have the same error, I deleted user in auth tab while beeing login on my localhost, clearing cookies do nothing

silentworks commented 1 year ago

@wojtekKrol I just tested this and my user gets logged out (which is what should happen) when the token tried refreshing along with the error from the API (which should be handled by yourself in your code). My test project can be found here if you'd like to inspect the code https://github.com/supabase-community/supabase-by-example/tree/main/reset-flow/nextjs.

silentworks commented 1 year ago

I've just re-read all the messages here and now I'm wondering what the actual issue is? All the scenarios provided seem to lead to the same error but the developer should be handling the error in their app. This isn't something that supabase-js or auth-helpers should manage as this is down to error management in your app itself. Please reply to this message with what you expect to happen when your token has expired and not able to refresh due to whatever reason.

jareddr commented 1 year ago

I've just re-read all the messages here and now I'm wondering what the actual issue is? All the scenarios provided seem to lead to the same error but the developer should be handling the error in their app. This isn't something that supabase-js or auth-helpers should manage as this is down to error management in your app itself. Please reply to this message with what you expect to happen when your token has expired and not able to refresh due to whatever reason.

I started my project based upon the supabase sveletekit auth example and I'm running into this same thing. Are you thinking that the example code needs to be updated to solve this issue?

silentworks commented 1 year ago

@jareddr I'm not sure how you read my comment and came to this conclusion. Please read it again as this isn't a bug, its how an application behaves when your token has expired. As I've asked in my last sentence Please reply to this message with what you expect to happen when your token has expired and not able to refresh due to whatever reason.

tomekit commented 1 year ago

its how an application behaves when your token has expired.

Fair point, but what's the case where token has legitimately expired? Isn't it supposed to be permanent until logout?

jareddr commented 1 year ago

@jareddr I'm not sure how you read my comment and came to this conclusion. Please read it again as this isn't a bug, its how an application behaves when your token has expired. As I've asked in my last sentence Please reply to this message with what you expect to happen when your token has expired and not able to refresh due to whatever reason.

I'm not suggesting it's a bug, but that it's not a great developer experience. I'm trying to learn the Supabase framework and I'm building upon the Supabase Sveltekit example project that demonstrates auth flow.

What happens: After my token expires, if I try to load the homepage of my app, I'm met with a 400 error instead of my homepage. In the console I see the error

AuthApiError: Invalid Refresh Token: Refresh Token Not Found
    at /home/project/node_modules/@supabase/gotrue-js/dist/main/lib/fetch.js:41:20
    at process.processTicksAndRejections (node:internal/process/task_queues:95:5) {
  __isAuthError: true,
  status: 400
}

I honestly can't tell which call from my codebase is triggering this error.

What I expect to happen: I would expect the example app to show where to gracefully handle errors of this nature and to redirect the user a login screen if its not possible to authorize the session.

I want to emphasize again, that I'm a brand new supabase user. I've got an mvp of my app going and I'm sporadically running into this Auth problem. After digging around in my code base trying to figure out where it's happening I started googling which ended up on this thread.

silentworks commented 1 year ago

@jareddr can you provide a link to the example app you are referring to please?

silentworks commented 1 year ago

its how an application behaves when your token has expired.

Fair point, but what's the case where token has legitimately expired? Isn't it supposed to be permanent until logout?

@tomekit the cases are listed above by various users. These are JWTs which have expiry time, the supabase-js library refreshes them before they expire most of the time if autoRefresh is turned on, but there are scenarios where this could fail. JWTs aren't meant to be long lived, they are short lived tokens.

jareddr commented 1 year ago

@jareddr can you provide a link to the example app you are referring to please?

Sure, this is what I used as a base for my project: https://github.com/supabase/supabase/tree/master/examples/user-management/sveltekit-user-management

DOZBORNE commented 1 year ago

I also had this issue on fresh install of Supabase. I'm trying this example app based on a recommendation you made @silentworks in the discord channel after reading through some bugs there.

Just putting here for a +1. I downloaded from this link. Fire up app. Instantly errors/failure to log in due to refresh token error.

mb21 commented 1 year ago

Seems there's just a couple of console.log statements lying around in the supabase codebase (this is one of them we hit as well). Would be great to have this configurable with a logging level, or better yet, that we could supply our own logger. As console.log is not recommended in production in Node.js if I remember correctly (it's not a big problem in the browser of course).

j4w8n commented 1 year ago

Seems there's just a couple of console.log statements lying around in the supabase codebase (this is one of them we hit as well). Would be great to have this configurable with a logging level, or better yet, that we could supply our own logger. As console.log is not recommended in production in Node.js if I remember correctly (it's not a big problem in the browser of course).

@mb21 you can supply your own debug logger in the latest version of supabase-js

https://github.com/supabase/gotrue-js/releases/tag/v2.52.0

jdgamble555 commented 1 year ago

The issue I'm facing is when logged out. I should not get a AuthApiError: Invalid Refresh Token: Refresh Token Not Found error when I am logged out. @silentworks If I'm logged out, the expected response is to NOT have an error at all. It seems to be showing this message on any read to the database, even though my app technically works fine.

J

Jennelle186 commented 1 year ago

How can you resolve this? I can also see this error in my node terminal whenever I npm run dev my application

solsonal commented 1 year ago

Seems like we have a bug here (using try without catch) , so the exception Invalid Refresh Token: Refresh Token Not Found is thrown here when you call getSession()

kalafut commented 1 year ago

I'm seeing the same exception when using https://github.com/supabase/supabase-flutter during initialize().

marcfrankel commented 1 year ago

Seeing this issue after switching from the next auth helper to the SSR package. Not breaking anything. Just causing logouts to be super frequent

ARMATAV commented 1 year ago

Same here - reminds me of an original bug in auth-helpers. Logs you out, in my case "token already used".

yl-chow commented 1 year ago

Same here - reminds me of an original bug in auth-helpers. Logs you out, in my case "token already used".

I'm also facing the same issue. It just.. happens.

ARMATAV commented 1 year ago

Seeing this issue after switching from the next auth helper to the SSR package. Not breaking anything. Just causing logouts to be super frequent

And this is exactly the cause for me too - to repro just use the with-supabase NextJS example, and wait a while, you'll get logged out

eposha commented 1 year ago

Seeing this issue after switching from the next auth helper to the SSR package. Not breaking anything. Just causing logouts to be super frequent

And this is exactly the cause for me too - to repro just use the with-supabase NextJS example, and wait a while, you'll get logged out

And to reduce the time to get the error, the JWT life time should be reduced

https://supabase.com/dashboard/project/{YOUR_ID}/settings/auth

@silentworks Can you help with it?

cipriancaba commented 1 year ago

Just opened an issue here, missed this issue

https://github.com/supabase/supabase/issues/18981

You might want to try to set autoRefreshToken to false in all of your createServerClient except for the one that runs in the middleware.

return createServerClient(
    env.NEXT_PUBLIC_SUPABASE_URL,
    env.NEXT_PUBLIC_SUPABASE_ANON_KEY,
    {
      auth: {
        autoRefreshToken: false,
      },
      cookies: {
        get(name: string) {
          return cookieStore.get(name)?.value
        },
        set(name: string, value: string, options: CookieOptions) {
          try {
            // eslint-disable-next-line @typescript-eslint/no-unsafe-argument
            cookieStore.set({ name, value, ...options })
          } catch (error) {
            // The `set` method was called from a Server Component.
            // This can be ignored if you have middleware refreshing
            // user sessions.
          }
        },
        remove(name: string, options: CookieOptions) {
          try {
            // eslint-disable-next-line @typescript-eslint/no-unsafe-argument
            cookieStore.set({ name, value: '', ...options })
          } catch (error) {
            // The `delete` method was called from a Server Component.
            // This can be ignored if you have middleware refreshing
            // user sessions.
          }
        },
      },
    }
eposha commented 1 year ago

Just opened an issue here, missed this issue

supabase/supabase#18981

You might want to try to set autoRefreshToken to false in all of your createServerClient except for the one that runs in the middleware.

return createServerClient(
    env.NEXT_PUBLIC_SUPABASE_URL,
    env.NEXT_PUBLIC_SUPABASE_ANON_KEY,
    {
      auth: {
        autoRefreshToken: false,
      },
      cookies: {
        get(name: string) {
          return cookieStore.get(name)?.value
        },
        set(name: string, value: string, options: CookieOptions) {
          try {
            // eslint-disable-next-line @typescript-eslint/no-unsafe-argument
            cookieStore.set({ name, value, ...options })
          } catch (error) {
            // The `set` method was called from a Server Component.
            // This can be ignored if you have middleware refreshing
            // user sessions.
          }
        },
        remove(name: string, options: CookieOptions) {
          try {
            // eslint-disable-next-line @typescript-eslint/no-unsafe-argument
            cookieStore.set({ name, value: '', ...options })
          } catch (error) {
            // The `delete` method was called from a Server Component.
            // This can be ignored if you have middleware refreshing
            // user sessions.
          }
        },
      },
    }

It doesn't work for me

@silentworks Do you have any idea?