Skip to content

Basic Auth Generator — Kotlin source

Build HTTP Basic Access Authentication headers from a username and password, or decode an existing Authorization header back to its credentials. UTF-8 safe and fully client-side, with a shareable link.

This is the Kotlin implementation — the same logic the interactive tool runs, in a shareable, citable form.

// basic-auth-generator — HTTP Basic-Auth header encode/decode.
//
// Language: Kotlin (1.9, JVM, standard library only)
// Source:   CosmoDev polyglot showcase port of the Basic Auth Generator tool,
//           ported from src/lib/basic-auth.ts (the canonical TypeScript
//           implementation) and kept in lock-step with cli/basic-auth-generator.
// License:  display source — part of CosmoDev's polyglot tool pages.
//
// Design goals:
//   - Pure + deterministic; never throws (parseHeader returns null on
//     malformed input).
//   - Functionally equivalent to the TS reference + Go twin: same inputs ->
//     same outputs, same reject behavior on malformed headers.
//   - Self-contained: Kotlin stdlib + the JDK's java.util.Base64 (the RFC 4648
//     standard codec, the direct equivalent of Go's encoding/base64).
//
// Two JDK quirks are compensated for below — java.util.Base64's basic decoder
// accepts unpadded input, and String construction from UTF-8 bytes silently
// replaces invalid sequences with U+FFFD — so both get the strict
// Go-equivalent behavior.

import java.nio.ByteBuffer
import java.nio.charset.CharacterCodingException
import java.nio.charset.CodingErrorAction
import java.nio.charset.StandardCharsets
import java.util.Base64
import java.util.Locale

/**
 * A parsed username/password pair. Mirrors the Go twin's `Credentials` struct
 * and the TS `BasicAuthCredentials` interface.
 */
data class Credentials(val user: String, val pass: String)

/**
 * Build an HTTP Basic-Auth header value: `"Basic " + base64(user:pass)`.
 * Mirrors `BuildHeader` in the Go twin and `buildBasicAuth` in the TS source.
 * The UTF-8 byte representation is encoded, so multibyte credentials (e.g.
 * `café:päss`) work.
 */
fun buildHeader(user: String, pass: String): String {
    val raw = "$user:$pass".toByteArray(StandardCharsets.UTF_8)
    return "Basic " + Base64.getEncoder().encodeToString(raw)
}

/**
 * Parse a `"Basic <token>"` header back into credentials, or `null` if the
 * header is malformed or the credentials have no colon separator. Logic
 * mirrors `ParseHeader` in the Go twin exactly: trim the header, require a
 * case-insensitive `"basic "` prefix, trim the remaining token, base64-decode
 * it (rejecting invalid characters), then split on the FIRST colon so a
 * password containing colons round-trips.
 */
fun parseHeader(header: String): Credentials? {
    val h = header.trim()
    val prefix = "basic "
    if (h.take(prefix.length).lowercase(Locale.ROOT) != prefix) return null

    val token = h.substring(prefix.length).trim()
    // The basic decoder ignores nothing and accepts missing padding, so strip
    // whitespace and enforce the multiple-of-4 length ourselves — exactly
    // like Go's StdEncoding.
    val cleaned = token.replace(Regex("\\s+"), "")
    if (cleaned.length % 4 != 0) return null

    val decoded = try {
        Base64.getDecoder().decode(cleaned)
    } catch (e: IllegalArgumentException) {
        return null
    }

    val text = decodeUtf8Strict(decoded) ?: return null
    val colon = text.indexOf(':')
    if (colon < 0) return null
    return Credentials(text.substring(0, colon), text.substring(colon + 1))
}

/**
 * Strict UTF-8 decode: `String(bytes, Charsets.UTF_8)` silently replaces
 * malformed sequences with U+FFFD; the Go/Rust twins reject them, so this
 * decoder reports them and maps the failure to null.
 */
private fun decodeUtf8Strict(bytes: ByteArray): String? =
    try {
        StandardCharsets.UTF_8.newDecoder()
            .onMalformedInput(CodingErrorAction.REPORT)
            .onUnmappableCharacter(CodingErrorAction.REPORT)
            .decode(ByteBuffer.wrap(bytes))
            .toString()
    } catch (e: CharacterCodingException) {
        null
    }

// ---------- showcase-only demo (canonical suite lives in src/lib) ----------
fun main() {
    val h = buildHeader("user", "pass")
    check(h == "Basic dXNlcjpwYXNz") { "known vector mismatch: $h" }
    println(h)

    val roundTrips = listOf("alice" to "s3cr3t", "" to "x", "x" to "", "u" to "a:b:c")
    for ((user, pass) in roundTrips) {
        val creds = parseHeader(buildHeader(user, pass))
        check(creds != null) { "round trip should parse for ($user, $pass)" }
        check(creds.user == user) { "user mismatch for ($user, $pass)" }
        check(creds.pass == pass) { "password mismatch for ($user, $pass)" } // colon-bearing pw intact
    }

    val malformed = listOf("Bearer xyz", "", "Basic !!!not-base64!!!", "Basic bm9jb2xvbg==")
    for (bad in malformed) {
        check(parseHeader(bad) == null) { "$bad should be rejected" }
    }
    println("all basic-auth-generator demo checks passed")
}

Also available in 13 other languages

Every CosmoDev tool ships its pure logic in TypeScript (web) and Go (CLI), with authored implementations in a dozen-plus languages — the same contract, ported. Compare all languages side by side →