Refreshing auth token with AppAuth library on iOS

3.6k Views Asked by At

I'm using the AppAuth library to get an access token for the Gmail API. I've successfully been able to create an Auth Session, and use the retrieved token to later fetch the emails.

In my AppDelegate I have two variables:

var currentAuthorizationFlow: OIDExternalUserAgentSession?
var authState: OIDAuthState?

In my SignInViewController, I have the following code for performing the authorization flow:

@objc func startAuthFlow() {
    
    Analytics.logEvent("auth_started", parameters: nil)
    
    let authorizationEndpoint = URL(string: "https://accounts.google.com/o/oauth2/v2/auth")!
    let tokenEndpoint = URL(string: "https://www.googleapis.com/oauth2/v4/token")!
    let configuration = OIDServiceConfiguration(authorizationEndpoint: authorizationEndpoint,
                                                tokenEndpoint: tokenEndpoint)
    let kRedirectURI: String = "com.googleusercontent.apps.someNumber:/oauthredirect";
    
    guard let redirectURI = URL(string: kRedirectURI) else {
        return
    }
    
    let appDelegate = UIApplication.shared.delegate as! AppDelegate

    // builds authentication request
    let request = OIDAuthorizationRequest(configuration: configuration,
                                          clientId: "myID",
                                          clientSecret: nil,
                                          scopes: ["https://mail.google.com/"],
                                          redirectURL: redirectURI,
                                          responseType: OIDResponseTypeCode,
                                          additionalParameters: nil)
    

    
    // performs authentication request
    print("Initiating authorization request with scope: \(request.scope ?? "nil")")
            
    appDelegate.currentAuthorizationFlow =
        OIDAuthState.authState(byPresenting: request, presenting: self) { authState, error in
                if let authState = authState {

                print("Got authorization tokens. Access token: " +
                        "\(authState.lastTokenResponse?.accessToken ?? "nil")")
               A0SimpleKeychain().setString((authState.lastTokenResponse?.accessToken)!, forKey: "auth0-user-jwt")
                A0SimpleKeychain().setString((authState.lastTokenResponse?.refreshToken)!, forKey: "auth0-user-jwt-refresh")

                EmailFetcher.shared.setupEmailSession(token: (authState.lastTokenResponse?.accessToken)!)
                

            } else {
              
                print("Authorization error: \(error?.localizedDescription ?? "Unknown error")")
            }
        }
}

I then successfully save the token and the refresh token.

I saw that there was a tokenRefreshRequest() method for OIDAuthState, but my understanding is that you would need to pass in the refresh token to get a new, fresh token, correct? What's the missing piece to implementing this with AppAuth?

2

There are 2 best solutions below

2
On

To refresh an access token you use a 'refresh token grant' message with arguments similar to this:

let request = OIDTokenRequest(
    configuration: self.metadata!,
    grantType: OIDGrantTypeRefreshToken,
    authorizationCode: nil,
    redirectURL: nil,
    clientID: self.configuration.clientId,
    clientSecret: nil,
    scope: nil,
    refreshToken: tokenData!.refreshToken!,
    codeVerifier: nil,
    additionalParameters: nil)

OIDAuthorizationService.perform(request) { tokenResponse, error in ...

Note that I'm not using the AuthState class in my sample, since I wanted to store tokens encrypted in the iOS keychain, so your code may be a little different. For something to compare against, you can run the code sample from by blog:

0
On

Create an authState from auth token or directly save authstate model into key chain then access latest refresh token using below methods. authState

authState.performAction { (accessToken, authToken, error) in
        guard let err = error else {return} 
        print("updated refresh token is : \(accessToken)")
     }