Skip to content

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

// basic-auth-generator — HTTP Basic-Auth header encode/decode.
//
// Language: Java (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 (parseHeader returns Optional.empty()
//     on malformed input).
//   - Functionally equivalent to the TS reference + Go twin: same inputs ->
//     same outputs, same reject behavior on malformed headers.
//   - Self-contained: the JDK only (java.util.Base64 is the RFC 4648 standard
//     codec, the direct equivalent of Go's encoding/base64 StdEncoding).
//
// The class is package-private on purpose: the polyglot showcase names this
// file java.java, and javac requires only *public* classes to match their
// filename. Two JDK quirks are compensated for below — java.util.Base64's
// basic decoder accepts unpadded input, and new String(bytes, UTF_8) 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;
import java.util.Optional;

/**
 * HTTP Basic-Auth header encode/decode, ported from src/lib/basic-auth.ts
 * (the canonical TypeScript implementation) and kept in lock-step with
 * cli/basic-auth-generator.
 */
final class BasicAuthGenerator {

    private BasicAuthGenerator() {}

    /**
     * A parsed username/password pair. Mirrors the Go twin's Credentials
     * struct and the TS BasicAuthCredentials interface.
     */
    record Credentials(String user, String pass) {}

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

    /**
     * Parse a {@code "Basic <token>"} header back into credentials, or
     * {@code Optional.empty()} if the header is malformed or the credentials
     * have no colon separator. Logic mirrors {@code ParseHeader} in the Go
     * twin exactly: trim the header, require a case-insensitive
     * {@code "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.
     */
    static Optional<Credentials> parseHeader(String header) {
        String h = header.strip();
        String prefix = "basic ";
        if (h.length() < prefix.length()
                || !h.substring(0, prefix.length()).toLowerCase(Locale.ROOT).equals(prefix)) {
            return Optional.empty();
        }

        String token = h.substring(prefix.length()).strip();
        // 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.
        String cleaned = token.replaceAll("\\s+", "");
        if (cleaned.length() % 4 != 0) {
            return Optional.empty();
        }

        byte[] decoded;
        try {
            decoded = Base64.getDecoder().decode(cleaned);
        } catch (IllegalArgumentException e) {
            return Optional.empty();
        }

        String text = decodeUtf8Strict(decoded);
        if (text == null) {
            return Optional.empty();
        }

        int colon = text.indexOf(':');
        if (colon < 0) {
            return Optional.empty();
        }
        return Optional.of(new Credentials(text.substring(0, colon), text.substring(colon + 1)));
    }

    /**
     * Strict UTF-8 decode: {@code new String(bytes, 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 static String decodeUtf8Strict(byte[] bytes) {
        try {
            return StandardCharsets.UTF_8.newDecoder()
                    .onMalformedInput(CodingErrorAction.REPORT)
                    .onUnmappableCharacter(CodingErrorAction.REPORT)
                    .decode(ByteBuffer.wrap(bytes))
                    .toString();
        } catch (CharacterCodingException e) {
            return null;
        }
    }

    // ---------- showcase-only demo (canonical suite lives in src/lib) ----------
    public static void main(String[] args) {
        String h = buildHeader("user", "pass");
        if (!h.equals("Basic dXNlcjpwYXNz")) throw new AssertionError(h);
        System.out.println(h);

        String[][] roundTrips = {{"alice", "s3cr3t"}, {"", "x"}, {"x", ""}, {"u", "a:b:c"}};
        for (String[] pair : roundTrips) {
            Credentials c = parseHeader(buildHeader(pair[0], pair[1])).orElseThrow();
            if (!c.user().equals(pair[0]) || !c.pass().equals(pair[1])) {
                throw new AssertionError("round trip failed for " + pair[0] + ":" + pair[1]);
            }
        }

        String[] malformed = {
            "Bearer xyz", "", "Basic !!!not-base64!!!", "Basic bm9jb2xvbg==", // "nocolon"
        };
        for (String bad : malformed) {
            if (parseHeader(bad).isPresent()) throw new AssertionError(bad + " should be rejected");
        }
        System.out.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 →