Hey I am struggling with implementing custom Auth0...
# apollo-kotlin
i
Hey I am struggling with implementing custom Auth0 interceptor that retries requests with new access token (Apollo
3.x
). The issue I am heaving is that code inside
.flatMapConcat
is only executed if graphQL returns authenticated response. With this
UNAUTHENTICATED
response the code inside
flatMapConcat
never runs:
Copy code
/**
   * Check if the GraphQL response indicates an authentication error (HTTP status 200).
   *
   * Sample unauthenticated response:
   * {
   *   "errors": [
   *     {
   *       "message": "User not authorized. Invalid token.",
   *       "locations": [
   *         {
   *           "line": 1,
   *           "column": 14
   *         }
   *       ],
   *       "path": [
   *         "me"
   *       ],
   *       "extensions": {
   *         "code": "UNAUTHENTICATED"
   *       }
   *     }
   *   ],
   *   "data": {
   *     "me": null
   *   }
   * }
   */
So: • authenticate response logs
AuthApolloInterceptor intercept
and
AuthApolloInterceptor flatMapConcat
• unauthenticated response only logs
AuthApolloInterceptor intercept
Here is my Custom interceptor:
Copy code
class AuthApolloInterceptorImpl(
  private val getCredentialsUseCase: GetCredentialsUseCase,
  private val logoutUserUseCase: LogoutUserUseCase,
) : ApolloInterceptor {
  override fun <D : Operation.Data> intercept(
    request: ApolloRequest<D>,
    chain: ApolloInterceptorChain,
  ): Flow<ApolloResponse<D>> {
    Timber.d("AuthApolloInterceptor intercept")

    return chain
      .proceed(request)
      .flatMapConcat { response ->
        Timber.d("AuthApolloInterceptor flatMapConcat")
        if (isAuthenticationError(response)) {
          handleAuthenticationError(request, chain)
        } else {
          flowOf(response)
        }
      }
  }


  private fun isAuthenticationError(response: ApolloResponse<*>): Boolean {
    return response
      .errors
      ?.any { error ->
        error
          .extensions
          ?.get(RESPONSE_CODE_KEY) == RESPONSE_UNAUTHENTICATED_CODE
      } ?: false
  }

  private fun <D : Operation.Data> handleAuthenticationError(
    request: ApolloRequest<D>,
    chain: ApolloInterceptorChain,
  ): Flow<ApolloResponse<D>> =
    flow {
      val accessToken = getCredentialsUseCase()
      
      if(authToken != null)
      {
          val newRequest =
            request
              .newBuilder()
              .addHttpHeader(Header.AUTHORIZATION, "Bearer $accessToken")
              .build()

          emitAll(chain.proceed(newRequest))
      }
      else {
          // Emit nothing, effectively canceling the request
        }
      }
    }

  companion object {
    private const val RESPONSE_CODE_KEY = "code"
    private const val RESPONSE_UNAUTHENTICATED_CODE = "UNAUTHENTICATED"
  }
}
Any obvious mistake here? Is this a proper way of retrying requests with new
accessToken
?
s
Don't see why
Timber.d("AuthApolloInterceptor flatMapConcat")
would not run for all kinds of responses. You get a response, and you are just continuing on the map, that is all way before any of the rest of your code runs. Do you have any other interceptor which abruptly throws perhaps which might be breaking the chain somehow?
i
Not sure what happend before, seems to be working now
thx