Closed dannypurcell closed 9 months ago
Quite a large wall of text, in summary the issue seems to be this:
It is not possible to switch the Supabase app registration to a Single-Page Application client-type as it is a confidential server type and uses the ClientId/ClientSecret to authenticate itself with the Proivder's token endpoint. Changing the app type results in a failure to complete the initial Sign-in for the user.
This is very much not an issue with Supabase Auth but with your setup. The problem comes in that it appears that you are trying to refresh the provider_refresh_token
inside your frontend code. This is incorrect, and it should be done inside your server. You can use a Supabase Edge function if you like, or build any other server.
That server will use the Microsoft OAuth APIs including the client_secret
to refresh the token in question.
This was a request to implement an endpoint for triggering that refresh as suggested by @kangmingtay in https://github.com/supabase/gotrue/pull/641#issuecomment-1235178425
The developer using gotrue will have to manage the provider_access_token and provider_refresh_token on their own. At this point, gotrue does not provide any endpoints to help refresh the provider_access_token, though that can be a feature we'll consider adding in the future.
This comment along with the general design implied by returning the provider_refresh_token
in the front-end client in the first place, along with the efforts and discussion in this issue https://github.com/supabase/gotrue-js/issues/131 all indicate that the original intent was that refreshing could be done in the front-end.
That is the reason this was reported as an issue for GoTrue.
Since you are declining to help developers out with that, at minimum the documentation needs changed to match your suggested workaround here.
Currently https://supabase.com/docs/guides/auth/social-login#provider-tokens states
Your application will need to use the provider refresh token to obtain a new provider token.
Typically that means the front-end application. That will almost certainly be how most people will read it in the context of Supabase, which is a backend as a service. The whole idea is that we don't have to build and host another separate service in addition to what Supabase provides, especially if the only function for the separate service is just to refresh a token.
Edge function can make sense but that's rather awkward, non-intuitive, and not documented anywhere as far as I have seen.
Sorry it was a lot to read; however, the detail was intended to ensure that this was a sensible request to be making and that I had exhausted all of the obvious and documented choices for how to handle this before bothering the team with it.
@hf do you have an example of an edge function doing a provider token refresh such that supabase.auth.currentSession.providerToken
continues to have a valid provider token after the supabase client does a token refresh?
I'm looking in to your suggestion and it really seems like the edge functions have the same issue as trying to use the provider_refresh_token
to request a new provider_token
from the front end code. The Deno invocations will still be calling the provider's token endpoint from a different origin than the provider_refresh_token
was issued to. So it still will not work.
Additionally, it is unclear what the edge function should do with any token it does receive, such that said token will then be available to the front-end app.
I can see trying to store it in the auth
schema's flow_state
since it has a provider_access_token
column but that goes against the documentation and console's warnings against modifying anything in that schema and against the apparent reasons why the token is not already maintained by GoTrue.
https://supabase.com/docs/guides/auth/social-login#provider-tokens
Provider tokens are intentionally not stored in your project's database. This is because provider tokens give access to potentially sensitive user data in third-party systems.
Could also just return it from the invocation but then what is the point of using supabase.auth.currentSession.providerToken
in the first place? And this approach is basically the same, from a security standpoint, as just sending the client_secret
to the front-end app. Which, again, is not recommended under OAuth spec. The point of the PKCE flow is that we don't trust the front-end code to get tokens independent of the browser/origin the user is signing in from, since it is publicly hosted.
To be clear, what we're trying to understand here is why Supabase auth offers the provider_token
for the front-end code to use if it is going to be nulled out when Supabase does it's own token refresh. If you provide us the token GoTrue used to login with the provider, then it needs to stick around and there needs to be a way to refresh it, otherwise it's not actually useful.
However, if that token was never intended to be used to work with the provider API, and that reasoning in the docs is a real security concern, then there is no reason for it to even be available to the front-end code and the token's presence there represents a security flaw.
Any updates on this? @dannypurcell I'm curious what workaround you ended up using, if you don't mind sharing
Bug report
Describe the bug
Related to https://github.com/supabase/gotrue/pull/641#issuecomment-1235178425 and https://github.com/supabase/gotrue/issues/83#issuecomment-1913370935
It appears there is no viable way for a client app to use the
provider_refresh_token
returned by GoTrue.This is essentially a request for the suggestion in point 2. of @kangmingtay 's https://github.com/supabase/gotrue/pull/641#issuecomment-1235178425 to be implemented as mentioned.
As far as my own research on this use case has taken me, it seems a Supabase Auth endpoint is needed which would make the token refresh request to the provider and return a new
provider_access_token
and newprovider_refresh_token
(if the provider includes it). Without this, theprovider_token
may still be used to interact with the Provider's APIs but once it expires there is no good way to refresh it from the origin the app client is running on.Alternative options include, making the user login again or making the user login twice in the first place, once for supabase and once for the app itself. Both of those are no good from a UX standpoint and remove much of the utility of using Supabase, so are not really viable.
Reporting as a bug due to the documented method for an app client to use both Supabase Auth and the provider's API, not being possible as the authors originally intended.
To Reproduce
Create an application using Supabase with login possibly using https://github.com/supabase-community/flutter-auth-ui#social-auth
Ensure OAuth scopes requested include
offline_access
as in example:Set up a capture for supabase client auth state change event such as:
Set up secondary OAuth client for use with
providerToken
and for refreshing withproviderRefreshToken
Sign in and trigger token refresh request, verify token request in browser network log
grant_type: refresh_token refresh_token: ... client_id: ...
HTTP/1.1 400 Bad Request Cache-Control: no-store, no-cache Pragma: no-cache Content-Type: application/json; charset=utf-8 Expires: -1 Strict-Transport-Security: max-age=31536000; includeSubDomains X-Content-Type-Options: nosniff Access-Control-Allow-Origin: * Access-Control-Expose-Headers: Content-Length,Content-Encoding,x-ms-request-id Access-Control-Allow-Methods: POST, OPTIONS P3P: CP="DSP CUR OTPi IND OTRi ONL FIN" x-ms-request-id: ... x-ms-ests-server: 2.1.17122.3 - WUS3 ProdSlices report-to: {"group":"network-errors","max_age":86400,"endpoints":[{"url":"https://identity.nel.measure.office.net/api/report?catId=GW+estsfd+chi"}]} nel: {"report_to":"network-errors","max_age":86400,"success_fraction":0.001,"failure_fraction":1.0} Referrer-Policy: strict-origin-when-cross-origin X-XSS-Protection: 0 Set-Cookie: fpc=...; expires=Tue, 27-Feb-2024 00:38:50 GMT; path=/; secure; HttpOnly; SameSite=None Set-Cookie: x-ms-gateway-slice=estsfd; path=/; secure; samesite=none; httponly Set-Cookie: stsservicecookie=estsfd; path=/; secure; samesite=none; httponly Date: Sun, 28 Jan 2024 00:38:49 GMT Content-Length: 564
{"error":"invalid_request","error_description":"AADSTS9002326: Cross-origin token redemption is permitted only for the 'Single-Page Application' client-type. Request origin: 'http://localhost:8080'. Trace ID: ... Correlation ID: ... Timestamp: 2024-01-28 00:38:50Z","error_codes":[9002326],"timestamp":"2024-01-28 00:38:50Z","trace_id":"...","correlation_id":"...","error_uri":"https://login.microsoftonline.com/error?code=9002326"}
HttpException: invalid_request: AADSTS9002326: Cross-origin token redemption is permitted only for the 'Single-Page Application' client-type. Request origin: 'http://localhost:8080'. Trace ID: ... Correlation ID: ... Timestamp: 2024-01-28 00:36:29Z
Supabase Auth does not manage refreshing the provider token for the user. Your application will need to use the provider refresh token to obtain a new provider token.
supabase_flutter: ^2.0.2 supabase_auth_ui: ^0.3.0 gotrue: ^2.3.0 oauth2_client: ^3.2.2