How to handle session storage with CMP

Hi there! We started to use supabase-kt library in our Kotlin Multiplatform Project. I am trying to find a bit more information about how the auth works behind the scenes. Here is our useCase: - When user opens the app, they are either redirected to Auth page or Login. This is based on the session status check obtained from the supabase sdk. We are listening and collecting auth.sessionStatus in ViewModel which updates our state (simple flag if the user is logged in or not). My questions are: - How is the supabase sdk handling the session storage and its persistance, can/should this be configured? If the application is longer than few minutes offline (no internet), the session status is is set to SessionStatus.RefreshFailure. This results in user log-out even though they never logged out manually. User is logged back when they are back online. - How often is the sessionStatus flow emitted? It seems like when the SessionStatus.Refresh failure is returned, it will try to refresh every 10s. Can this be configured somewhere? - Do I need to do any token validation manually? - Such as saving token and refreshing it. Thank you! I would also appreciate some instructive resources related deeper understanding of the supabase-kt library.
18 Replies
ihm40
ihm404w ago
Session management in general is managed in the supabase auth schema session table and i don't think the use of any one library will affect that. It is advised to not play around with such tables because it is managed by supabase and you might introduce changes that may cause problems over time if supabase updated the functionality or schemas in anyway. i think at lease web app applications manage sessions simply by the frequency which the users interacts with supabase and it can be configured for pro users vis authentication -> sessions however i do know that this can be different for other platforms and i think someone on desktop was saying the other day that things get logged out automatically if the user closes their app or something along those lines
TheRealJan
TheRealJan4w ago
How is the supabase sdk handling the session storage and its persistance, can/should this be configured?
The session is stored in the shared preferences on Android via multiplatform settings. You can configure this in the Auth settings when installing the plugin. Whether you should depends on what you are doing and if you know what you are doing
How often is the sessionStatus flow emitted?
Whenever there is an update (session refresh, sign out, expired session, user update, ...). Its a state flow, so there is an underlying property.
If the application is longer than few minutes offline (no internet), the session status is is set to SessionStatus.RefreshFailure. This results in user log-out even though they never logged out manually. User is logged back when they are back online.
This has been changed in the newest versions, the session will still be active until it expires, only then the status gets changed to refresh failure.
Do I need to do any token validation manually? - Such as saving token and refreshing it.
No.
civomt
civomtOP4w ago
@Jan It seems like the frequency of the SessionStatus.RefreshFailure can be changed in the auth config via the "retryDelay" property as well. Thanks for pointing this option out. SessionStatus.RefreshFailure is returned if the session expired. Based on my tests, the session is considered as expired after 60 min. I guess that's the default value of the access token lifespan. I wonder, based on the documentation, the session starts when user sign in, and by default, last indefinetely. Why should I care about the SessionStatus.RefreshFailure then? Or do I mix apples with pears? Also, when I set the Auth settings "alwaysAutoRefresh" property to false, the flow always emmits SessionStatus.Authenticated, doesn't matter if 60 minutes or a few days passed. In this case, even if the user is deleted from the DB, the client's session will stay as "SessionStatus.Authenticated". If I manually call logout function, then the session will be changed to SessionStatus.NotAuthenticated. What we are trying to achieve is that the user log in via the client (mobile app) and stay log in until manually call logout. User can CRUD tables accordingly.
TheRealJan
TheRealJan4w ago
It seems like the frequency of the SessionStatus.RefreshFailure can be changed in the auth config via the "retryDelay" property as well. Thanks for pointing this option out.
You could interpret it that way; its just that if the session expired and a refresh attempt failed, a new status is emitted each time
Based on my tests, the session is considered as expired after 60 min.
The session is not considered expired, it is expired. You cannot use it for requests after that without refreshing it. You can configure this duration in your Supabase dashboard.
I wonder, based on the documentation, the session starts when user sign in, and by default, last indefinetely.
I don't know where you read that it lasts indefinitely. You could say after signing in you as an app don't have to do anything auth related anymore, but the library takes care of refreshing the session in the background
What we are trying to achieve is that the user log in via the client (mobile app) and stay log in until manually call logout.
RefreshFailure does not mean the user is logged out, this just means the session expired and the client could not refresh it yet. Any request with that expired session will fail, thats why there is a separate status. After a successful refresh, the Authenticated status will come back. It will never be deleted or anything like that.
civomt
civomtOP4w ago
The session is not considered expired, it is expired. You cannot use it for requests after that without refreshing it. You can configure this duration in your Supabase dashboard.
How so? If I let the client run in the offline mode and set the "alwaysAutoRefresh" to false, the client will stay logged in and the library returns SessionStatus.Authenticated. Even if the local session is long time after the expiration (ex. set time to the future). If I delete the user from the DB while the client is offline and put the client back online, the flow still returns the SessionStatus.Authenticated even though the user doesn't exists in the db anymore. If I call auth.signOut(SignOutScope.Local), then the flow will trigger with SessionStatus.NotAuthenticated.
I don't know where you read that it lasts indefinitely...
https://supabase.com/docs/guides/auth/sessions#what-is-a-session
You could say after signing in you as an app don't have to do anything auth related anymore, but the library takes care of refreshing the session in the background
That sounds really nice. I would be glad if I can read bit deeper about what is going on behind the scenes. Could you provide some references?
RefreshFailure does not mean the user is logged out, this just means the session expired and the client could not refresh it yet. Any request with that expired session will fail, thats why there is a separate status.
Sorry for being cheeky, but when and how can this happen? You mentioned "library takes care of refreshing the session in the background". Could you give me some real world example when session is expired while request was made?
TheRealJan
TheRealJan4w ago
https://supabase.com/docs/guides/auth/sessions#what-is-a-session
Yea though the session may last indefinitely, the access token does not. And I don't know what you want to do with a session with an invalid access token, thats why I basically said the "session expires". Of course you can always refresh the session and get a new access token
How so? If I let the client run in the offline mode and set the "alwaysAutoRefresh" to false, the client will stay logged in and the library returns SessionStatus.Authenticated. Even if the local session is long time after the expiration (ex. set time to the future). If I delete the user from the DB while the client is offline and put the client back online, the flow still returns the SessionStatus.Authenticated even though the user doesn't exists in the db anymore.
I don't know if I just understand this wrong, but what do you mean? If you sign in, Supabase sends over the session data and the time after which the session (or more correctly the access token) expires. After this time, you basically have an useless access token, so you are basically unauthanticated and can no longer make authenticated requests. Just because you disabled all that and maybe deleted the user, it doesn't change the fact that the stored session is still useless. If you try to make any request with that session/access token, it will fail.
If I call auth.signOut(SignOutScope.Local), then the flow will trigger with SessionStatus.NotAuthenticated.
Yea that is expected? Even though you might see an error in the debug logs because it will try to invalidate the session first.
That sounds really nice. I would be glad if I can read bit deeper about what is going on behind the scenes. Could you provide some references?
There are no docs for the internals, you can read through the code.
Sorry for being cheeky, but when and how can this happen? You mentioned "library takes care of refreshing the session in the background". Could you give me some real world example when session is expired while request was made?
If your device has no internet and the session expired, the client will try to refresh it and it fails obviously. -> RefreshFailure (maybe I understand the question wrong?) And as I said any request with an expired access token will fail, thats why this status exists
There are no docs for the internals, you can read through the code.
The short version is that, once you sign-in, the client imports the session, stores it on the disk (except for Native Linux), starts a coroutine to refresh the session 20% before the session expires, and repeats. This is started automatically once e.g. the app opens and the client is initialized This also handles updating access tokens for realtime channels
civomt
civomtOP4w ago
@TheRealJan Good insights! We are trying to implement the supabase-kt sdk in our project and we try to understand the documentation and how to handle things most reliably. SessionStatus.RefreshFailure -> Session is expired. The user cannot do anything. This will stay periodical and try to refresh the session. The frequency of the tries can be adjusted via auth "retryDelay" property. Is there any limit when trying to refresh stops? Ex. after 10 minutes or number of unsuccessful tries? SessionStatus.NotAuthenticated -> Session is expired or the user has logged out. The user cannot do anything. SessionStatus.Authenticated -> Session is valid and the library will try to refresh the session (token) 20% before the session expires. What is the frequency for that? Is it the same value as assigned with "retryDelay" when configuring the auth? SessionStatus.Initializing -> Just data loading. Could you elaborate and possibly extend assumptions above? Thanks !
garyaustin
garyaustin4w ago
@civomt Also remember the kt client is an open source community project versus a Supabase owned repository. You can skim issues and source code there. This is a good summary of how access_tokens, refresh_tokens and the auth server sessions work. https://supabase.com/docs/guides/auth/sessions
TheRealJan
TheRealJan4w ago
What is the frequency for that? Is it the same value as assigned with "retryDelay" when configuring the auth?
Yea that is why this property exists
Is there any limit when trying to refresh stops?
No
NotAuthenticated -> Session is expired or the user has logged out.
This status will not appear if the session expired
civomt
civomtOP4w ago
Okay thanks. I let the sdk do their job. Next thing. I am accessing the api abstraction in my example ViewModel in the init block. ViewModel is initialized via the Koin DI. If the app is offline and meanwhile the session expired, user opens the app from the BG and the api periodically returns status failure (as expected). However, now if the user puts app back into BG, the periodical update is still ongoing is this intended? I also noticed that with every openning of the app from the BG while the session is expired and app still offline will trigger another concurrent periodical emission. This means - if you open the app 3 times from the BG you will get 3 different periodical emissions. How to prevent this?
class AppViewModel(private val api: AuthApi): ViewModel(){
...
init{
viewModelScope.launch {
api.sessionStatus().collect { sessionStatus ->
when(sessionStatus){
is SessionStatus.Authenticated -> {
println("Session authenticated")
_state.update...
}
is SessionStatus.RefreshFailure -> {
println("Session refresh failure")
_state.update...
}
is SessionStatus.Initializing -> {
println("Session initializing")
_state.update...
}
is SessionStatus.NotAuthenticated -> {
println("Logged out!")
_state.update...
}
}
}
}
}
}
class AppViewModel(private val api: AuthApi): ViewModel(){
...
init{
viewModelScope.launch {
api.sessionStatus().collect { sessionStatus ->
when(sessionStatus){
is SessionStatus.Authenticated -> {
println("Session authenticated")
_state.update...
}
is SessionStatus.RefreshFailure -> {
println("Session refresh failure")
_state.update...
}
is SessionStatus.Initializing -> {
println("Session initializing")
_state.update...
}
is SessionStatus.NotAuthenticated -> {
println("Logged out!")
_state.update...
}
}
}
}
}
}
And these are the logs what you will get when you put the app into offline mode and switch device time ex +1h - to be sure that the session was expired. Put the app 3 times into BG...You will get 3 different flows...if I would put the app 10 times into BG and coming back to foreground (while still offline), then there will be 10 different flows...
// App coming from the BG
2025-09-29 18:38:35.982 Session initializing
2025-09-29 18:40:13.486 Session failure
2025-09-29 18:40:23.496 Session failure
2025-09-29 18:40:33.514 Session failure
// App going to the BG
- emissions ongoing
// App coming from the BG
2025-09-29 18:42:43.486 Session failure
2025-09-29 18:42:46.010 Session failure
2025-09-29 18:42:53.912 Session failure
2025-09-29 18:42:56.022 Session failure
2025-09-29 18:43:03.939 Session failure
2025-09-29 18:43:06.031 Session failure
// App going to the BG
- emissions ongoing
// App coming from the BG
2025-09-29 18:49:55.105 Session failure
2025-09-29 18:49:57.108 Session failure
2025-09-29 18:49:58.700 Session failure
2025-09-29 18:50:05.138 Session failure
2025-09-29 18:50:07.140 Session failure
2025-09-29 18:50:08.732 Session failure
// App coming from the BG
2025-09-29 18:38:35.982 Session initializing
2025-09-29 18:40:13.486 Session failure
2025-09-29 18:40:23.496 Session failure
2025-09-29 18:40:33.514 Session failure
// App going to the BG
- emissions ongoing
// App coming from the BG
2025-09-29 18:42:43.486 Session failure
2025-09-29 18:42:46.010 Session failure
2025-09-29 18:42:53.912 Session failure
2025-09-29 18:42:56.022 Session failure
2025-09-29 18:43:03.939 Session failure
2025-09-29 18:43:06.031 Session failure
// App going to the BG
- emissions ongoing
// App coming from the BG
2025-09-29 18:49:55.105 Session failure
2025-09-29 18:49:57.108 Session failure
2025-09-29 18:49:58.700 Session failure
2025-09-29 18:50:05.138 Session failure
2025-09-29 18:50:07.140 Session failure
2025-09-29 18:50:08.732 Session failure
Now I switch the app back online and leave it in the BG. Emissions are still ongoing until some certain point when they stopped. They will stop only if the device is back online.
// App going to the BG
2025-09-29 18:58:46.658 Session failure
2025-09-29 18:58:48.665 Session failure
2025-09-29 18:58:56.678 Session failure
2025-09-29 18:58:58.701 Session failure
2025-09-29 18:58:59.966 Session failure
- emissions ongoing until certain point (not sure what is the mechanism to stop these - but it seems it works only if the app is online)
// App coming from the BG
2025-09-29 19:04:31.832 Session authenticated
2025-09-29 19:04:31.869 Session authenticated
- emissions stopped
// App going to the BG
2025-09-29 18:58:46.658 Session failure
2025-09-29 18:58:48.665 Session failure
2025-09-29 18:58:56.678 Session failure
2025-09-29 18:58:58.701 Session failure
2025-09-29 18:58:59.966 Session failure
- emissions ongoing until certain point (not sure what is the mechanism to stop these - but it seems it works only if the app is online)
// App coming from the BG
2025-09-29 19:04:31.832 Session authenticated
2025-09-29 19:04:31.869 Session authenticated
- emissions stopped
What I would expect is that if the app is offline and in the BG, there shouldn't be any periodical updates whatsoever as we can't reach the network anyway...
TheRealJan
TheRealJan4w ago
1. Can you give more details about your device and app 2. Can you enable debug logs in the supabase client builder and check them 3. It should not be possible that multiple session jobs start unless you somehow have multiple clients 4. The session job gets cancelled on android devices when going into BG Hard to say anything with these logs
civomt
civomtOP4w ago
Here are the logs from the supabase itself:
// App starts
2025-09-28 12:57:34.137 12327-12327 Supabase-Core SupabaseClient created! Please report any bugs you find.
2025-09-28 12:57:34.553 12327-12327 Supabase-Auth Initializing Auth plugin...
2025-09-28 12:57:34.554 12327-12527 Supabase-Auth Loading session from storage...
2025-09-28 12:57:34.554 12327-12327 Supabase-Auth nitialized Auth plugin
2025-09-28 12:57:34.554 12327-12527 Supabase-Auth No session found in storage.
2025-09-28 12:57:34.554 12327-12527 Supabase-Auth Setting session status to NotAuthenticated(isSignOut=false)
2025-09-28 12:57:34.801 12327-12327 Supabase-Auth Trying to re-load session from storage...
2025-09-28 12:57:34.801 12327-12527 Supabase-Auth No session found, not starting auto refresh
// User login
2025-09-28 13:02:59.634 12327-12327 Supabase-Auth Importing session UserSession(accessToken=eyJ...)
2025-09-28 13:02:59.642 12327-12327 Supabase-Auth Session saved to storage (auto refresh enabled)
2025-09-28 13:02:59.643 12327-12327 Supabase-Auth Setting session status to Authenticated(…)
2025-09-28 13:02:59.643 12327-12327 Supabase-Auth Session imported successfully. Starting auto refresh...
2025-09-28 13:02:59.644 12327-12327 Supabase-Auth Auto refresh started.
2025-09-28 13:02:59.644 12327-12527 Supabase-Auth Refreshing session in 47m 59.988586s.
// Internet connection off
// App going to the BG
2025-09-28 13:07:08.166 12327-12327 Supabase-Auth Cancelling auto refresh because app is switching to the background
2025-09-28 13:07:08.167 12327-12527 Supabase-Auth Stopping auto refresh for current session
2025-09-28 13:07:08.168 12327-12527 Supabase-Auth Setting session status to Initializing
// App starts
2025-09-28 12:57:34.137 12327-12327 Supabase-Core SupabaseClient created! Please report any bugs you find.
2025-09-28 12:57:34.553 12327-12327 Supabase-Auth Initializing Auth plugin...
2025-09-28 12:57:34.554 12327-12527 Supabase-Auth Loading session from storage...
2025-09-28 12:57:34.554 12327-12327 Supabase-Auth nitialized Auth plugin
2025-09-28 12:57:34.554 12327-12527 Supabase-Auth No session found in storage.
2025-09-28 12:57:34.554 12327-12527 Supabase-Auth Setting session status to NotAuthenticated(isSignOut=false)
2025-09-28 12:57:34.801 12327-12327 Supabase-Auth Trying to re-load session from storage...
2025-09-28 12:57:34.801 12327-12527 Supabase-Auth No session found, not starting auto refresh
// User login
2025-09-28 13:02:59.634 12327-12327 Supabase-Auth Importing session UserSession(accessToken=eyJ...)
2025-09-28 13:02:59.642 12327-12327 Supabase-Auth Session saved to storage (auto refresh enabled)
2025-09-28 13:02:59.643 12327-12327 Supabase-Auth Setting session status to Authenticated(…)
2025-09-28 13:02:59.643 12327-12327 Supabase-Auth Session imported successfully. Starting auto refresh...
2025-09-28 13:02:59.644 12327-12327 Supabase-Auth Auto refresh started.
2025-09-28 13:02:59.644 12327-12527 Supabase-Auth Refreshing session in 47m 59.988586s.
// Internet connection off
// App going to the BG
2025-09-28 13:07:08.166 12327-12327 Supabase-Auth Cancelling auto refresh because app is switching to the background
2025-09-28 13:07:08.167 12327-12527 Supabase-Auth Stopping auto refresh for current session
2025-09-28 13:07:08.168 12327-12527 Supabase-Auth Setting session status to Initializing

