or run

npx @tessl/cli init
Log in

Version

Tile

Overview

Evals

Files

docs

authentication.mdcontent-multipart.mdcontent-types.mdcookies.mdheaders-parameters.mdhttp-protocol.mdindex.mdurl-handling.md
tile.json

authentication.mddocs/

Authentication

HTTP authentication header parsing and generation supporting multiple authentication schemes including Basic, Bearer, Digest, and custom schemes with flexible parameter handling.

HttpAuthHeader Classes

Base HttpAuthHeader

sealed class HttpAuthHeader(val authScheme: String) {
    abstract fun render(): String
    abstract fun render(encoding: HeaderValueEncoding): String
    override fun toString(): String
}

Single Token Authentication

class HttpAuthHeader.Single(
    authScheme: String,
    val blob: String
) : HttpAuthHeader(authScheme) {
    override fun render(): String
    override fun render(encoding: HeaderValueEncoding): String
}

Used for authentication schemes that use a single token or credential, such as Bearer tokens.

Parameterized Authentication

class HttpAuthHeader.Parameterized(
    authScheme: String,
    val parameters: Map<String, String>
) : HttpAuthHeader(authScheme) {
    override fun render(): String
    override fun render(encoding: HeaderValueEncoding): String
    
    fun withParameter(name: String, value: String): HttpAuthHeader.Parameterized
    fun parameter(name: String): String?
}

Used for authentication schemes that require multiple parameters, such as Digest authentication.

Authentication Schemes

Standard Schemes

object AuthScheme {
    const val Basic: String = "Basic"
    const val Digest: String = "Digest"
    const val Negotiate: String = "Negotiate"
    const val OAuth: String = "OAuth"
    const val Bearer: String = "Bearer"
}

Header Value Encoding

HeaderValueEncoding Enum

enum class HeaderValueEncoding {
    QUOTED_WHEN_REQUIRED,
    QUOTED_ALWAYS,
    URI_ENCODE
}

Controls how authentication parameter values are encoded in the header.

Authentication Parsing

Parsing Functions

fun parseAuthorizationHeader(headerValue: String): HttpAuthHeader?
fun parseAuthorizationHeaders(headerValue: String): List<HttpAuthHeader>

Parse Authorization header values into structured authentication objects.

Usage Examples

Bearer Token Authentication

// Creating Bearer token authentication
val bearerToken = HttpAuthHeader.Single(
    authScheme = AuthScheme.Bearer,
    blob = "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9..."
)

// Rendering the header value
val headerValue = bearerToken.render()
println(headerValue) // Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...

// Using in HTTP request
val headers = headersOf(
    HttpHeaders.Authorization to bearerToken.render()
)

Basic Authentication

// Creating Basic authentication
val credentials = "username:password"
val encodedCredentials = Base64.getEncoder().encodeToString(credentials.toByteArray())

val basicAuth = HttpAuthHeader.Single(
    authScheme = AuthScheme.Basic,
    blob = encodedCredentials
)

val authHeader = basicAuth.render()
println(authHeader) // Basic dXNlcm5hbWU6cGFzc3dvcmQ=

// Helper function for Basic auth
fun createBasicAuth(username: String, password: String): HttpAuthHeader.Single {
    val credentials = "$username:$password"
    val encoded = Base64.getEncoder().encodeToString(credentials.toByteArray())
    return HttpAuthHeader.Single(AuthScheme.Basic, encoded)
}

val basicHeader = createBasicAuth("admin", "secret123")

Digest Authentication

// Creating Digest authentication with parameters
val digestAuth = HttpAuthHeader.Parameterized(
    authScheme = AuthScheme.Digest,
    parameters = mapOf(
        "username" to "admin",
        "realm" to "api.example.com",
        "nonce" to "dcd98b7102dd2f0e8b11d0f600bfb0c093",
        "uri" to "/api/users",
        "response" to "6629fae49393a05397450978507c4ef1",
        "opaque" to "5ccc069c403ebaf9f0171e9517f40e41",
        "qop" to "auth",
        "nc" to "00000001",
        "cnonce" to "0a4f113b"
    )
)

val digestHeader = digestAuth.render()
println(digestHeader)
// Digest username="admin", realm="api.example.com", nonce="dcd98b7102dd2f0e8b11d0f600bfb0c093", ...

// Adding parameters to existing auth header
val updatedDigest = digestAuth.withParameter("algorithm", "MD5")
val algorithmValue = updatedDigest.parameter("algorithm") // "MD5"

OAuth Authentication

