supabase / supabase-flutter

Flutter integration for Supabase. This package makes it simple for developers to build secure and scalable products.
https://supabase.com/
MIT License
661 stars 154 forks source link

onAuthStateChange not triggered when JWT expired #452

Open KirioXX opened 1 year ago

KirioXX commented 1 year ago

Describe the bug We had this week quite a lot of users where the JWT expired. There users where not logged out instead they had to close and reopen the app to trigger the logout.

In our logs we have seen a lot of these errors:

data:text/text;charset=utf-8,
PostgrestException(message: JWT expired, code: PGRST301, details: Unauthorized, hint: null)

When the exception was thrown, this was the stack:
0       PostgrestBuilder._parseResponse (package:postgrest/src/postgrest_builder.dart:323:0)
1       PostgrestBuilder._execute (package:postgrest/src/postgrest_builder.dart:198:0)
2       PostgrestBuilder.then (package:postgrest/src/postgrest_builder.dart:400:0)

To Reproduce Steps to reproduce the behavior:

  1. Setup a listener to listen for onAuthStateChange (we use a bloc cubit for that )
  2. Login
  3. Let the JWT expire
  4. Try to make a request

Expected behavior User is getting logged out either when the JWT expires or when the try to make a request.

Screenshots

Version (please complete the following information): On Linux/macOS Please run dart pub deps | grep -E "supabase|gotrue|postgrest|storage_client|realtime_client|functions_client" in your project directory and paste the output here.

│   └── supabase_flutter...
│   └── supabase_flutter...
│   └── supabase_flutter...
│   ├── supabase_flutter...
│   ├── supabase_flutter...
│   └── supabase_flutter...
│   ├── supabase_flutter...
│   └── supabase_flutter...
│   ├── supabase_flutter...
│   └── supabase_flutter...
│   └── supabase_flutter...
│   └── supabase_flutter...
├── supabase_flutter 1.7.0
│   ├── supabase 1.6.3
│   │   ├── functions_client 1.1.1
│   │   ├── gotrue 1.6.0
│   │   ├── postgrest 1.2.3
│   │   ├── realtime_client 1.0.3
│   │   ├── storage_client 1.3.0

On Windows Please run dart pub deps | findstr "supabase gotrue postgrest storage_client realtime_client functions_client" in your project directory and paste the output here.

Additional context Add any other context about the problem here.

larsbloch commented 11 months ago

Yes i am using it for a custom api. Actually i dont know if the refresh fails as i cant see any logs from the phone. The refreshsession should be able to get a new token right ? The above code i supplied should be sufficient ?

Im am doing some further tests at the moment to see if i can find a pattern. I might be able to get my flutter developer to help me debug a phone next week so we can do more prints and see output.

Vinzent03 commented 11 months ago

Yeah your code should generally work. Two things though to consider

  1. You have no margin for the expiry check and the actual validation on the server. I would use the newly added isExpired getter on the session, because it adds a 5 second buffer.
  2. As already stated in another post, if the refresh token is already used or invalidated because of a sign out of the same account, the token refresh fails and you have to sign in again. The client should automatically log you out.
KirioXX commented 10 months ago

Hi @dshukertjr, sorry for the delayed response. We where not able to test it, but we just started rolling out to a new customer so I should be able to give you feedback in a couple days on this issue. What though noticed is that we have with the new version some strange behaviour with the streams, I created a new issue for that: https://github.com/supabase/supabase-flutter/issues/604

bretmh commented 8 months ago

Error: PostgrestException(message: JWT expired, code: PGRST301, details: Unauthorized, hint: null).

I'm curious about the progress of this issue. Starting my application I am already logged in, the event AuthChangeEvent.signedIn is fired (session is recovered).

I've checked the calls the state changes and also the session recovery in supabase_auth.dart it all looks fine. It's failing on an RPC call, I've also checked the postgrest_builder.dart and the configuration of the AuthHttpClient. There is nothing wrong, however, the backend doesn't like it. How I've determined it's okay is the expiresAt and currentTime values being used results in the token are still valid.

dshukertjr commented 5 months ago

I'm going to close this issue as we have implemented many updates to mitigate the original issue of the access token not refreshing properly, but if anyone is still experiencing the same issue, please let us know in the comments and we will re-open this one.

tomekit commented 5 months ago

In our case we get expired token in our login handler every time when user starts the app when stored access token (1h expiration time already expired)

Likely because: tokenRefreshed event is sent before: _saveSession finishes, so any code that relies on: tokenRefreshed event and: Supabase.instance.client will generate 403 until the _saveSession finalizes.

gotrue-2.3.0/lib/src/gotrue_client.dart

  _saveSession(authResponse.session!);
      if (!_refreshTokenCompleter!.isCompleted) {
        _refreshTokenCompleter!.complete(authResponse);
      }

      notifyAllSubscribers(AuthChangeEvent.tokenRefreshed);
dshukertjr commented 5 months ago

@tomekit

In our case we get expired token in our login handler every time when user starts the app when stored access token (1h expiration time already expired)

The token being expired on app launch shouldn't be an issue as the token should be automatically refreshed before making an API request.

so any code that relies on: tokenRefreshed

Do you have any code that you use that relies on tokenRefreshed event?

What exactly are you experiencing? Do you get PostgrestException with error code PGRST301 thrown when making API request?