45 lines
1.5 KiB
TypeScript
45 lines
1.5 KiB
TypeScript
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<string, unknown>
|
|
): 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, unknown>
|
|
): string {
|
|
return base64urlEncodeString(JSON.stringify(header));
|
|
}
|