// OAuth 1.0a signature authentication
val oauthAuth = HttpAuthHeader.Parameterized(
    authScheme = AuthScheme.OAuth,
    parameters = mapOf(
        "oauth_consumer_key" to "xvz1evFS4wEEPTGEFPHBog",
        "oauth_nonce" to "kYjzVBB8Y0ZFabxSWbWovY3uYSQ2pTgmZeNu2VS4cg",
        "oauth_signature_method" to "HMAC-SHA1",
        "oauth_timestamp" to "1318622958",
        "oauth_token" to "370773112-GmHxMAgYyLbNEtIKZeRNFsMKPR9EyMZeS9weJAEb",
        "oauth_version" to "1.0",
        "oauth_signature" to "tnnArxj06cWHq44gCs1OSKk/jLY="
    )
)

val oauthHeader = oauthAuth.render()
println(oauthHeader)
// OAuth oauth_consumer_key="xvz1evFS4wEEPTGEFPHBog", oauth_nonce="kYjzVBB8Y0ZFabxSWbWovY3uYSQ2pTgmZeNu2VS4cg", ...

Custom Authentication Schemes

// Custom API key authentication
val apiKeyAuth = HttpAuthHeader.Single(
    authScheme = "ApiKey",
    blob = "ak_live_1234567890abcdef"
)

// Custom multi-parameter scheme
val customAuth = HttpAuthHeader.Parameterized(
    authScheme = "CustomScheme",
    parameters = mapOf(
        "client_id" to "my-client-id",
        "signature" to "computed-signature",
        "timestamp" to System.currentTimeMillis().toString()
    )
)

val customHeader = customAuth.render()
println(customHeader) // CustomScheme client_id="my-client-id", signature="computed-signature", ...

Header Value Encoding Options

val authWithSpecialChars = HttpAuthHeader.Parameterized(
    authScheme = AuthScheme.Digest,
    parameters = mapOf(
        "username" to "user@domain.com",
        "realm" to "My Realm With Spaces",
        "response" to "hash/with+special=chars"
    )
)

// Different encoding strategies
val quotedWhenRequired = authWithSpecialChars.render(HeaderValueEncoding.QUOTED_WHEN_REQUIRED)
val alwaysQuoted = authWithSpecialChars.render(HeaderValueEncoding.QUOTED_ALWAYS)
val uriEncoded = authWithSpecialChars.render(HeaderValueEncoding.URI_ENCODE)

println("Quoted when required: $quotedWhenRequired")
println("Always quoted: $alwaysQuoted")
println("URI encoded: $uriEncoded")

Parsing Authentication Headers

// Parse single Authorization header
val authHeaderValue = "Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiaWF0IjoxNTE2MjM5MDIyfQ.SflKxwRJSMeKKF2QT4fwpMeJf36POk6yJV_adQssw5c"

val parsedAuth = parseAuthorizationHeader(authHeaderValue)
when (parsedAuth) {
    is HttpAuthHeader.Single -> {
        println("Scheme: ${parsedAuth.authScheme}") // Bearer
        println("Token: ${parsedAuth.blob}") // JWT token
    }
    is HttpAuthHeader.Parameterized -> {
        println("Scheme: ${parsedAuth.authScheme}")
        println("Parameters: ${parsedAuth.parameters}")
    }
    null -> println("Failed to parse authorization header")
}

// Parse multiple Authorization headers (rare but possible)
val multipleHeaders = listOf(
    "Bearer token123",
    "Basic dXNlcjpwYXNz"
)

val parsedHeaders = parseAuthorizationHeaders(multipleHeaders)
parsedHeaders.forEach { auth ->
    println("Parsed: ${auth.authScheme} - ${auth.render()}")
}

Parsing Digest Authentication

val digestHeaderValue = """Digest username="admin", realm="api.example.com", nonce="dcd98b7102dd2f0e8b11d0f600bfb0c093", uri="/api/users", response="6629fae49393a05397450978507c4ef1", opaque="5ccc069c403ebaf9f0171e9517f40e41", qop="auth", nc="00000001", cnonce="0a4f113b""""

val parsedDigest = parseAuthorizationHeader(digestHeaderValue)
if (parsedDigest is HttpAuthHeader.Parameterized) {
    println("Username: ${parsedDigest.parameter("username")}") // admin
    println("Realm: ${parsedDigest.parameter("realm")}") // api.example.com
    println("Nonce: ${parsedDigest.parameter("nonce")}") // dcd98b7102dd2f0e8b11d0f600bfb0c093
    println("QOP: ${parsedDigest.parameter("qop")}") // auth
}

Authentication Helper Functions

// Helper function to create JWT Bearer auth
fun createJwtAuth(token: String): HttpAuthHeader.Single {
    return HttpAuthHeader.Single(AuthScheme.Bearer, token)
}

// Helper function to create API key auth
fun createApiKeyAuth(apiKey: String, scheme: String = "ApiKey"): HttpAuthHeader.Single {
    return HttpAuthHeader.Single(scheme, apiKey)
}

// Helper function to extract Bearer token
fun extractBearerToken(headers: Headers): String? {
    val authHeader = headers[HttpHeaders.Authorization] ?: return null
    val parsed = parseAuthorizationHeader(authHeader) ?: return null
    
    return if (parsed is HttpAuthHeader.Single && parsed.authScheme == AuthScheme.Bearer) {
        parsed.blob
    } else {
        null
    }
}

// Helper function to validate Basic auth
fun validateBasicAuth(headers: Headers, expectedUsername: String, expectedPassword: String): Boolean {
    val authHeader = headers[HttpHeaders.Authorization] ?: return false
    val parsed = parseAuthorizationHeader(authHeader) ?: return false
    
    if (parsed !is HttpAuthHeader.Single || parsed.authScheme != AuthScheme.Basic) {
        return false
    }
    
    return try {
        val decoded = String(Base64.getDecoder().decode(parsed.blob))
        val (username, password) = decoded.split(":", limit = 2)
        username == expectedUsername && password == expectedPassword
    } catch (e: Exception) {
        false
    }
}

// Usage examples
val jwtAuth = createJwtAuth("eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...")
val apiKeyAuth = createApiKeyAuth("ak_live_1234567890abcdef")

val requestHeaders = headersOf(
    HttpHeaders.Authorization to jwtAuth.render()
)

val token = extractBearerToken(requestHeaders)
println("Extracted token: $token")

Error Handling

// Safe parsing with error handling
fun parseAuthSafe(headerValue: String): HttpAuthHeader? {
    return try {
        parseAuthorizationHeader(headerValue)
    } catch (e: Exception) {
        println("Failed to parse auth header: ${e.message}")
        null
    }
}

// Validate auth header format
fun isValidAuthHeader(headerValue: String): Boolean {
    return parseAuthorizationHeader(headerValue) != null
}

// Examples of invalid headers that will return null
val invalidHeaders = listOf(
    "", // empty
    "InvalidScheme", // no space or parameters
    "Bearer", // missing token
    "Digest incomplete=parameter" // malformed parameters
)

invalidHeaders.forEach { header ->
    val result = parseAuthSafe(header)
    println("'$header' -> ${result != null}")
}

Authentication Middleware Pattern

// Example of authentication handling in middleware
class AuthenticationHandler {
    fun handleAuthentication(headers: Headers): AuthResult {
        val authHeader = headers[HttpHeaders.Authorization]
            ?: return AuthResult.Missing
        
        val parsed = parseAuthorizationHeader(authHeader)
            ?: return AuthResult.Invalid("Malformed authorization header")
        
        return when (parsed) {
            is HttpAuthHeader.Single -> when (parsed.authScheme) {
                AuthScheme.Bearer -> validateBearerToken(parsed.blob)
                AuthScheme.Basic -> validateBasicCredentials(parsed.blob)
                else -> AuthResult.UnsupportedScheme(parsed.authScheme)
            }
            is HttpAuthHeader.Parameterized -> when (parsed.authScheme) {
                AuthScheme.Digest -> validateDigestAuth(parsed)
                AuthScheme.OAuth -> validateOAuthSignature(parsed)
                else -> AuthResult.UnsupportedScheme(parsed.authScheme)
            }
        }
    }
    
    private fun validateBearerToken(token: String): AuthResult {
        // JWT validation logic here
        return AuthResult.Success("user-id-from-token")
    }
    
    private fun validateBasicCredentials(credentials: String): AuthResult {
        // Basic auth validation logic here
        return AuthResult.Success("user-id-from-basic")
    }
    
    private fun validateDigestAuth(auth: HttpAuthHeader.Parameterized): AuthResult {
        // Digest auth validation logic here
        return AuthResult.Success("user-id-from-digest")
    }
    
    private fun validateOAuthSignature(auth: HttpAuthHeader.Parameterized): AuthResult {
        // OAuth signature validation logic here  
        return AuthResult.Success("user-id-from-oauth")
    }
}

sealed class AuthResult {
    object Missing : AuthResult()
    data class Invalid(val reason: String) : AuthResult()
    data class UnsupportedScheme(val scheme: String) : AuthResult()
    data class Success(val userId: String) : AuthResult()
}