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# (12, .NET 8, 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: the base class library only (System.Convert base64 +
//     System.Text.UTF8Encoding).
//
// C# is a dependency-rich polyglot here, like the Python port: the BCL ships
// an RFC 4648 standard codec that matches Go's base64.StdEncoding. Two BCL
// quirks are compensated for below — Convert.FromBase64String treats padding
// as optional, and Encoding.UTF8.GetString silently replaces invalid byte
// sequences with U+FFFD — so both get the strict Go-equivalent behavior.

using System;
using System.Linq;
using System.Text;

namespace CosmoDev.Snippets;

/// <summary>A parsed username/password pair. Mirrors the Go twin's
/// <c>Credentials</c> struct and the TS <c>BasicAuthCredentials</c> interface.</summary>
public sealed record Credentials(string User, string Pass);

public static class BasicAuth
{
    /// <summary>UTF-8 encoding that throws on invalid sequences instead of
    /// replacing them with U+FFFD — the Go/Rust twins reject invalid UTF-8,
    /// so this port does too.</summary>
    private static readonly UTF8Encoding Utf8Strict =
        new(encoderShouldEmitUTF8Identifier: false, throwOnInvalidBytes: true);

    /// <summary>Build an HTTP Basic-Auth header value:
    /// <c>"Basic " + base64(user:pass)</c>. Mirrors <c>BuildHeader</c> in the
    /// Go twin and <c>buildBasicAuth</c> in the TS source. The UTF-8 byte
    /// representation is encoded, so multibyte credentials (e.g.
    /// <c>café:päss</c>) work.</summary>
    public static string BuildHeader(string user, string pass)
    {
        byte[] raw = Utf8Strict.GetBytes(user + ":" + pass);
        return "Basic " + Convert.ToBase64String(raw);
    }

    /// <summary>Parse a <c>"Basic &lt;token&gt;"</c> header back into
    /// credentials, or <c>null</c> if the header is malformed or the
    /// credentials have no colon separator. Logic mirrors <c>ParseHeader</c>
    /// in the Go twin exactly: trim the header, require a case-insensitive
    /// <c>"basic "</c> prefix, trim the remaining token, base64-decode it
    /// (rejecting invalid characters), then split on the FIRST colon so a
    /// password containing colons round-trips.</summary>
    public static Credentials? ParseHeader(string header)
    {
        string h = header.Trim();
        if (!h.StartsWith("Basic ", StringComparison.OrdinalIgnoreCase))
        {
            return null;
        }

        string token = h[6..].Trim();

        // FromBase64String ignores whitespace but treats '=' padding as
        // optional; strip whitespace and enforce the multiple-of-4 length
        // ourselves so malformed tokens are rejected like Go's StdEncoding.
        string compact = string.Concat(token.Where(c => !char.IsWhiteSpace(c)));
        if (compact.Length % 4 != 0)
        {
            return null;
        }

        byte[] decoded;
        try
        {
            decoded = Convert.FromBase64String(compact);
        }
        catch (FormatException)
        {
            return null;
        }

        string text;
        try
        {
            text = Utf8Strict.GetString(decoded);
        }
        catch (DecoderFallbackException)
        {
            return null;
        }

        int colon = text.IndexOf(':');
        return colon < 0 ? null : new Credentials(text[..colon], text[(colon + 1)..]);
    }
}

#if BASIC_AUTH_DEMO
// Showcase-only demo (the canonical suite lives in src/lib): compile with
// -define:BASIC_AUTH_DEMO to run the same vectors as the Rust port.
public static class BasicAuthDemo
{
    public static void Main()
    {
        string h = BasicAuth.BuildHeader("user", "pass");
        System.Diagnostics.Debug.Assert(h == "Basic dXNlcjpwYXNz");
        Console.WriteLine(h);

        (string User, string Pass)[] roundTrips =
        {
            ("alice", "s3cr3t"), ("", "x"), ("x", ""), ("u", "a:b:c"),
        };
        foreach (var (user, pass) in roundTrips)
        {
            Credentials c = BasicAuth.ParseHeader(BasicAuth.BuildHeader(user, pass))!;
            System.Diagnostics.Debug.Assert(c.User == user);
            System.Diagnostics.Debug.Assert(c.Pass == pass); // colon-bearing password intact
        }

        string[] malformed = { "Bearer xyz", "", "Basic !!!not-base64!!!", "Basic bm9jb2xvbg==" };
        foreach (string bad in malformed)
        {
            System.Diagnostics.Debug.Assert(BasicAuth.ParseHeader(bad) is null);
        }
        Console.WriteLine("all basic-auth-generator demo checks passed");
    }
}
#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 →