// Time changed +1day
// App coming from the BG
2025-09-29 13:09:03.775 12327-12327 Supabase-Auth trying to re-load session from storage...
2025-09-29 13:09:03.804 12327-12526 Supabase-Auth Importing session UserSession(accessToken=eyJ…)
2025-09-29 13:09:03.804 12327-12526 Supabase-Auth Session is under the threshold date. Refreshing session...
2025-09-29 13:09:03.804 12327-12526 Supabase-Auth Session expired. Refreshing session...
2025-09-29 13:09:03.805 12327-12526 Supabase-Auth Refreshing session
2025-09-29 13:09:03.844 12327-12527 Supabase-Core POST request to endpoint /auth/v1/token failed with exception Unable to resolve host..
2025-09-29 13:09:03.844 12327-12527 Supabase-Auth Couldn't reach Supabase. Either the address doesn't exist or the network might not be on. Retrying in 10s...
2025-09-29 13:09:03.846 12327-12527 Supabase-Auth Session expired while trying to refresh the session. Updating status...
2025-09-29 13:09:03.846 12327-12527 Supabase-Auth Setting session status to RefreshFailure(cause=NetworkError..)
2025-09-29 13:09:03.846 12327-12527 Supabase-Auth Emitting event RefreshFailure(cause=NetworkError(…)


// Time changed +1day
// App coming from the BG
2025-09-29 13:09:03.775 12327-12327 Supabase-Auth trying to re-load session from storage...
2025-09-29 13:09:03.804 12327-12526 Supabase-Auth Importing session UserSession(accessToken=eyJ…)
2025-09-29 13:09:03.804 12327-12526 Supabase-Auth Session is under the threshold date. Refreshing session...
2025-09-29 13:09:03.804 12327-12526 Supabase-Auth Session expired. Refreshing session...
2025-09-29 13:09:03.805 12327-12526 Supabase-Auth Refreshing session
2025-09-29 13:09:03.844 12327-12527 Supabase-Core POST request to endpoint /auth/v1/token failed with exception Unable to resolve host..
2025-09-29 13:09:03.844 12327-12527 Supabase-Auth Couldn't reach Supabase. Either the address doesn't exist or the network might not be on. Retrying in 10s...
2025-09-29 13:09:03.846 12327-12527 Supabase-Auth Session expired while trying to refresh the session. Updating status...
2025-09-29 13:09:03.846 12327-12527 Supabase-Auth Setting session status to RefreshFailure(cause=NetworkError..)
2025-09-29 13:09:03.846 12327-12527 Supabase-Auth Emitting event RefreshFailure(cause=NetworkError(…)

// App going to the BG
- emissions ongoing
// App comming from the BG
2025-09-29 13:17:39.188 12327-12327 Supabase-Auth Trying to re-load session from storage...
2025-09-29 13:17:39.192 12327-16674 Supabase-Auth Importing session UserSession(accessToken=eyJ…)
2025-09-29 13:17:39.193 12327-16674 Supabase-Auth Session is under the threshold date. Refreshing session...
2025-09-29 13:17:39.193 12327-16674 Supabase-Auth Session expired. Refreshing session...
2025-09-29 13:17:39.193 12327-16674 Supabase-Auth Refreshing session
2025-09-29 13:17:39.209 12327-16675 Supabase-Core POST request to endpoint /auth/v1/token failed with exception Unable to resolve host..
2025-09-29 13:17:39.209 12327-12527 Supabase-Auth Couldn't reach Supabase. Either the address doesn't exist or the network might not be on. Retrying in 10s...
2025-09-29 13:17:39.209 12327-16675 Supabase-Auth Session expired while trying to refresh the session. Updating status...
2025-09-29 13:17:39.209 12327-16675 Supabase-Auth Setting session status to RefreshFailure(cause=NetworkError..)
2025-09-29 13:17:39.209 12327-16675 Supabase-Auth Emitting event RefreshFailure(cause=NetworkError(…)
// App going to the BG
- emissions ongoing
// App comming from the BG
2025-09-29 13:17:39.188 12327-12327 Supabase-Auth Trying to re-load session from storage...
2025-09-29 13:17:39.192 12327-16674 Supabase-Auth Importing session UserSession(accessToken=eyJ…)
2025-09-29 13:17:39.193 12327-16674 Supabase-Auth Session is under the threshold date. Refreshing session...
2025-09-29 13:17:39.193 12327-16674 Supabase-Auth Session expired. Refreshing session...
2025-09-29 13:17:39.193 12327-16674 Supabase-Auth Refreshing session
2025-09-29 13:17:39.209 12327-16675 Supabase-Core POST request to endpoint /auth/v1/token failed with exception Unable to resolve host..
2025-09-29 13:17:39.209 12327-12527 Supabase-Auth Couldn't reach Supabase. Either the address doesn't exist or the network might not be on. Retrying in 10s...
2025-09-29 13:17:39.209 12327-16675 Supabase-Auth Session expired while trying to refresh the session. Updating status...
2025-09-29 13:17:39.209 12327-16675 Supabase-Auth Setting session status to RefreshFailure(cause=NetworkError..)
2025-09-29 13:17:39.209 12327-16675 Supabase-Auth Emitting event RefreshFailure(cause=NetworkError(…)
2025-09-29 13:17:46.984 12327-16674 Supabase-Auth Importing session UserSession(accessToken=eyJ…)
2025-09-29 13:17:46.985 12327-16674 Supabase-Auth Session is under the threshold date. Refreshing session...
2025-09-29 13:17:46.985 12327-16674 Supabase-Auth Session expired. Refreshing session...
2025-09-29 13:17:46.985 12327-16674 Supabase-Auth Refreshing session
2025-09-29 13:17:47.018 12327-16674 Supabase-Core POST request to endpoint /auth/v1/token failed with exception Unable to resolve host..
2025-09-29 13:17:47.020 12327-12527 Supabase-Auth Couldn't reach Supabase. Either the address doesn't exist or the network might not be on. Retrying in 10s...
2025-09-29 13:17:47.020 12327-16674 Supabase-Auth Session expired while trying to refresh the session. Updating status...
2025-09-29 13:17:47.020 12327-16674 Supabase-Auth Setting session status to RefreshFailure(cause=NetworkError..)
2025-09-29 13:17:47.022 12327-16674 Supabase-Auth Emitting event RefreshFailure(cause=NetworkError(…)
- emissions ongoing
2025-09-29 13:17:46.984 12327-16674 Supabase-Auth Importing session UserSession(accessToken=eyJ…)
2025-09-29 13:17:46.985 12327-16674 Supabase-Auth Session is under the threshold date. Refreshing session...
2025-09-29 13:17:46.985 12327-16674 Supabase-Auth Session expired. Refreshing session...
2025-09-29 13:17:46.985 12327-16674 Supabase-Auth Refreshing session
2025-09-29 13:17:47.018 12327-16674 Supabase-Core POST request to endpoint /auth/v1/token failed with exception Unable to resolve host..
2025-09-29 13:17:47.020 12327-12527 Supabase-Auth Couldn't reach Supabase. Either the address doesn't exist or the network might not be on. Retrying in 10s...
2025-09-29 13:17:47.020 12327-16674 Supabase-Auth Session expired while trying to refresh the session. Updating status...
2025-09-29 13:17:47.020 12327-16674 Supabase-Auth Setting session status to RefreshFailure(cause=NetworkError..)
2025-09-29 13:17:47.022 12327-16674 Supabase-Auth Emitting event RefreshFailure(cause=NetworkError(…)
- emissions ongoing
Current behavior: - If the session expire while the app is offline, the sdk will try to refresh the token as long as the app is alive (doesn't matter if in the foreground or in the background). If the app is open multiple times from the BG, multiple concurrent flows will launch. Expected behavior(from my perspective): - If the session is expired while the app is offline don't try to refresh the token as we cannot reach the cloud anyway. If the app is in the BG, don't run the refresh flow either. If the app comes from the BG to the FG - don't start new concurrent flow. Possible issue: Auto-refresh is not cancelled when app is offline or in the BG. How to replicate: - Login - Put device offline - Wait 1h or your expiration time or change time of the device into future (app can be in the foreground or background doesn't matter) - the refreshing process is ongoing all the time - Open the app - refreshing process starts - Put app into BG - Open the app - another refreshing process starts Replicated on: - Samsung Galaxy S22 Ultra Android 15 (Real device) - Pixel Android 16 (Emulator) Example implementation:
// Supabase abs
sealed interface AuthApi{
fun sessionStatus(): Flow<SessionStatus>
}

// Supabase Impl
class SupabaseAuthApi(private val supabaseClient: SupabaseClient): AuthApi {
override fun sessionStatus(): Flow<SessionStatus>{
return supabaseClient.auth.sessionStatus
}
}

// ViewModel
class AppViewModel(private val authApi: AuthApi): ViewModel(){
init{
viewModelScope.launch {
api.sessionStatus().collect { sessionStatus ->
when(sessionStatus){
is SessionStatus.Authenticated -> {
println("Session authenticated")
_state.update...
}
is SessionStatus.RefreshFailure -> {
println("Session refresh failure")
_state.update...
}
is SessionStatus.Initializing -> {
println("Session initializing")
_state.update...
}
is SessionStatus.NotAuthenticated -> {
println("Logged out!")
_state.update...
}
}
}
}
}
}
// Modules
val coreModule = module {
single {
createSupabaseClient(
supabaseUrl = ...
supabaseKey = ...
){
defaultLogLevel = LogLevel.DEBUG
install(Auth)
install(Postgrest)
}
}
}

val appModule = module {
viewModelOf(::AppViewModel)
}

// Koin init
startKoin {
modules (
appModule,
coreModule
)
}
// Supabase abs
sealed interface AuthApi{
fun sessionStatus(): Flow<SessionStatus>
}

// Supabase Impl
class SupabaseAuthApi(private val supabaseClient: SupabaseClient): AuthApi {
override fun sessionStatus(): Flow<SessionStatus>{
return supabaseClient.auth.sessionStatus
}
}

// ViewModel
class AppViewModel(private val authApi: AuthApi): ViewModel(){
init{
viewModelScope.launch {
api.sessionStatus().collect { sessionStatus ->
when(sessionStatus){
is SessionStatus.Authenticated -> {
println("Session authenticated")
_state.update...
}
is SessionStatus.RefreshFailure -> {
println("Session refresh failure")
_state.update...
}
is SessionStatus.Initializing -> {
println("Session initializing")
_state.update...
}
is SessionStatus.NotAuthenticated -> {
println("Logged out!")
_state.update...
}
}
}
}
}
}
// Modules
val coreModule = module {
single {
createSupabaseClient(
supabaseUrl = ...
supabaseKey = ...
){
defaultLogLevel = LogLevel.DEBUG
install(Auth)
install(Postgrest)
}
}
}

val appModule = module {
viewModelOf(::AppViewModel)
}

// Koin init
startKoin {
modules (
appModule,
coreModule
)
}
Any ideas? @TheRealJan Supabase logs just confirmed the same behavior as I observed
TheRealJan
TheRealJan4w ago
Alright thanks, maybe its a bug happening when the client already tries to retry refresh the session before going into BG and fails, maybe something breaks the cancelling there, because it normally cancels the refresh job as you can see in the logs. Will look at it.
civomt
civomtOP4w ago
Okay, so are contributing to the supbase-kt library?
TheRealJan
TheRealJan4w ago
You could say that I'm the creator/maintainer Can you create an issue with this bug?
civomt
civomtOP4w ago
Nice! Let me know if you were able to replicate the issue or if you need more logs / testing. I can do it. Do you want me to do it via github or is there any special procedure which needs to be followed here or on the supabase page?
TheRealJan
TheRealJan4w ago
Yea just on the supabase kt github so I don't forget it
civomt
civomtOP4w ago
issue created

Did you find this page helpful?