Search code examples
iosfirebasefirebase-authenticationoauthgoogle-signin

Firebase Auth with Google as an IdP expires ID token frequently and why?


My iOS app integrates with Firebase Authentication and provides Google sign-in with it. I'm observing that some of our users fail to retrieve the Firebase ID token via getIDToken (with forcingRefresh NO) in Firebase Auth SDK.

Domain: FIRAuthErrorDomain Code: 17021 NSLocalizedDescription: The user's credential is no longer valid. The user must sign in again.

What's interesting is that it (almost)only happens with the user who signs in by Google and it doesn't for users who use email/password or Apple sign-in.

My understanding of the ID token is that, when we get it via Firebase SDK, it is refreshed under the hood with the refresh token if the current ID token is expired (so basically it won't expire forever or for so long until the refresh token expires at least).

I also read this very useful article. According to the article, Third-Party OAuth Access Tokens (in this time case Google OAuth access token?) is not used to authenticate to Firebase so its expiration is most likely NOT the cause of the issue.

Do you have any idea why it is happening especially only in Google sign-in? For example, is it possible that the refresh token for Google sign-in is short-lived?


Solution

  • I have never ever found out the root cause but I finally observed it has been solved by re-fetching the ID token forcing the refresh when it fails due to the expiration reason as follows.

    -    func getIdToken() async throws -> String {
    +    func getIdToken(forcingRefresh: Bool = false) async throws -> String {
             guard let currentUser = Auth.auth().currentUser else {
                 throw AuthenticationServiceError.unauthenticated
             }
             do {
    -            return try await currentUser.getIDToken()
    +            return try await currentUser.getIDTokenResult(forcingRefresh: forcingRefresh).token
             } catch {
    +            // Retry to get ID token forcing refresh to see if it solves the token expiration problem observed many in production.
    +            if let error = error as? AuthErrorCode, error.code == .userTokenExpired && !forcingRefresh {
    +                return try await getIdToken(forcingRefresh: true)
    +            }
                 throw error
             }
        }