CtrlK
BlogDocsLog inGet started
Tessl Logo

tessl/maven-io-ktor--ktor-client-auth-js

Ktor client authentication and authorization plugin for JavaScript platforms supporting Basic, Digest, and Bearer token authentication with automatic token refresh

Pending
Overview
Eval results
Files

bearer-authentication.mddocs/

Bearer Authentication

Bearer token authentication provider with support for automatic token refresh, access/refresh token pairs, circuit breaker functionality, and configurable token loading and refresh mechanisms for OAuth flows and API token authentication.

Capabilities

Bearer Authentication Provider Installation

Install the Bearer authentication provider with configuration for token management.

/**
 * Installs the client's BearerAuthProvider
 * @param block Configuration block for Bearer authentication
 */
fun AuthConfig.bearer(block: BearerAuthConfig.() -> Unit)

Usage Example:

import io.ktor.client.*
import io.ktor.client.plugins.auth.*
import io.ktor.client.plugins.auth.providers.*

val client = HttpClient {
    Auth {
        bearer {
            loadTokens {
                BearerTokens("access_token", "refresh_token")
            }
            refreshTokens { params ->
                // Refresh token logic
                refreshAccessToken(params.oldTokens?.refreshToken)
            }
        }
    }
}

Bearer Authentication Configuration

Configuration class for BearerAuthProvider with token loading, refreshing, and sending behavior control.

/**
 * Configuration for BearerAuthProvider
 */
class BearerAuthConfig {
    /**
     * Optional realm specification for Bearer authentication
     */
    var realm: String?
    
    /**
     * Configures a callback that refreshes a token when the 401 status code is received
     * @param block Suspend function that handles token refresh with refresh parameters
     */
    fun refreshTokens(block: suspend RefreshTokensParams.() -> BearerTokens?)
    
    /**
     * Configures a callback that loads a cached token from a local storage
     * Note: Using the same client instance here to make a request will result in a deadlock
     * @param block Suspend function that returns cached tokens or null
     */
    fun loadTokens(block: suspend () -> BearerTokens?)
    
    /**
     * Configures when to send credentials without waiting for HttpStatusCode.Unauthorized
     * @param block Function that determines based on request whether to send credentials proactively
     */
    fun sendWithoutRequest(block: (HttpRequestBuilder) -> Boolean)
}

Usage Examples:

bearer {
    // Load tokens from storage
    loadTokens {
        loadTokensFromSecureStorage()
    }
    
    // Refresh token logic
    refreshTokens { params ->
        val oldRefreshToken = params.oldTokens?.refreshToken
        if (oldRefreshToken != null) {
            // Use a different client instance to avoid deadlock
            val refreshClient = HttpClient()
            
            // Mark as refresh request to prevent infinite loops
            val response = refreshClient.post("/oauth/refresh") {
                markAsRefreshTokenRequest()
                setBody(RefreshTokenRequest(oldRefreshToken))
            }
            
            if (response.status.isSuccess()) {
                val tokenResponse = response.body<TokenResponse>()
                BearerTokens(tokenResponse.accessToken, tokenResponse.refreshToken)
            } else null
        } else null
    }
    
    // Send tokens proactively for API calls
    sendWithoutRequest { request ->
        request.url.host == "api.example.com"
    }
    
    realm = "api"
}

Bearer Tokens

Container class for Bearer authentication tokens supporting access/refresh token pairs.

/**
 * Container for bearer tokens
 * @param accessToken The access token used for authentication
 * @param refreshToken Optional refresh token used to obtain new access tokens
 */
class BearerTokens(
    val accessToken: String,
    val refreshToken: String?
)

Refresh Token Parameters

Parameters passed to the token refresh callback providing context for the refresh operation.

/**
 * Parameters passed to refreshTokens lambda
 * @param client HttpClient instance for making refresh requests
 * @param response HttpResponse that triggered the token refresh
 * @param oldTokens Previously cached tokens, may be null
 */
class RefreshTokensParams(
    val client: HttpClient,
    val response: HttpResponse,
    val oldTokens: BearerTokens?
) {
    /**
     * Marks that this request is for refreshing auth tokens, resulting in special handling
     * This prevents the request from triggering additional authentication flows
     */
    fun HttpRequestBuilder.markAsRefreshTokenRequest()
}

Usage Example:

