Skip to content

Auto-refreshing of tokens can fail and never restart again #630

@vojtabohm

Description

@vojtabohm

Bug report

  • I confirm this is a bug with Supabase, not with my own application.
  • I confirm I have searched the Docs, GitHub Discussions, and Discord.

Describe the bug

When the refresh of a token fails (e.g. the app went into background or the refresh API call fails for plethora of reasons) the library silently fails and never refreshes the token again.

_ = try? await refreshSession(refreshedSession.refreshToken) (in the scheduleNextTokenRefresh function).

To Reproduce

Set your phone into airplane mode, wait for refresh to fail. It will never refresh again since scheduleNextTokenRefresh is called recursively from refreshSession that can fail, it will be stopped.

If you follow the docs recommendation

It's best practice and highly recommended to extract the access token (JWT) and store it in memory for further use in your application.

basically something like this:

func authToken() async throws -> String {
        if let token = _authToken {
            return token
        }
        
        return try await supabase.auth.session.accessToken
    }
... inside a task ...

for await (event, session) in supabase.auth.authStateChanges {
                print("Auth change:", event, session)
                switch event {
                case .initialSession, .signedIn, .tokenRefreshed:
                    // Store _authToken in memory, as per Supabase's docs recommendation
                    _authToken = session?.accessToken

... some other code ...

and you rely on authStateChanges to always give you a token, then you can run into issue of token failing to refresh and you're forever stuck with the old one.

Expected behavior

It would be great to incorporate some sort of retry mechanism. I worked around this with storing the entire session, not just the token and manually refreshing if it's expired.

func authToken() async throws -> String {
        if let session = _authSession, !session.isExpired {
            return session.accessToken
        } else {
            let newSession = try await supabase.auth.session
            _authSession = newSession
            
            return newSession.accessToken
        }
    }

I think this is a good work-around so if a retry mechanism is too complicated (which is understandable), perhaps update of the docs could be in order.

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugSomething isn't working

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions