Basic Auth Generator — TypeScript 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 TypeScript implementation — the same logic the interactive tool runs, in a shareable, citable form.
// Pure HTTP Basic-Auth header builder/parser. Zero deps - reuses the UTF-8
// safe base64 helpers from src/lib/base64.ts. The unit-test surface for the
// Basic Auth Generator tool.
import { b64encode, b64decode } from './base64';
export interface BasicAuthCredentials {
user: string;
pass: string;
}
/** Build an HTTP Basic-Auth header value: `Basic <base64(user:pass)>`. */
export function buildBasicAuth(user: string, pass: string): string {
return 'Basic ' + b64encode(`${user}:${pass}`);
}
/** Parse a `Basic <token>` header back into credentials, or null if malformed. */
export function parseBasicAuth(header: string): BasicAuthCredentials | null {
const m = header.trim().match(/^Basic\s+(.+)$/i);
if (!m) return null;
let creds: string;
try {
creds = b64decode(m[1]);
} catch {
return null;
}
const idx = creds.indexOf(':');
if (idx < 0) return null;
return { user: creds.slice(0, idx), pass: creds.slice(idx + 1) };
}
export type BasicAuthIssueCode =
| 'empty-username'
| 'colon-in-username'
| 'password-non-ascii';
export interface BasicAuthIssue {
code: BasicAuthIssueCode;
severity: 'warn' | 'info';
value?: string;
}
/**
* Lint credentials before they ship. All three checks are 'warn':
* - empty-username: servers usually accept it, but logs and proxies get confusing
* - colon-in-username: the user:pass split happens at the FIRST colon, so a
* colon in the username is silently truncated by every conforming parser
* - password-non-ascii: RFC 7617 defaults to UTF-8 but many servers assume
* ISO-8859-1/US-ASCII and mangle or reject non-ASCII bytes
*/
export function validateBasicAuth(creds: BasicAuthCredentials): BasicAuthIssue[] {
const issues: BasicAuthIssue[] = [];
if (creds.user === '') {
issues.push({ code: 'empty-username', severity: 'warn' });
}
if (creds.user.includes(':')) {
issues.push({ code: 'colon-in-username', severity: 'warn', value: creds.user });
}
if (/[^\x00-\x7F]/.test(creds.pass)) {
issues.push({ code: 'password-non-ascii', severity: 'warn', value: creds.pass });
}
return issues;
}
// URL codecs for shareable state. Each component is encodeURIComponent'd on
// the way out; the two flat strings are independent params (no ',' '|' '='
// grammar needed). Malformed escapes decode verbatim rather than throw.
const enc = (s: string) => encodeURIComponent(s);
const dec = (s: string): string => {
try {
return decodeURIComponent(s);
} catch {
return s; // malformed escape - keep verbatim rather than throw
}
};
export function toQuery(creds: BasicAuthCredentials): string {
const p = new URLSearchParams();
if (creds.user !== '') p.set('u', enc(creds.user));
if (creds.pass !== '') p.set('p', enc(creds.pass));
return p.toString();
}
export function fromQuery(params: URLSearchParams): BasicAuthCredentials | null {
const u = params.get('u');
const p = params.get('p');
if (u === null && p === null) return null;
return { user: u === null ? '' : dec(u), pass: p === null ? '' : dec(p) };
}
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 →