Basic Auth Generator — Swift 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 Swift implementation — the same logic the interactive tool runs, in a shareable, citable form.
// basic-auth-generator — HTTP Basic-Auth header encode/decode.
//
// Language: Swift (5.9, standard library + Foundation)
// 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 traps (parseHeader returns nil on
// malformed input).
// - Functionally equivalent to the TS reference + Go twin: same inputs ->
// same outputs, same reject behavior on malformed headers.
// - Self-contained: stdlib + Foundation only (Data supplies the UTF-8 byte
// bridge that Swift's String API needs).
//
// Codec note: Foundation's Data.base64EncodedString() provides the RFC 4648
// standard encoder used by every other port. For decoding, Data(base64Encoded:)
// has historically ignored unknown characters on some platforms, so — like the
// Rust port — a strict decoder is implemented inline: whitespace is stripped,
// the length must be a multiple of 4, and every character must be in the
// standard alphabet or count as '=' padding.
import Foundation
/// Credentials hold a parsed username/password pair. Mirrors the Go twin's
/// `Credentials` struct and the TS `BasicAuthCredentials` interface.
struct Credentials: Equatable {
var user: String
var pass: String
}
/// RFC 4648 §4 standard base64 alphabet, as UTF-8 bytes.
private let b64Alphabet = Array("ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/".utf8)
/// ASCII whitespace skipped by the decoder: space (0x20), tab (0x09),
/// line feed (0x0A), carriage return (0x0D) — the same set Rust's
/// `u8::is_ascii_whitespace` strips.
private func isSkippedWhitespace(_ byte: UInt8) -> Bool {
byte == 0x20 || byte == 0x09 || byte == 0x0A || byte == 0x0D
}
/// Reverse lookup: value of one base64 digit, or nil if not in the alphabet.
private func b64Value(_ byte: UInt8) -> UInt8? {
if let idx = b64Alphabet.firstIndex(of: byte) {
return UInt8(idx)
}
return nil
}
/// Build an HTTP Basic-Auth header value: `"Basic " + base64(user:pass)`.
/// Mirrors `BuildHeader` in the Go twin and `buildBasicAuth` in the TS source.
/// Data(string.utf8) carries the UTF-8 byte representation, so multibyte
/// credentials (e.g. `café:päss`) encode correctly.
func buildHeader(user: String, pass: String) -> String {
let raw = Data("\(user):\(pass)".utf8)
return "Basic " + raw.base64EncodedString()
}
/// Decode standard base64 strictly into bytes. Returns nil on any invalid
/// character or malformed padding, matching Go's
/// `base64.StdEncoding.DecodeString`. Whitespace inside the token is ignored,
/// mirroring the TS `b64decode(token.replace(/\s+/g, ''))` helper.
private func b64Decode(_ input: String) -> Data? {
let cleaned = input.utf8.filter { !isSkippedWhitespace($0) }
if cleaned.count % 4 != 0 { return nil }
var out = [UInt8]()
out.reserveCapacity(cleaned.count / 4 * 3)
var i = 0
while i < cleaned.count {
var idx = [UInt8](repeating: 0, count: 4)
var pads = 0
for j in 0..<4 {
let byte = cleaned[i + j]
if byte == 0x3D { // '='
pads += 1
} else if let v = b64Value(byte) {
idx[j] = v
} else {
return nil // invalid character
}
}
let n = (UInt32(idx[0]) << 18) | (UInt32(idx[1]) << 12)
| (UInt32(idx[2]) << 6) | UInt32(idx[3])
out.append(UInt8(truncatingIfNeeded: n >> 16))
if pads < 2 { out.append(UInt8(truncatingIfNeeded: n >> 8)) }
if pads < 1 { out.append(UInt8(truncatingIfNeeded: n)) }
i += 4
}
return Data(out)
}
/// Parse a `"Basic <token>"` header back into credentials, or nil 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), UTF-8-validate, then split on the FIRST
/// colon so a password containing colons round-trips.
func parseHeader(_ header: String) -> Credentials? {
let h = header.trimmingCharacters(in: .whitespacesAndNewlines)
guard h.lowercased().hasPrefix("basic ") else { return nil }
let token = String(h.dropFirst(6)).trimmingCharacters(in: .whitespacesAndNewlines)
guard let data = b64Decode(token) else { return nil }
// String(data:encoding:) is nil on invalid UTF-8 — the same strict
// validation Rust's String::from_utf8 performs.
guard let decoded = String(data: data, encoding: .utf8) else { return nil }
// Split on the FIRST colon only (maxSplits: 1) — the rest stays in the
// password. Empty halves are kept so ("", "x") and ("x", "") round-trip.
let parts = decoded.split(separator: ":", maxSplits: 1, omittingEmptySubsequences: false)
guard parts.count == 2 else { return nil }
return Credentials(user: String(parts[0]), pass: String(parts[1]))
}
/// Showcase-only demo (the canonical suite lives in src/lib): runs the same
/// vectors as the Rust port. Swift files other than main.swift cannot hold
/// top-level code, so the examples live in a callable run() instead of a
/// main() — invoke `BasicAuthDemo.run()` from a host program or REPL.
enum BasicAuthDemo {
static func run() {
let h = buildHeader(user: "user", pass: "pass")
precondition(h == "Basic dXNlcjpwYXNz", "known vector mismatch: \(h)")
print(h)
let roundTrips: [(String, String)] = [
("alice", "s3cr3t"), ("", "x"), ("x", ""), ("u", "a:b:c"),
]
for (user, pass) in roundTrips {
guard let creds = parseHeader(buildHeader(user: user, pass: pass)) else {
preconditionFailure("round trip should parse for (\(user), \(pass))")
}
precondition(creds.user == user, "user mismatch for (\(user), \(pass))")
// colon-bearing password stays intact: split happens on the first colon only
precondition(creds.pass == pass, "password mismatch for (\(user), \(pass))")
}
let malformed = ["Bearer xyz", "", "Basic !!!not-base64!!!", "Basic bm9jb2xvbg=="]
for bad in malformed {
precondition(parseHeader(bad) == nil, "\(bad) should be rejected")
}
print("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 →