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

Supabase.initialize don't refresh token after 1h on app open again #906

Open
gabrielviannadev opened this issue Apr 26, 2024 · 27 comments
Open
Labels
auth This issue or pull request is related to authentication bug Something isn't working

Comments

@gabrielviannadev
Copy link

Describe the bug
From 1 week and a couple days ago, we have a problem in our application, basically, before this time, users use login with OTP normally, and have a refresh token automatically, every good.

But now, after this days, when user login with OTP, this generate a valid token, but, if they close and access app again after 1 hour, this is logged out, because the token is no more valid.

The problem is, i use Supase from 1 year, and never have this problem, i use Supabase.initialize all time on start app, and this check if the old token is valid, if not, refresh that. But now, don't work anymore.

Debbuging, i see the client.auth.session <- its comming null on app after this 1hour time.

To Reproduce
Only starts Supabase.initialiize, this generate a token in console -> put app closed in background, and re-open after a medium time, this retorn the same token (don't refresh).

Expected behavior
The token needs to be refresh when i open app again, when the old token its not valid, and goTrue Session not be null.

Screenshots

Version (please complete the following information):
On Linux/macOS
I'm using latest version of Flutter, and latest version of Supabase supabase_flutter 2.5.1.

On Windows

Additional context
I think explained all infos.

@gabrielviannadev gabrielviannadev added the bug Something isn't working label Apr 26, 2024
@gabrielviannadev gabrielviannadev changed the title Supabase.initialiize don't refresh token after 1h on app open again Supabase.initialize don't refresh token after 1h on app open again Apr 26, 2024
@dshukertjr dshukertjr added the auth This issue or pull request is related to authentication label Apr 26, 2024
@dshukertjr
Copy link
Member

@gabrielviannadev I cannot reproduce this on my end, but are you able to reproduce it? Do you have reliable steps to reproduce every time, or do you see this happening only once in a while?

@com8member2
Copy link

i am also facing the issue but i is not happening consistently but many times when app is opened after some time is gives below error and user gets sign out.

invalid JWT: unable to parse or verify signature, token is expired by 2m5s
I/flutter (18692): #0      GotrueFetch.request (package:gotrue/src/fetch.dart:99:7)
I/flutter (18692): <asynchronous suspension>
I/flutter (18692): #1      GoTrueAdminApi.signOut (package:gotrue/src/gotrue_admin_api.dart:42:5)
I/flutter (18692): <asynchronous suspension>
I/flutter (18692): #2      GoTrueClient.signOut (package:gotrue/src/gotrue_client.dart:813:9)
I/flutter (18692): <asynchronous suspension>
I/flutter (18692): #3      GoTrueClient._callRefreshToken (package:gotrue/src/gotrue_client.dart:1115:11)
I/flutter (18692): <asynchronous suspension>
I/flutter (18692): #4      GoTrueClient.recoverSession (package:gotrue/src/gotrue_client.dart:928:16)
I/flutter (18692): <asynchronous suspension>
I/flutter (18692): #5      SupabaseAuth.recoverSession (package:supabase_flutter/src/supabase_auth.dart:86:11)
I/flutter (18692): <asynchronous suspension>
I/flutter (18692): #6      CancelableCompleter.complete.<anonymous closure> (package:async/src/cancelable_operation.dart:425:16)
I/flutter (18692): <asynchronous suspension>

@marektomczyk
Copy link

I experience the same problem on my production app - users are randomly logged out from the app. In Supabase logs, I see exactly the same log as @com8member2 included above. Please bump priority on it!

@dshukertjr
Copy link
Member

@com8member2 @marektomczyk
So this random logout happens not when the user launches the app but when they have the app opened for a long time?

@tomekit
Copy link

tomekit commented Jun 7, 2024

In our case when app is closed for more than an hour, token expires which is expected. Problem happens when app is started again, we then issue the API request using the most recent JWT (which has expired) which results in API call failing with 403 Forbidden.
The: _callRefreshToken logic auto refreshes the token successfully, but new token is received shortly (depending on network conditions etc.) after the initial request was issued.

Perhaps: _refreshTokenCompleter could be exposed, so we could either await on it if it's Future or even run: _callRefreshToken ourselves to ensure that latest JWT is fetched before any API call is made.

@Vinzent03
Copy link
Collaborator

@tomekit What's your use case to manually use the latest jwt? If you are calling supabase endpoints via the sdk, the sdk should try to get a new jwt before making the actual request.

@zhangheng2022
Copy link

Maybe we need an example

@tomekit
Copy link

tomekit commented Jul 1, 2024

Hi @Vinzent03,

Sorry for late reply. In our case we're using Supabase mostly for auth and then pass JWT to external APIs outside of Supabase ecosystem.

I think it's fair to assume that there are many systems (legacy or not) which would like to integrate authentication mechanism and not necessarily would like to be rewritten using Supabase mindset.
Given that JWT integration is extremely easy I think it's rather common use case to "connect" other systems to unified auth system which Supabase's GoTrue is (https://github.com/supabase/gotrue).
The current downside is that Supbase doesn't support public keys, so currently sharing secret to external API shares private key, which isn't inherently secure: https://github.com/orgs/supabase/discussions/12759#discussioncomment-9584184)
It would be also good to allow optional 2FA JWT verification, we've added that ourselves: supabase/auth@8878f32 but that's probably bit offtopic.

My point is, that if Supabase Flutter allows user to access current session:

Session? get currentSession => _currentSession;

it already leaks enough internals (and that's good, because it allows JWT external use) that it should probably allow user to access Future to await to make sure that currentSession is up to date, but that's my opinion only.

@Vinzent03
Copy link
Collaborator

Calling refreshSeession() if the session is expired (session.isExpired) should fix it. It returns the future to the already ongoing request or starts a new refresh.

@tomekit
Copy link

tomekit commented Jul 2, 2024

Thanks for you reply. I've just double checked our codebase and that's actually what we've been doing already... and in fact it should behave as you describe.
Sorry for the confusion caused, we're still getting some intermittent issues with not up to date token, but it seems that this issue likely lies somewhere on our end, as Flutter's goauth library resolved most of these over the past months.

@vikrvm
Copy link

vikrvm commented Sep 4, 2024

Hi, @gabrielviannadev. Did you ever find a solution to this issue? I've run into the same issue when the iOS application tries to update in the background.

@dshukertjr
Copy link
Member

@vikrvm Do you have a way to reproduce this issue consistently? That would help us figure out what the root cause is.

@vikrvm
Copy link

vikrvm commented Sep 4, 2024

@vikrvm Do you have a way to reproduce this issue consistently? That would help us figure out what the root cause is.

I do, I have a Swift application I'm working on and this happens frequently. I could send you over a private copy? It's very barebones at the moment but you'll see the logs in the dashboard start throwing token errors after some time.

@dshukertjr
Copy link
Member

@vikrvm Is it a Swift or Flutter application running on iOS?

@vikrvm
Copy link

vikrvm commented Sep 4, 2024

@vikrvm Is it a Swift or Flutter application running on iOS?

I'm running into the same issue described in a Swift application using Supabase. I know this is a Flutter specific issue but the description matched my issue exactly also.

@dshukertjr
Copy link
Member

@vikrvm Okay, could you open an issue on the supabase-swift repo for that?

@ArbazIrshad
Copy link

We are experiencing the same issue too. We are using supabase as our auth service and pass the JWT to our backend. If the user opens the app after 1 hour the token doesn't refresh and user is unable to use our app. But if the user kills the app and opens it again, It starts working again. For now, we are manually refreshing the token.

@dshukertjr
Copy link
Member

@ArbazIrshad And this is with supabase-flutter?

@ArbazIrshad
Copy link

Yes, Currently facing this issue with supabase-flutter.

@zcmgyu
Copy link

zcmgyu commented Dec 12, 2024

Please fix

@dshukertjr
Copy link
Member

This issue should not be happening. supabase-flutter will refresh the token automatically if the token has expired.

If you really believe you are facing this issue, please provide a public GitHub repo with minimal code to reproduce this issue.

@zcmgyu
Copy link

zcmgyu commented Dec 15, 2024

@dshukertjr こんにちは Tyler :D

In theory, the refresh token should be updated upon expiration.

However, in practice, I encountered an issue with supabase-flutter 2.8.1. After a while ( maybe more than an hour), when I reopened the app and connected to the realtime channel, I received an error message indicating the token had expired: "Invalid value for JWT claim \"exp\" with value xxx" as the screenshot.

Upon reviewing the source code, I found that the realtime auth token should be updated if initialSession, tokenRefreshed, or signedIn is triggered.

void _handleTokenChanged(AuthChangeEvent event, String? token) {
if (event == AuthChangeEvent.initialSession ||
event == AuthChangeEvent.tokenRefreshed ||
event == AuthChangeEvent.signedIn) {
realtime.setAuth(token);
} else if (event == AuthChangeEvent.signedOut) {
// Token is removed
realtime.setAuth(_supabaseKey);
}
}

Therefore, the realtime auth token was not updated because the token refresh did not occur as I think

Realtime connect source:

void warmUpLobby() {
  logger.i('Warm up lobby');

  warmUpLobbyChannel = supabase
      .channel('lobby')
      .onPostgresChanges(
          event: PostgresChangeEvent.insert,
          schema: 'public',
          table: 'players',
          filter: PostgresChangeFilter(
            type: PostgresChangeFilterType.eq,
            column: 'user_id',
            value: authService.userId,
          ),
          callback: _handleWarmUpPlayerCreated)
      .onSystemEvents(_handleSystemEvent)
      .subscribe(_handleSubscribeLobby);
}

Future<void> _handleSubscribeLobby(
    RealtimeSubscribeStatus status, Object? error) async {
  if ([
    RealtimeSubscribeStatus.channelError,
    RealtimeSubscribeStatus.timedOut,
  ].contains(status)) {
    logger.i('Subscribe lobby status: $status');
    logger.i('Subscribe lobby error: $error');
    showDialog(
        context: Get.context!,
        builder: (context) => AlertDialog(
              title: Text('Status: $status'),
              content: Text('Error subscribing to lobby: $error'),
            ));
    await _restartLobbySubscription();
  }
}

@dshukertjr
Copy link
Member

@zcmgyu

Your access token is being refreshed, but you are trying to connect to Realtime before you receive the new accessToken, which is causing this error is what I think is happening here. This should certainly be handled by the client, so we will confirm on our end if this is really happening, but make necessary changes.

For now, you can just make sure to only connect to realtime after you receive the tokenRefreshed auth event.

@zcmgyu
Copy link

zcmgyu commented Dec 16, 2024

@dshukertjr Thank you. Just to confirm, I’ve already addressed this case across all real-time channels, and I hope it resolves the issue.

Future<void> _handleSubscribeLobby(
    RealtimeSubscribeStatus status, Object? error) async {
  if ([
    RealtimeSubscribeStatus.channelError,
    RealtimeSubscribeStatus.timedOut,
  ].contains(status)) {
    logger.i('Subscribe lobby status: $status');
    logger.i('Subscribe lobby error: $error');
    if (error.toString().contains('Invalid value for JWT claim')) {
      logger.i('Session is expired while warm up lobby, refreshing session');
      await supabase.auth.refreshSession();
    }
    await _restartLobbySubscription();
  }
}

@dshukertjr
Copy link
Member

@zcmgyu Not sure if that will fix the issue, but did it work?

Once this version is released, it should fix the issue for you.

@zcmgyu
Copy link

zcmgyu commented Dec 17, 2024

@dshukertjr I'm not sure it worked. Until now, that issue did not occur. Waiting for it :D

@tomekit
Copy link

tomekit commented Dec 29, 2024

In our case token wasn't automatically refreshing in the background after upgrading to 2.8.2 release.
We've downgraded to 2.8.0 and things are back to normal.
This is applicable to web version of our app: https://web.s3drive.app/

Desktop and mobile seem to be unaffected and refreshing takes places normally on 2.8.2.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
auth This issue or pull request is related to authentication bug Something isn't working
Projects
None yet
Development

No branches or pull requests

10 participants