GoTrueClient intercepts non-Supabase initiated OAuth preventing manual login flows
Bug report
- [ x] I confirm this is a bug with Supabase, not with my own application.
- [ x] I confirm I have searched the Docs, GitHub Discussions, and Discord.
Describe the bug
Hi there, I've run in to an issue where Supabase Auth is intercepting url params/fragments that contain the keyword access_token in them even when auth requests aren't coming from a Supabase Auth initiated request.
For example, I use Supabase Auth for OAuth Login for social providers so I need to have detectSessionInUrl enabled. However, in my application I have a page for users to authenticate through Facebook Login to connect their Facebook Pages and Instagram Accounts.
I initiate this flow manually (outside of Supabase) using: https://developers.facebook.com/docs/instagram-platform/instagram-api-with-facebook-login/business-login-for-instagram
However the callback URL returns a string that looks like this:
https://my-clever-redirect-url.com/success/?#access_token=EAAHm...&data_access_expiration_time=1658889585&expires_in=4815&long_lived_token=ABAEs...
As you can see there is an access_token fragment. And if the user is logged in via Supabase already, this automatically logs the user out since it detects an access_token being returned in the URL, which is invalid and Supabase Auth uses this as a trigger to log the user out...
This issue occurs due to the check for implicitGrant here: https://github.com/supabase/auth-js/blob/8a1ec0602792191bd235d51fd45c0ec2cabdf216/src/GoTrueClient.ts#L311C31-L311C49
Where it checks if there are params for either (params.access_token || params.error_description)
Is there a way to prevent running this check on certain pages (like my redirect page) so there isn't this kind of conflict?
To prevent this, I thought I could just proxy my response through a server and rewrite the url to not interfere with Supabase but since this value is passed as a URL fragment, this data is not sent to a server and is only accessible in the browser...
Expected behavior
I expect to be able to use OAuth flow that isn't triggered by Supabase Auth without there being a conflict and Supabase intercepting non Supabase initiated Auth flows.
Screenshots
System information
- OS: MacOS
- Browser chrome
- Version of supabase-js: 2.43.5
- Version of Node.js: v20.12.0
Related: https://github.com/supabase/supabase-js/issues/1704
yea exactly, for now I've had to pnpm patch the GoTrue client to do this
That was helpful, @uncvrd. I'm having the same issue so I created a patch. Will keep an eye here for any updates.
Should be fixed in supabase/auth-js#986. I'm not sure when it'll get merged, but I believe it's close to being approved.
Excited to see this land!
Experiencing the same thing and thought I was going insane. I'm also hoping to see this fixed soon!
@ben-katz as a short term fix I just stood up an interim api route to catch the callbacks and forward onto the destination as code_= instead of code= and then had my code prepared to watch for that param
@mandarini hi there! reviewing the changes made in the associated PR, this unfortunately doesn't fix the issue presented in this ticket :/
I am running 2.87.1
On this line https://github.com/supabase/supabase-js/blob/13af49b21cde4e49ca5ebd7a77e933dc4d9747f8/packages/core/auth-js/src/GoTrueClient.ts#L467
there is a check to see if the url contains the correct params to be deemed something to act upon by the supabase auth library, unfortunately for OAuth flows like Facebook they return a hash with #access_token=xxyyzz (example in original post above) which still triggers the implicit auth flow in supabase auth and since this token is invalid for supabase, it causes my app to log users out.
I can't proxy this response to a server endpoint since the access token is in a hash and these cannot be accessed server-side. Would you kindly re-open this ticket?
Thank you!
EDIT:
by doing this, I can fix it for now, but not ideal
_isImplicitGrantCallback(params) {
return Boolean(params.access_token || params.error_description) && !["/facebook/redirect"].includes(window.location.pathname);
}
Hi @uncvrd 💚
this looks like a mismatch 😢
between your client's flowType (pkce) and facebook returning implicit grant params.
try setting detectSessionInUrl: false on your redirect route so you can handle the callback yourself.
please let me know if this sorts it out
hi @7ttp thanks for the quick response! In my original message I mentioned that I need detectSessionInUrl since I use Social OAuth providers for Supabase Login in the app. I just use a single client across the site that I import in to each file I need to make queries from....
export const supabase: SupaClient = createServerClient<Database>(serverEnv.VITE_SUPABASE_PROJECT_URL, serverEnv.VITE_SUPABASE_ANON_KEY, {
cookies: {
getAll() {
return Object.entries(getCookiesIsomorphicFn()).map(([name, value]) => ({
name,
value,
}))
},
setAll(cookies) {
cookies.forEach((cookie) => {
setCookieIsomorphicFn(cookie.name, cookie.value)
})
},
},
cookieOptions: {
domain: serverEnv.VITE_WEB_HOSTNAME,
},
})
Are you saying that I need a separate createServerClient with detectSessionInUrl disabled just for this route?
@7ttp they aren't using Supabase OAuth for this flow, so the mismatch wouldn't apply.
@uncvrd the only thing I can think of is to switch your Supabase OAuth tactic to use the pkce mode. I'm not sure if that's viable for you or not.
@j4w8n that's correct, this OAuth flow is independent from my general User Auth login flow handled nicely by Supabase.
Naive and general question to the audience, but is it possible for Supabase Auth to populate the callback URL with a Supabase Auth unique query param so that the library only acts upon such callbacks if a ?isSupabaseAuth=true&access_token=abc123 param exists or something?
This would ensure there wouldn't be any library conflict....just an idea
Do you find that this issue needs to reopen?
I believe it does
Regarding your ask @uncvrd : yes, that seems feasible, if it's something the maintainers wanna do.
Make whatever change needed on the backend, then change the below function in auth-js to do an "and" check for that new param.
/**
* Checks if the current URL contains parameters given by an implicit oauth grant flow (https://www.rfc-editor.org/rfc/rfc6749.html#section-4.2)
*/
private _isImplicitGrantCallback(params: { [parameter: string]: string }): boolean {
return Boolean(params.access_token || params.error_description)
}
Reopened. Thanks for the detailed explanation @uncvrd and the input @j4w8n. To fix this properly, we need a server-side change in GoTrue to include a Supabase-specific identifier in the implicit grant redirect URL. Then we can update _isImplicitGrantCallback to check for that param. I'll discuss with the auth team to see if this is something we want to pursue. Will update here once I have more info.