igor.wojda
10/24/2024, 12:31 PM3.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:
/**
* 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:
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
?Stylianos Gakis
10/24/2024, 12:47 PMTimber.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?igor.wojda
10/24/2024, 2:52 PMigor.wojda
10/24/2024, 2:52 PM