OAuthTokenManager#

OAuthTokenManager handles the OAuth lifecycle after the HAAPI flow reaches OAuthAuthorizationResponseStep: fetching the first access token, refreshing it before it expires, and revoking the refresh token on logout. Obtain an instance from HaapiAccessor (created via the SDK Layer builders) and call it from the same authentication context that ran the flow.

Fetching the Access Token#

When the HAAPI flow completes, exchange the authorization code for a TokenResponse. Success cases carry an access token, refresh token, and expiry; error cases surface as ErrorTokenResponse.

let code = authResponseStep.oauthAuthorizationResponseProperties.code!

oAuthTokenManager.fetchAccessToken(with: code) { [weak self] tokenResponse in
    self?.handleTokenResponse(tokenResponse)
}

private func handleTokenResponse(_ tokenResponse: TokenResponse) {
    switch tokenResponse {
    case let .successfulToken(success):
        let accessToken = success.accessToken
        let refreshToken = success.refreshToken
        // persist tokens to secure storage
    case let .errorToken(errorTokenResponse):
        // handle OAuth-protocol error
        break
    case let .error(anError):
        // handle framework or transport error
        break
    }
}

Store accessToken and refreshToken in secure storage — Keychain on iOS, EncryptedSharedPreferences or the Keystore on Android.

Successful Token Response Fields#

The success branch carries six fields. Only accessToken and expiresIn are guaranteed non-null — the rest depend on what the server returned for the configured scopes and grant.

FieldiOS (SuccessfulTokenResponse)Android (SuccessfulTokenResponse)React Native (SuccessfulTokenResponse)Notes
accessTokenStringStringstringRequired. The OAuth access token to attach to authenticated requests.
expiresInIntIntnumberRequired. Lifetime of accessToken in seconds, counted from token issuance.
refreshTokenString?String?string?Present when the client is configured for a refresh-capable grant.
tokenTypeString?String?string?Typically "DPoP" when binding is enabled, "Bearer" otherwise.
scopeString?String?string?Space-delimited list of scopes the server actually granted. May differ from what was requested.
idTokenString?String?string?Present when openid is in the granted scopes.

UI-Layer integrations receive the same six fields under different type names — OAuthTokenModel on iOS, OauthModel.Token on Android — delivered through the flow-result handler rather than through OAuthTokenManager.fetchAccessToken. The fields and their semantics are identical.

Refreshing the Access Token#

When the access token expires, present the refresh token to obtain a fresh pair:

oAuthTokenManager.refreshAccessToken(with: "refresh_token") { [weak self] tokenResponse in
    self?.handleTokenResponse(tokenResponse)
}

On invalid_grant, the SDK automatically clears any stored DPoP key pair so the next flow starts cleanly. The recommended UX response is to discard the held tokens and prompt the user to re-authenticate.

Revoking the Refresh Token#

To log the user out cleanly, revoke the refresh token. The SDK clears any associated DPoP key pair as part of revocation:

oAuthTokenManager.revokeRefreshToken("refresh_token") { result in
    // handle success or error
}

The DPoP-proof requirement when token binding is enabled and the raw-response listener pattern each get their own page:

Was this helpful?