Supabase Authentication Token Cross-Instance Validation Issue #35944
Replies: 15 comments
-
|
I find it strange that getUser would do this since it makes a network request to the supabase instance backend to verify the jwt - which I have assumed gets validated against the jwt secret from that specific project. |
Beta Was this translation helpful? Give feedback.
-
|
Question: are you storing the auth token with a custom name that happens to be the same for each instance? Because by default it stores the project id in the name, so I can't imagine that being picked up by another instance. |
Beta Was this translation helpful? Give feedback.
-
|
I can confirm that solely depending on getSession to determine if there is a session - and using the same custom storage key for both instances - does trick my app into believing someone is logged in. However, none of my calls to the db yield any results since the jwt and user_id are wrong for the instance. Furthermore, calling getUser returns the below error; which makes sense to me since it should be validating the jwt against the wrong supabase instance. {
data: { user: null },
error: AuthSessionMissingError: Auth session missing!
} |
Beta Was this translation helpful? Give feedback.
-
|
Yeah it's strange. Essentially, I'm building an admin CMS for staging/prod instance. So Supabase setup is org then instance for prod, staging, admin cms. Running locally, was logged in as a prod test user, terminated server, ran server for admin cms, (running on different ports fyi). Closed browser tab for prod instance, opened admin and I was logged in as the prod user. No custom storage key. Different env variables. `const { data: { user }, error: userError } = await supabase.auth.getUser();
|
Beta Was this translation helpful? Give feedback.
-
|
Obviously with RLS etc was not able to do much but just found it to be a strange bug since it bypassed authguard so could navigate app etc. |
Beta Was this translation helpful? Give feedback.
-
|
Just to clarify this one:
If this is incorrect, please correct me. If not, I think this could be closed |
Beta Was this translation helpful? Give feedback.
-
|
I can replicate it every time - This await supabase.auth.getUser(); returns authenticated for a user doesn't exist in that instances auth. It literally returns a user object with a user id and authenticated status. If that's not an issue to a SOC2 authenticated org, close away. |
Beta Was this translation helpful? Give feedback.
-
That seems off because the Supabase client calling that function is tied to a specific instance. It doesn't have permission to auth itself against another. |
Beta Was this translation helpful? Give feedback.
-
|
The only thing I can think of is that as the initial auth state is request by localhost, then even though that has been terminated, and localhost (although different port) is making another auth request its validating it. Not sure, but I can replicate it every time. Again, can't do anything as RLS kicks in but can navigate app. |
Beta Was this translation helpful? Give feedback.
-
|
I should add both instances are under one org in Supbase, however with a different url and anon key obvs |
Beta Was this translation helpful? Give feedback.
-
|
I'm not able to replicate this. I get
My nav bar thinks someone is logged in because it only uses getSession; this succeeds because getSession doesn't verify anything against Supabase, just grabs a session based on the name 'token' (custom storageKey). However, calling getUser on the server-side results in the proper error above. Furthermore, it only shows me that error because I'm using the same storageKey across projects. Since you're not, it shouldn't even be finding an auth token to send with getUser, or process with getSession, when you're logged into your 2nd app. |
Beta Was this translation helpful? Give feedback.
-
|
Not sure why then. Might be my org instance then on Supabase |
Beta Was this translation helpful? Give feedback.
-
|
Hi @jimmy3574 - Is this an issue still for you? I'm going to move this over as a discussion. |
Beta Was this translation helpful? Give feedback.
-
|
This is expected behavior in some cases and does not mean Supabase is accepting cross-project authentication. The important distinction is:
What likely happened here is that both local apps were sharing browser storage on However, that does not mean the JWT is actually valid for the second Supabase project. Evidence from the discussion:
The key point is: getSession() != secure authentication validation
getSession() can return cached/local persisted auth state without verifying the token against the current project.
Real validation happens when:
calling protected APIs
refreshing sessions
querying DB with RLS
verifying JWT signature against the current project's secret
That is why:
navigation/auth guards may appear authenticated
but DB queries and authenticated backend calls fail
The most likely explanation here is localhost/browser storage collision between multiple apps during development, especially if:
same localhost domain
same storage namespace
stale persisted auth state
hot reload/dev server reuse
To avoid this entirely, set an explicit unique storage key per project:
const supabase = createClient(url, key, {
auth: {
storageKey: 'my-project-auth'
}
})
and avoid relying solely on getSession() for authorization decisions.
Use:
await supabase.auth.getUser()
or backend JWT verification for actual auth validation instead. |
Beta Was this translation helpful? Give feedback.
-
|
This is almost certainly not a Supabase bug, but it is a genuinely confusing behavior worth explaining clearly. The most likely cause: shared localStorage during local development If both applications are running on The critical distinction between
How to verify which scenario you are in Check what const { data, error } = await supabase.auth.getUser(tokenFromInstanceA)
console.log(data, error)If this returns a valid user with no error, check whether both projects share the same JWT secret (possible if self-hosted with copied config). If it returns an error, then the token is correctly being rejected at the server level and your application logic is the part accepting the session prematurely via The fix if this is a localhost development issue Use different hostnames instead of different ports for local development. You can add entries to The fix if this is a production concern Always use |
Beta Was this translation helpful? Give feedback.
Uh oh!
There was an error while loading. Please reload this page.
-
Bug report
When a user has a valid Supabase authentication token from Instance A (e.g., Web App X), this token is being incorrectly accepted as valid by Instance B (a separate Web App with SUpabase Auth) without proper validation.
Steps to reproduce:
Expected behavior:
auth.getUser()andauth.getSession()methods should validate the token against the current instanceActual behavior:
auth.getUser()andauth.getSession()return successful responses with tokens from different instancesSecurity Implications:
Library:
-Supabase JS (Client SDK, not SSR)
Beta Was this translation helpful? Give feedback.
All reactions