import { base64urlDecodeJson, base64urlEncodeString, } from "../base64url/base64url.js"; import { JWS_CODES, JwsError } from "../errors/error-codes.js"; import { recordError } from "../observability/metrics.js"; import { JoseHeaderSchema } from "../schemas/schemas.js"; import type { JoseHeader } from "./header.types.js"; /** * Parse a base64url-encoded JOSE protected header (or a raw object) and * validate the registered-parameter shape per RFC 7515 ยง4.1. Unknown * parameters survive round-trip. * * @throws JwsError(JWS.HEADER_MALFORMED) when the input does not decode * to a JSON object whose `alg` is a string. */ export function parseHeader( input: string | Record ): JoseHeader { const raw = typeof input === "string" ? base64urlDecodeJson(input) : input; const parsed = JoseHeaderSchema.safeParse(raw); if (!parsed.success) { const err = new JwsError( JWS_CODES.HEADER_MALFORMED, "error.jws.header-shape-invalid", { context: { issues: parsed.error.issues } } ); recordError(err.code); throw err; } return parsed.data as JoseHeader; } /** * Serialize a JOSE header to a base64url-encoded UTF-8 JSON string. * Keys preserve insertion order; callers that need a canonical order * should sort before passing in. */ export function serializeHeader( header: JoseHeader | Record ): string { return base64urlEncodeString(JSON.stringify(header)); }