All Downloads are FREE. Search and download functionalities are using the official Maven repository.

commonMain.com.apollographql.apollo.interceptor.BearerTokenInterceptor.kt Maven / Gradle / Ivy

package com.apollographql.apollo.interceptor

import com.apollographql.apollo.ApolloHttpException
import com.apollographql.apollo.ApolloBearerTokenException
import com.apollographql.apollo.api.ApolloExperimental
import com.apollographql.apollo.api.Operation
import com.apollographql.apollo.network.HttpExecutionContext
import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.FlowPreview
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.catch
import kotlinx.coroutines.flow.flatMapConcat
import kotlinx.coroutines.flow.flow
import kotlinx.coroutines.flow.retry
import kotlinx.coroutines.sync.Mutex
import kotlinx.coroutines.sync.withLock

@ApolloExperimental
class BearerTokenInterceptor(private val tokenProvider: TokenProvider) : ApolloRequestInterceptor {
  private val mutex = Mutex()

  private fun  ApolloRequest.withHeader(name: String, value: String): ApolloRequest {
    val httpRequestContext = (executionContext[HttpExecutionContext.Request]
        ?: HttpExecutionContext.Request(emptyMap()))
        .let {
          it.copy(headers = it.headers + (name to value))
        }

    return ApolloRequest(
        operation = operation,
        scalarTypeAdapters = scalarTypeAdapters,
        executionContext = executionContext + httpRequestContext
    )
  }

  private fun  proceedWithToken(
      request: ApolloRequest,
      interceptorChain: ApolloInterceptorChain,
      token: String
  ): Flow> {
    val newRequest = request.withHeader("Authorization", "Bearer $token")
    return interceptorChain.proceed(newRequest)
  }

  @FlowPreview
  @ExperimentalCoroutinesApi
  override fun  intercept(request: ApolloRequest, chain: ApolloInterceptorChain): Flow> {
    return flow {
      val token = mutex.withLock { tokenProvider.currentToken() }
      emit(token)
    }.flatMapConcat { token ->
      proceedWithToken(request, chain, token).catch { exception ->
        if (exception is ApolloHttpException && exception.statusCode == 401) {
          throw ApolloBearerTokenException(message = "Request failed with status code `401`", cause = exception, token = token)
        } else {
          throw exception
        }
      }
    }.retry(retries = 1) { error ->
      if (error is ApolloBearerTokenException) {
        tokenProvider.refreshToken(error.token)
        true
      } else {
        false
      }
    }
  }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy