Skip to content

Basic Auth Generator — C++ 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 C++ implementation — the same logic the interactive tool runs, in a shareable, citable form.

// basic-auth-generator — HTTP Basic-Auth header encode/decode.
//
// Language: C++ (C++17, 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 (parse_header returns std::nullopt
//     on malformed input).
//   - Functionally equivalent to the TS reference + Go twin: same inputs ->
//     same outputs, same reject behavior on malformed headers.
//   - Self-contained: std only (noBoost or other codec dependencies).
//
// Unicode note: C++ has no stdlib base64 codec, so the full RFC 4648 §4
// standard encoder/decoder is implemented inline (like the Rust port).
// std::string is a byte string, so multibyte UTF-8 credentials (e.g.
// "café:päss") encode correctly; C++17 ships no UTF-8 validator, so decoded
// bytes pass through unchanged — byte-for-byte compatible with the other
// ports for every valid input.

#include <cctype>
#include <cstddef>
#include <optional>
#include <string>

// RFC 4648 §4 standard base64 alphabet.
static const char *const B64_ALPHABET =
    "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";

// Reverse lookup: value of one base64 digit, or -1 if not in the alphabet.
static int b64_value(char c) {
    unsigned char u = static_cast<unsigned char>(c);
    if (u >= 'A' && u <= 'Z') return u - 'A';
    if (u >= 'a' && u <= 'z') return u - 'a' + 26;
    if (u >= '0' && u <= '9') return u - '0' + 52;
    if (c == '+') return 62;
    if (c == '/') return 63;
    return -1;
}

// Encode arbitrary bytes into standard base64 (with '=' padding), matching
// Go's base64.StdEncoding.EncodeToString. Processes input in 3-byte groups,
// emitting 4 characters per group; a trailing 1- or 2-byte group emits 2 or 3
// characters plus '=' padding.
static std::string b64_encode(const std::string &bytes) {
    std::string out;
    out.reserve((bytes.size() + 2) / 3 * 4);

    for (std::size_t i = 0; i < bytes.size(); i += 3) {
        unsigned b0 = static_cast<unsigned char>(bytes[i]);
        unsigned b1 = (i + 1 < bytes.size()) ? static_cast<unsigned char>(bytes[i + 1]) : 0u;
        unsigned b2 = (i + 2 < bytes.size()) ? static_cast<unsigned char>(bytes[i + 2]) : 0u;
        unsigned n = (b0 << 16) | (b1 << 8) | b2;

        out.push_back(B64_ALPHABET[(n >> 18) & 0x3Fu]);
        out.push_back(B64_ALPHABET[(n >> 12) & 0x3Fu]);
        out.push_back((i + 1 < bytes.size()) ? B64_ALPHABET[(n >> 6) & 0x3Fu] : '=');
        out.push_back((i + 2 < bytes.size()) ? B64_ALPHABET[n & 0x3Fu] : '=');
    }
    return out;
}

// Decode a standard base64 string into bytes. Whitespace inside the token is
// ignored; the cleaned length must be a multiple of 4; any character outside
// the alphabet (other than '=' padding) is rejected. Mirrors Go's
// base64.StdEncoding.DecodeString (error -> std::nullopt).
static std::optional<std::string> b64_decode(const std::string &input) {
    std::string cleaned;
    cleaned.reserve(input.size());
    for (char c : input) {
        if (!std::isspace(static_cast<unsigned char>(c))) cleaned.push_back(c);
    }
    if (cleaned.size() % 4 != 0) return std::nullopt;

    std::string out;
    out.reserve(cleaned.size() / 4 * 3);
    for (std::size_t i = 0; i < cleaned.size(); i += 4) {
        unsigned idx[4] = {0u, 0u, 0u, 0u};
        int pads = 0;
        for (int j = 0; j < 4; ++j) {
            char c = cleaned[i + static_cast<std::size_t>(j)];
            if (c == '=') {
                ++pads;
            } else {
                int v = b64_value(c);
                if (v < 0) return std::nullopt; // invalid character
                idx[j] = static_cast<unsigned>(v);
            }
        }
        unsigned n = (idx[0] << 18) | (idx[1] << 12) | (idx[2] << 6) | idx[3];
        out.push_back(static_cast<char>((n >> 16) & 0xFFu));
        if (pads < 2) out.push_back(static_cast<char>((n >> 8) & 0xFFu));
        if (pads < 1) out.push_back(static_cast<char>(n & 0xFFu));
    }
    return out;
}

// Credentials hold a parsed username/password pair. Mirrors the Go twin's
// Credentials struct and the TS BasicAuthCredentials interface.
struct Credentials {
    std::string user;
    std::string pass;
};

// Build an HTTP Basic-Auth header value: "Basic " + base64(user:pass).
// Mirrors BuildHeader in the Go twin and buildBasicAuth in the TS source.
std::string build_header(const std::string &user, const std::string &pass) {
    return "Basic " + b64_encode(user + ":" + pass);
}

// Trim ASCII whitespace from both ends (std::string has no trim in C++17).
static std::string trimmed(const std::string &s) {
    std::size_t b = 0;
    std::size_t e = s.size();
    while (b < e && std::isspace(static_cast<unsigned char>(s[b]))) ++b;
    while (e > b && std::isspace(static_cast<unsigned char>(s[e - 1]))) --e;
    return s.substr(b, e - b);
}

// Parse a "Basic <token>" header back into credentials, or std::nullopt 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.
std::optional<Credentials> parse_header(const std::string &header) {
    const std::string h = trimmed(header);
    static const std::string prefix = "basic ";
    if (h.size() < prefix.size()) return std::nullopt;
    for (std::size_t i = 0; i < prefix.size(); ++i) {
        if (std::tolower(static_cast<unsigned char>(h[i])) != prefix[i]) return std::nullopt;
    }

    const std::string token = trimmed(h.substr(prefix.size()));
    const std::optional<std::string> decoded = b64_decode(token);
    if (!decoded.has_value()) return std::nullopt;

    const std::size_t colon = decoded->find(':');
    if (colon == std::string::npos) return std::nullopt;
    return Credentials{decoded->substr(0, colon), decoded->substr(colon + 1)};
}

// ---------- showcase-only demo (canonical suite lives in src/lib) ----------
// Compile with -DBASIC_AUTH_DEMO to run the same vectors as the Rust port.
#ifdef BASIC_AUTH_DEMO
#include <cassert>
#include <iostream>

int main() {
    std::string h = build_header("user", "pass");
    assert(h == "Basic dXNlcjpwYXNz");
    std::cout << h << '\n';

    const std::pair<std::string, std::string> round_trips[] = {
        {"alice", "s3cr3t"}, {"", "x"}, {"x", ""}, {"u", "a:b:c"},
    };
    for (const auto &[user, pass] : round_trips) {
        auto creds = parse_header(build_header(user, pass));
        assert(creds.has_value());
        assert(creds->user == user);
        assert(creds->pass == pass); // colon-bearing password intact
    }

    const std::string malformed[] = {
        "Bearer xyz", "", "Basic !!!not-base64!!!", "Basic bm9jb2xvbg==", // "nocolon"
    };
    for (const std::string &bad : malformed) {
        assert(!parse_header(bad).has_value());
    }
    std::cout << "all basic-auth-generator demo checks passed\n";
    return 0;
}
#endif

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 →