Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Updating & refreshing user metadata causes Error "Invalid Refresh Token: Already Used" #755

Open
2 tasks done
skoshx opened this issue Aug 4, 2023 · 6 comments
Open
2 tasks done
Labels
auth bug Something isn't working

Comments

@skoshx
Copy link

skoshx commented Aug 4, 2023

Bug report

  • I confirm this is a bug with Supabase, not with my own application.
  • I confirm I have searched the Docs, GitHub Discussions, and Discord.

Describe the bug

I'm trying to use the Supabase user metadata as a means of storing user metadata (like preferences eg. is_subscribed_to_newsletter), since it seems way more intuitive than having a clone of it in Postgres, and updating those... This is my code:

await supabaseClient.auth.updateUser({
            data: {
                ...metadata,
                ...updatedMetadata
            }
        })
        const { error } = await supabaseClient.auth.refreshSession()

Basically, the first time I update any user metadata, it works perfectly, but the second time and all times after that I get the error "Invalid Refresh Token: Already Used"

As seen here and here, other people are also facing this issue.

To Reproduce

  1. Create a Next app with an authenticated user
  2. call updateUser, then refreshSession
  3. Try to updateUser, then refreshSession again, and you will get error "Invalid Refresh Token: Already Used"

Expected behavior

I expect the refreshSession to also refetch a new refresh-token, while refetching updated account information for the useUser hook.

Being able to use the user_metadata field for small personal user preferences is super good for DX. I always cringed at the idea of creating some Postgres triggers (that last I checked weren't even production ready) only to create a matching public.users table that then contained the users data, when we could just use the user_metadata itself.

Screenshots

If applicable, add screenshots to help explain your problem.

System information

  • OS: macOS
  • Browser (if applies) Chrome
  • Version of supabase-js: 2.26.0
  • Version of Node.js: v18.15.0
@skoshx skoshx added the bug Something isn't working label Aug 4, 2023
@gregnr gregnr added the auth label Aug 4, 2023
@douglasqian
Copy link

douglasqian commented Aug 4, 2023

+1 also hitting this in our production app. Not a blocker, but hugely painful to have to log out and log back in everytime to manually reset the token.

For our use case, we use user_metadata to track the current org view we are switched into from an admin view. Here's the code snippet:

    // Updates currently active user org in app as well as
    // cached org in Supabase user metadata.
    const setOrgForUser = async (newOrg: Organization) => {
        setCurrentOrg(newOrg)
        await supabase.auth.updateUser({
            data: {
                org: newOrg.id,
            },
        })
        await supabase.auth.refreshSession()
        const getUserRes = await supabase.auth.getUser()
        console.log('getUserRes', getUserRes)
        // window.location.reload()
    }

Normally we call window.location.reload() to ensure all components are refreshed using this new orgID, but I commented it out here to trace this more carefully. What I'm seeing is that the getUserRes log outputs the expected result when I switched from org A to B. I confirmed by running a query in the SQL editor on the auth.users table as well to read out the value of raw_user_meta_data.org on that row.

But after refreshing the browser tab it loads org A again and I'm trying to debug this. My understanding is that the access token and refresh tokens come from the 3rd party identity provider (in our case Google) so I'm not sure why failing to use the refresh token would lead to the issue that I'm observing.

@kangmingtay
Copy link
Member

@skoshx i can't seem to reproduce the issue on my end - this is the code i'm using to test: https://gist.github.com/kangmingtay/c3559556033ba599f51182cf5956ac6c

@douglasqian

My understanding is that the access token and refresh tokens come from the 3rd party identity provider (in our case Google)

The access and refresh tokens are created by gotrue as gotrue acts as the intermediary. If you need the tokens returned by google, you need to retrieve the provider_access_token or provider_refresh_token from the session.

Based on your code, you don't need to call refreshSession followed by getUser. refreshSession already returns an AuthResponse which contains the user's information in it.

@kangmingtay kangmingtay transferred this issue from supabase/supabase Aug 8, 2023
@skoshx
Copy link
Author

skoshx commented Aug 8, 2023

@kangmingtay , the problem is that useUser hook doesn't return refreshed user metadata without calling refreshSession.

@hf
Copy link
Contributor

hf commented Aug 10, 2023

This feels like an issue for another repo. gotrue-js does not define a useUser hook. @silentworks where is this defined?

@silentworks
Copy link
Contributor

This was fixed recently in the auth-helpers by a PR from a user supabase/auth-helpers#617

@skoshx
Copy link
Author

skoshx commented Aug 10, 2023

I see that now the session is updated which is good. I'm wondering though that does the refreshed session also make it so that the refresh token rotates?

Just wondering that does the auth-helper fix really close out this issue.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
auth bug Something isn't working
Projects
None yet
Development

No branches or pull requests

6 participants