5

I'm having basically the same problem as the user asking this question: AppAuth library for Android lacks proper documentation.

My problem occurs when the current access token expires, disallowing API communication from inside my app. To prevent that I forced my TokenInterceptor to acquire the token on each request from the getAccessToken method, which uses AppAuth's performActionWithFreshTokens method, that supposedly performs a token refresh request (I looked through its code). However, it always throws an AuthorizationException with error invalid_grant for me.

It crashes my app the first time, but works fine after relaunching. So the token does refresh, doesn't it?

class TokenInterceptor @Inject constructor(
        private val authStateStorage: AuthStateStorage,
        private val authService: AuthorizationService
): Interceptor {
    private companion object {
        const val TAG = "TokenInterceptor"
        const val AUTH_HEADER = "Authorization"
    }

    override fun intercept(chain: Interceptor.Chain): Response {
        var request = chain.request()
        request.header(AUTH_HEADER) ?: run {
            request = chain.request()
                    .newBuilder()
                    .addHeader(AUTH_HEADER, "Bearer ${getAccessToken()}")
                    .build()
        }
        return chain.proceed(request)
    }

    private fun getAccessToken(): String = runBlocking {
        val authState = authStateStorage.authStateFlow.first()
        val isNeedToUpdateToken = authState.needsTokenRefresh

        // authState.refreshToken is not null or empty for me!

        suspendCoroutine { continuation ->
            authState.performActionWithFreshTokens(authService) { accessToken, _, exception ->
                exception?.let {
                    Log.e(TAG, "Exception in token process: ", it)
                    continuation.resumeWithException(it)
                } ?: run {
                    if (isNeedToUpdateToken) {
                        runBlocking {
                            authStateStorage.updateAuthState(authState)
                        }
                    }
                    continuation.resume(accessToken!!)
                }
            }
        }
    }
}

Did I forget some steps for this to work properly? Why does it throw an exception, but I still wind up with a valid token?

Calamity
  • 700
  • 7
  • 23

1 Answers1

1

invalid_grant typically occurs in a refresh token grant message when the refresh token is expired. First check that your Authorization Server is actually returning a refresh token to the app and that its expiry is configured as greater than that of the access token.

Personally I like to be in control of my own API calls rather than doing them all via AppAuth, so I use the performTokenRequest method directly. Here is some example code you might find useful to compare against:

For best troubleshooting I'd recommend also tracing the HTTP requests to see the refresh token grant message, which should look like step 15 of my blog post. There is a working sample there also, in case useful.

Gary Archer
  • 22,534
  • 2
  • 12
  • 24