refreshTokens { params ->
    val refreshToken = params.oldTokens?.refreshToken
    if (refreshToken != null) {
        try {
            // Create separate client for refresh to avoid deadlock
            val refreshClient = HttpClient()
            
            val response = refreshClient.post("/auth/refresh") {
                // Important: mark as refresh request
                markAsRefreshTokenRequest()
                
                header("Authorization", "Bearer $refreshToken")
                contentType(ContentType.Application.Json)
                setBody(mapOf("refresh_token" to refreshToken))
            }
            
            if (response.status.isSuccess()) {
                val newTokens = response.body<TokenResponse>()
                BearerTokens(newTokens.access_token, newTokens.refresh_token)
            } else {
                null // Refresh failed
            }
        } catch (e: Exception) {
            null // Handle refresh errors
        }
    } else {
        null // No refresh token available
    }
}

Bearer Authentication Provider

Authentication provider implementation for the Bearer HTTP authentication scheme supporting OAuth flows and API token authentication.

/**
 * Authentication provider for the Bearer HTTP authentication scheme
 * Bearer authentication involves security tokens called bearer tokens
 * These tokens can be used as part of OAuth flow to authorize users of your application
 * by using external providers, such as Google, Facebook, Twitter, and so on
 */
class BearerAuthProvider(
    private val refreshTokens: suspend RefreshTokensParams.() -> BearerTokens?,
    loadTokens: suspend () -> BearerTokens?,
    private val sendWithoutRequestCallback: (HttpRequestBuilder) -> Boolean = { true },
    private val realm: String?
) : AuthProvider {
    
    // Note: clearToken() method is available but marked as @InternalAPI
    
    override fun sendWithoutRequest(request: HttpRequestBuilder): Boolean
    override fun isApplicable(auth: HttpAuthHeader): Boolean
    override suspend fun addRequestHeaders(request: HttpRequestBuilder, authHeader: HttpAuthHeader?)
    override suspend fun refreshToken(response: HttpResponse): Boolean
}

Usage Examples:

import io.ktor.client.*
import io.ktor.client.plugins.auth.*
import io.ktor.client.plugins.auth.providers.*

// Simple bearer token setup
val client = HttpClient {
    Auth {
        bearer {
            loadTokens {
                BearerTokens("my-access-token", null)
            }
        }
    }
}

// OAuth flow with refresh tokens
val oauthClient = HttpClient {
    Auth {
        bearer {
            loadTokens {
                // Load from secure storage
                val tokens = secureStorage.getTokens()
                if (tokens != null) {
                    BearerTokens(tokens.accessToken, tokens.refreshToken)
                } else null
            }
            
            refreshTokens { params ->
                val refreshToken = params.oldTokens?.refreshToken
                if (refreshToken != null) {
                    val refreshClient = HttpClient()
                    
                    val response = refreshClient.post("https://oauth.provider.com/token") {
                        markAsRefreshTokenRequest()
                        parameter("grant_type", "refresh_token")
                        parameter("refresh_token", refreshToken)
                        parameter("client_id", "my-client-id")
                    }
                    
                    if (response.status.isSuccess()) {
                        val tokenData = response.body<OAuthTokenResponse>()
                        // Cache new tokens
                        secureStorage.saveTokens(tokenData)
                        BearerTokens(tokenData.access_token, tokenData.refresh_token)
                    } else null
                } else null
            }
            
            sendWithoutRequest { request ->
                // Always send for API requests
                request.url.host.contains("api.")
            }
        }
    }
}

// Note: clearToken() is available as internal API for clearing cached tokens

Authentication Flow

  1. Token Loading: loadTokens() is called to retrieve cached tokens
  2. Proactive Sending: If sendWithoutRequest returns true, Bearer header is added immediately
  3. 401 Response: When server responds with 401, provider checks isApplicable() for Bearer scheme
  4. Realm Matching: Provider validates realm from WWW-Authenticate header if specified
  5. Token Refresh: refreshTokens() is called with refresh parameters
  6. Cache Update: New tokens are cached automatically after successful refresh
  7. Retry Request: Request is retried with new Bearer token
  8. Circuit Breaker: Refresh requests marked with markAsRefreshTokenRequest() skip authentication

Install with Tessl CLI

npx tessl i tessl/maven-io-ktor--ktor-client-auth-js

docs

basic-authentication.md

bearer-authentication.md

digest-authentication.md

index.md

plugin-configuration.md

tile.json