[PM-20306] Migrate Auth Token Interceptor (#5065)

This commit is contained in:
Patrick Honkonen
2025-04-16 12:41:43 -04:00
committed by GitHub
parent 3311086dfc
commit 0be26c1eda
11 changed files with 176 additions and 42 deletions

View File

@@ -2,8 +2,10 @@ import org.jetbrains.kotlin.gradle.dsl.JvmTarget
plugins {
alias(libs.plugins.android.library)
alias(libs.plugins.hilt)
alias(libs.plugins.kotlin.android)
alias(libs.plugins.kotlin.serialization)
alias(libs.plugins.ksp)
}
android {
@@ -46,6 +48,8 @@ dependencies {
implementation(project(":core"))
implementation(libs.androidx.core.ktx)
implementation(libs.google.hilt.android)
ksp(libs.google.hilt.compiler)
implementation(libs.kotlinx.serialization)
implementation(libs.square.okhttp)
implementation(libs.square.okhttp.logging)

View File

@@ -0,0 +1,33 @@
package com.bitwarden.network.interceptor
import com.bitwarden.network.util.HEADER_KEY_AUTHORIZATION
import com.bitwarden.network.util.HEADER_VALUE_BEARER_PREFIX
import okhttp3.Interceptor
import okhttp3.Response
import java.io.IOException
import javax.inject.Singleton
/**
* Interceptor responsible for adding the auth token(Bearer) to API requests.
*/
@Singleton
class AuthTokenInterceptor(
private val authTokenProvider: AuthTokenProvider,
) : Interceptor {
private val missingTokenMessage = "Auth token is missing!"
override fun intercept(chain: Interceptor.Chain): Response {
val token = authTokenProvider.getActiveAccessTokenOrNull()
?: throw IOException(IllegalStateException(missingTokenMessage))
val request = chain
.request()
.newBuilder()
.addHeader(
name = HEADER_KEY_AUTHORIZATION,
value = "${HEADER_VALUE_BEARER_PREFIX}$token",
)
.build()
return chain
.proceed(request)
}
}

View File

@@ -0,0 +1,12 @@
package com.bitwarden.network.interceptor
/**
* A provider for all the functionality needed to properly refresh the users access token.
*/
interface AuthTokenProvider {
/**
* The currently active user's access token.
*/
fun getActiveAccessTokenOrNull(): String?
}

View File

@@ -0,0 +1,52 @@
package com.bitwarden.network.interceptor
import io.mockk.every
import io.mockk.mockk
import junit.framework.TestCase.assertEquals
import okhttp3.Request
import org.junit.Assert.assertThrows
import org.junit.Test
import java.io.IOException
import javax.inject.Singleton
@Singleton
class AuthTokenInterceptorTest {
private val mockAuthTokenProvider = mockk<AuthTokenProvider> {
every { getActiveAccessTokenOrNull() } returns null
}
private val interceptor: AuthTokenInterceptor = AuthTokenInterceptor(
authTokenProvider = mockAuthTokenProvider,
)
private val request: Request = Request
.Builder()
.url("http://localhost")
.build()
@Test
fun `intercept should add the auth token when set`() {
every { mockAuthTokenProvider.getActiveAccessTokenOrNull() } returns ACCESS_TOKEN
val response = interceptor.intercept(
chain = FakeInterceptorChain(request = request),
)
assertEquals(
"Bearer $ACCESS_TOKEN",
response.request.header("Authorization"),
)
}
@Test
fun `intercept should throw an exception when an auth token is missing`() {
val throwable = assertThrows(IOException::class.java) {
interceptor.intercept(
chain = FakeInterceptorChain(request = request),
)
}
assertEquals(
"Auth token is missing!",
throwable.cause?.message,
)
}
}
private const val ACCESS_TOKEN: String = "access_token"