Skip to content

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

# basic-auth-generator — HTTP Basic-Auth header encode/decode.
#
# Language: Ruby (3.2+, 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 raises (parse_header 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 only (the base64 default gem, which ships with
#     Ruby).
#
# Ruby is a dependency-rich polyglot here, like the Python port: the standard
# library ships an RFC 4648 codec. Base64.strict_encode64/strict_decode64 use
# the real standard path — strict_decode64 rejects invalid characters exactly
# like Go's base64.StdEncoding.DecodeString. Two quirks are compensated for
# below: the strict decoder does not skip whitespace, and its result is tagged
# ASCII-8BIT, so the bytes are re-tagged UTF-8 and validated before use.

require 'base64'

module BasicAuth
  # A parsed username/password pair. Mirrors the Go twin's Credentials struct
  # and the TS BasicAuthCredentials interface.
  Credentials = Struct.new(:user, :pass)

  module_function

  # Build an HTTP Basic-Auth header value: "Basic " + base64(user:pass).
  # Mirrors BuildHeader in the Go twin and buildBasicAuth in the TS source.
  # strict_encode64 packs the string's bytes, so multibyte UTF-8 credentials
  # (e.g. "café:päss") encode correctly.
  def build_header(user, pass)
    "Basic #{Base64.strict_encode64("#{user}:#{pass}")}"
  end

  # 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), then split on the FIRST colon so a
  # password containing colons round-trips.
  def parse_header(header)
    h = header.strip
    prefix = 'basic '
    return nil if h.length < prefix.length || h[0, prefix.length].downcase != prefix

    token = h[prefix.length..].strip
    # strict_decode64 does not skip whitespace; strip it and enforce the
    # multiple-of-4 length ourselves — exactly like Go's StdEncoding.
    cleaned = token.gsub(/\s+/, '')
    return nil if cleaned.length % 4 != 0

    decoded =
      begin
        Base64.strict_decode64(cleaned)
      rescue ArgumentError
        return nil
      end

    decoded = decoded.force_encoding(Encoding::UTF_8)
    return nil unless decoded.valid_encoding?

    user, pass = decoded.split(':', 2)
    return nil if pass.nil? # no colon separator

    Credentials.new(user, pass)
  end
end

# ---------- showcase-only demo (canonical suite lives in src/lib) ----------
if __FILE__ == $PROGRAM_NAME
  h = BasicAuth.build_header('user', 'pass')
  raise "known vector mismatch: #{h}" unless h == 'Basic dXNlcjpwYXNz'
  puts h

  [['alice', 's3cr3t'], ['', 'x'], ['x', ''], ['u', 'a:b:c']].each do |user, pass|
    creds = BasicAuth.parse_header(BasicAuth.build_header(user, pass))
    raise "round trip should parse for (#{user}, #{pass})" if creds.nil?
    raise "user mismatch for (#{user}, #{pass})" unless creds.user == user
    # colon-bearing password stays intact: split happens on the first colon only
    raise "password mismatch for (#{user}, #{pass})" unless creds.pass == pass
  end

  ['Bearer xyz', '', 'Basic !!!not-base64!!!', 'Basic bm9jb2xvbg=='].each do |bad|
    raise "#{bad} should be rejected" unless BasicAuth.parse_header(bad).nil?
  end
  puts 'all basic-auth-generator demo checks passed'
end

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 →