@gramota/jose
JWS sign + verify, x5c chain validation, pluggable Signer Strategy.
Install: pnpm add @gramota/jose
Source: github.com/gramota-org/gramota/tree/main/packages/jose
Classes
JwkSigner
Defined in: @gramota/jose/dist/signer.d.ts:64
Default Signer implementation backed by an in-memory JWK.
Handy for tests, dev environments, and server-side issuers that hold their signing key in a secret-manager-fetched env var. For mobile wallets and high-assurance flows, swap in a hardware-backed Signer.
Implements
Constructors
Constructor
new JwkSigner(options: JwkSignerOptions): JwkSigner;
Defined in: @gramota/jose/dist/signer.d.ts:70
Parameters
options
Returns
Properties
publicKey
readonly publicKey: JsonWebKey;
Defined in: @gramota/jose/dist/signer.d.ts:65
Public counterpart — verifiers use this. Must always be extractable
(not in an HSM), since it's needed downstream for cnf.jwk etc.
Implementation of
alg
readonly alg: SupportedAlg;
Defined in: @gramota/jose/dist/signer.d.ts:66
JWS algorithm this signer produces. Must match publicKey's
algorithm capabilities (e.g. ES256 with a P-256 EC key).
Implementation of
Methods
sign()
sign(signedPayload: string): Promise<string>;
Defined in: @gramota/jose/dist/signer.d.ts:71
Sign a "header.payload" string, return base64url(signature).
Parameters
signedPayload
string
Returns
Promise<string>
Implementation of
JoseError
Defined in: @gramota/jose/dist/types.d.ts:41
Extends
GramotaError
Constructors
Constructor
new JoseError(
code: JoseErrorCode,
message: string,
options?: {
cause?: unknown;
}): JoseError;
Defined in: @gramota/jose/dist/types.d.ts:43
Parameters
code
message
string
options?
cause?
unknown
Returns
Overrides
GramotaError.constructor
Properties
cause?
readonly optional cause?: unknown;
Defined in: .pnpm/@gramota+core@0.2.1/node_modules/@gramota/core/dist/error.d.ts:44
Optional original error that caused this one. Always set when the
Gramota package is wrapping a thrown exception from a dependency
(Web Crypto, JOSE, fetch). Survives JSON.stringify(err) only via
the cause property — Node 16.9+ logs it natively.
Inherited from
GramotaError.cause
code
readonly code: JoseErrorCode;
Defined in: @gramota/jose/dist/types.d.ts:42
Stable string that identifies the failure mode. Subclasses narrow the type; at runtime it's always a string. Use for branching, logs, and metrics labels — never serialize GramotaError.message for that purpose, message strings drift across versions.
Overrides
GramotaError.code
name
name: string;
Defined in: .pnpm/typescript@5.9.3/node_modules/typescript/lib/lib.es5.d.ts:1076
Inherited from
GramotaError.name
message
message: string;
Defined in: .pnpm/typescript@5.9.3/node_modules/typescript/lib/lib.es5.d.ts:1077
Inherited from
GramotaError.message
stack?
optional stack?: string;
Defined in: .pnpm/typescript@5.9.3/node_modules/typescript/lib/lib.es5.d.ts:1078
Inherited from
GramotaError.stack
Interfaces
SignJwsOptions
Defined in: @gramota/jose/dist/sign.d.ts:2
Properties
alg
alg: SupportedAlg;
Defined in: @gramota/jose/dist/sign.d.ts:4
Algorithm to use. Must be in the supported set.
typ?
optional typ?: string;
Defined in: @gramota/jose/dist/sign.d.ts:6
Optional typ JOSE header (e.g. "kb+jwt", "vc+sd-jwt").
kid?
optional kid?: string;
Defined in: @gramota/jose/dist/sign.d.ts:8
Optional kid JOSE header.
extraHeader?
optional extraHeader?: Record<string, unknown>;
Defined in: @gramota/jose/dist/sign.d.ts:10
Additional protected header parameters. Cannot override alg/typ/kid.
Signer
Defined in: @gramota/jose/dist/signer.d.ts:38
A pluggable signing strategy.
sign() takes the JWS-canonical "header.payload" string (two
base64url-encoded segments joined by a dot) and returns just the
base64url-encoded signature segment. This shape matches what
@gramota/sd-jwt's issueSdJwt signer callback expects, so a
Signer instance can drop in as that callback via signer.sign.
Implementations are expected to be stateless from the caller's
perspective — concurrent sign() calls must not interfere.
Properties
publicKey
readonly publicKey: JsonWebKey;
Defined in: @gramota/jose/dist/signer.d.ts:41
Public counterpart — verifiers use this. Must always be extractable
(not in an HSM), since it's needed downstream for cnf.jwk etc.
alg
readonly alg: SupportedAlg;
Defined in: @gramota/jose/dist/signer.d.ts:44
JWS algorithm this signer produces. Must match publicKey's
algorithm capabilities (e.g. ES256 with a P-256 EC key).
Methods
sign()
sign(signedPayload: string): Promise<string>;
Defined in: @gramota/jose/dist/signer.d.ts:46
Sign a "header.payload" string, return base64url(signature).
Parameters
signedPayload
string
Returns
Promise<string>
JwkSignerOptions
Defined in: @gramota/jose/dist/signer.d.ts:48
Properties
publicKey
publicKey: JsonWebKey;
Defined in: @gramota/jose/dist/signer.d.ts:50
Public counterpart — also used to bind the signer to its alg.
privateKey
privateKey: JsonWebKey;
Defined in: @gramota/jose/dist/signer.d.ts:53
Private JWK. Held in memory — see file header. Not for production
secret-material storage; use WebAuthnSigner/HsmSigner there.
alg
alg: SupportedAlg;
Defined in: @gramota/jose/dist/signer.d.ts:55
JWS alg.
JsonWebKey
Defined in: @gramota/jose/dist/types.d.ts:3
JSON Web Key (RFC 7517). Minimum fields by key type.
Indexable
[key: string]: unknown
Properties
kty
kty: "RSA" | "EC" | "OKP" | "oct";
Defined in: @gramota/jose/dist/types.d.ts:4
alg?
optional alg?: string;
Defined in: @gramota/jose/dist/types.d.ts:5
kid?
optional kid?: string;
Defined in: @gramota/jose/dist/types.d.ts:6
use?
optional use?: string;
Defined in: @gramota/jose/dist/types.d.ts:7
n?
optional n?: string;
Defined in: @gramota/jose/dist/types.d.ts:8
e?
optional e?: string;
Defined in: @gramota/jose/dist/types.d.ts:9
d?
optional d?: string;
Defined in: @gramota/jose/dist/types.d.ts:10
p?
optional p?: string;
Defined in: @gramota/jose/dist/types.d.ts:11
q?
optional q?: string;
Defined in: @gramota/jose/dist/types.d.ts:12
dp?
optional dp?: string;
Defined in: @gramota/jose/dist/types.d.ts:13
dq?
optional dq?: string;
Defined in: @gramota/jose/dist/types.d.ts:14
qi?
optional qi?: string;
Defined in: @gramota/jose/dist/types.d.ts:15
crv?
optional crv?: string;
Defined in: @gramota/jose/dist/types.d.ts:16
x?
optional x?: string;
Defined in: @gramota/jose/dist/types.d.ts:17
y?
optional y?: string;
Defined in: @gramota/jose/dist/types.d.ts:18
k?
optional k?: string;
Defined in: @gramota/jose/dist/types.d.ts:19
VerifyJwsOptions
Defined in: @gramota/jose/dist/types.d.ts:24
Extended by
Properties
algorithms?
optional algorithms?: readonly SupportedAlg[];
Defined in: @gramota/jose/dist/types.d.ts:26
Algorithm allowlist. Defaults to all supported algorithms above.
VerifiedJws
Defined in: @gramota/jose/dist/types.d.ts:28
Extended by
Properties
header
header: {
[key: string]: unknown;
alg: string;
};
Defined in: @gramota/jose/dist/types.d.ts:30
Decoded JWS protected header.
Index Signature
[key: string]: unknown
alg
alg: string;
payload
payload: Record<string, unknown>;
Defined in: @gramota/jose/dist/types.d.ts:35
Decoded JWS payload (parsed as JSON).
alg
alg: SupportedAlg;
Defined in: @gramota/jose/dist/types.d.ts:37
The exact algorithm that verified successfully.
VerifyJwsX5cOptions
Defined in: @gramota/jose/dist/verify-x5c.d.ts:3
Extends
Properties
algorithms?
optional algorithms?: readonly SupportedAlg[];
Defined in: @gramota/jose/dist/types.d.ts:26
Algorithm allowlist. Defaults to all supported algorithms above.
Inherited from
trustAnchors?
optional trustAnchors?: readonly string[];
Defined in: @gramota/jose/dist/verify-x5c.d.ts:6
Optional cert-chain validation. When provided, the chain is verified against the supplied trust anchors before signature verification.
now?
optional now?: Date;
Defined in: @gramota/jose/dist/verify-x5c.d.ts:8
Override "now" — for chain-validity tests.
VerifiedJwsWithX5c
Defined in: @gramota/jose/dist/verify-x5c.d.ts:10
Extends
Properties
header
header: {
[key: string]: unknown;
alg: string;
};
Defined in: @gramota/jose/dist/types.d.ts:30
Decoded JWS protected header.
Index Signature
[key: string]: unknown
alg
alg: string;
Inherited from
payload
payload: Record<string, unknown>;
Defined in: @gramota/jose/dist/types.d.ts:35
Decoded JWS payload (parsed as JSON).
Inherited from
alg
alg: SupportedAlg;
Defined in: @gramota/jose/dist/types.d.ts:37
The exact algorithm that verified successfully.
Inherited from
chain?
optional chain?: ChainValidationResult;
Defined in: @gramota/jose/dist/verify-x5c.d.ts:12
Set when trustAnchors were supplied and the chain validated.
ChainValidationOptions
Defined in: @gramota/jose/dist/x5c.d.ts:19
Properties
trustAnchors
trustAnchors: readonly string[];
Defined in: @gramota/jose/dist/x5c.d.ts:21
PEM-encoded trust anchor certificates the chain must lead to.
now?
optional now?: Date;
Defined in: @gramota/jose/dist/x5c.d.ts:23
Override "now" — useful for tests.
ChainValidationResult
Defined in: @gramota/jose/dist/x5c.d.ts:25
Properties
leaf
leaf: X509Certificate;
Defined in: @gramota/jose/dist/x5c.d.ts:27
The leaf (x5c[0]) certificate, parsed.
chain
chain: readonly X509Certificate[];
Defined in: @gramota/jose/dist/x5c.d.ts:29
Every certificate in the chain, in x5c order.
anchor
anchor: X509Certificate;
Defined in: @gramota/jose/dist/x5c.d.ts:31
The trust anchor that ultimately validated the chain.
Type Aliases
SupportedAlg
type SupportedAlg =
| "ES256"
| "ES384"
| "ES512"
| "EdDSA"
| "RS256"
| "RS384"
| "RS512"
| "PS256"
| "PS384"
| "PS512";
Defined in: @gramota/jose/dist/types.d.ts:23
Algorithms we accept by default. alg: "none" is never permitted.
JoseErrorCode
type JoseErrorCode =
| "jose.invalid_input"
| "jose.malformed_jws"
| "jose.malformed_header"
| "jose.malformed_payload"
| "jose.malformed_signature"
| "jose.alg_missing"
| "jose.alg_none_disallowed"
| "jose.alg_not_allowed"
| "jose.signature_invalid"
| "jose.key_import_failed"
| "jose.signing_failed"
| "jose.x5c_missing"
| "jose.x5c_empty"
| "jose.x5c_parse_failed"
| "jose.x5c_chain_invalid"
| "jose.x5c_no_trust_anchor";
Defined in: @gramota/jose/dist/types.d.ts:40
Stable machine-readable error codes for JoseError.
Functions
makeSigner()
function makeSigner(privateKey: JsonWebKey, alg: SupportedAlg): Promise<(signedPayload: string) => Promise<string>>;
Defined in: @gramota/jose/dist/make-signer.d.ts:10
Build a signer compatible with @gramota/sd-jwt's issueSdJwt signer
field — takes header.payload and returns just the base64url signature.
This is the bridge between @gramota/jose (which signs full JWS strings) and @gramota/sd-jwt (which composes its own header/payload and only needs the signature back).
Parameters
privateKey
alg
Returns
Promise<(signedPayload: string) => Promise<string>>
signJws()
function signJws(
payload: Record<string, unknown>,
privateKey: JsonWebKey,
options: SignJwsOptions): Promise<string>;
Defined in: @gramota/jose/dist/sign.d.ts:21
Sign a payload as a compact-serialised JWS.
Hard rules:
alg=noneis impossible to request via this API: SupportedAlg never contains "none".- The provided
algis set in the protected header —josewill refuse to sign if the JWK can't perform that algorithm, so a typo is loud.
Parameters
payload
Record<string, unknown>
privateKey
options
Returns
Promise<string>
asSigner()
function asSigner(input:
| Signer
| {
publicKey: JsonWebKey;
privateKey: JsonWebKey;
alg: SupportedAlg;
}): Signer;
Defined in: @gramota/jose/dist/signer.d.ts:80
Promote a raw JWK config to a Signer, or pass through an existing one.
Used by orchestrators (Holder, Issuer, Oid4vciClient) to accept either form on their config and normalize internally. Stripe-style "shorthand" pattern — ergonomic for tests/dev, principled for prod.
Parameters
input
| Signer
| {
publicKey: JsonWebKey;
privateKey: JsonWebKey;
alg: SupportedAlg;
}
Returns
computeJwkThumbprint()
function computeJwkThumbprint(jwk: JsonWebKey): string;
Defined in: @gramota/jose/dist/thumbprint.d.ts:30
Compute the SHA-256 JWK Thumbprint per RFC 7638.
computeJwkThumbprint({ kty: "EC", crv: "P-256", x: "...", y: "..." }) → "G7B0w8...22 chars total"
Returns the thumbprint in base64url encoding (the canonical form
referenced by jkt claims). Throws JoseError
with jose.invalid_input for unsupported kty values or missing
required members.
Parameters
jwk
Returns
string
verifyJwsWithX5c()
function verifyJwsWithX5c(jws: string, options?: VerifyJwsX5cOptions): Promise<VerifiedJwsWithX5c>;
Defined in: @gramota/jose/dist/verify-x5c.d.ts:35
Verify a JWS where the public key is supplied via the x5c JOSE header
(RFC 7515 §4.1.6) — common for OID4VP authorization requests under the
EUDI HAIP profile and any deployment using x509 trust.
Two modes:
- Signature only — extract key from x5c[0], verify the JWS. This proves cryptographic integrity but NOT that you trust the issuer.
- With trust anchors — additionally validate the cert chain leads
to one of
options.trustAnchors. This proves authenticity to the extent your trust anchors are correct.
Errors:
jose.x5c_missing— header has no x5cjose.x5c_empty— x5c is an empty arrayjose.x5c_parse_failed— a cert in x5c is malformedjose.x5c_chain_invalid— chain check failed (validity, signature)jose.x5c_no_trust_anchor— last cert doesn't lead to a trusted rootjose.signature_invalid— JWS signature didn't verify against x5c[0]- all the standard
jose.*codes fromverifyJws
Parameters
jws
string
options?
Returns
Promise<VerifiedJwsWithX5c>
verifyJws()
function verifyJws(
jws: string,
publicKey: JsonWebKey,
options?: VerifyJwsOptions): Promise<VerifiedJws>;
Defined in: @gramota/jose/dist/verify.d.ts:30
Verify a compact-serialised JWS against a public JWK.
Hard rules (enforced before any crypto runs, so attackers can't smuggle past via a body the crypto library accepts despite a malformed header):
alg=noneis rejected unconditionally, regardless of the caller's allowlist.- The header
algMUST appear inoptions.algorithms(defaults to every IETF JOSE asymmetric algorithm). - The payload MUST decode to a JSON object — bare strings / arrays are rejected.
Parameters
jws
string
publicKey
options?
Returns
Promise<VerifiedJws>
Example
const { header, payload, alg } = await verifyJws(jws, issuerJwk, {
algorithms: ["ES256"], // narrow the allowlist
});
console.log(payload.iss);
Throws
JoseError with stable codes:
jose.invalid_input— empty / non-string JWSjose.malformed_jws,jose.malformed_header— pre-flight parse failuresjose.alg_missing,jose.alg_none_disallowed,jose.alg_not_allowedjose.key_import_failed— JWK couldn't be imported for this algjose.signature_invalid— cryptographic verification failedjose.malformed_payload— payload isn't a JSON object
x5cToPem()
function x5cToPem(x5cEntry: string): string;
Defined in: @gramota/jose/dist/x5c.d.ts:9
Convert a single x5c entry to PEM format.
Per RFC 7515 §4.1.6, x5c entries are base64-encoded (NOT base64url) DER certificates. We just wrap them in standard PEM headers.
Parameters
x5cEntry
string
Returns
string
parseX5cEntry()
function parseX5cEntry(x5cEntry: string): X509Certificate;
Defined in: @gramota/jose/dist/x5c.d.ts:11
Parse a single x5c entry as an X.509 certificate.
Parameters
x5cEntry
string
Returns
X509Certificate
extractPublicKeyFromX5c()
function extractPublicKeyFromX5c(x5c: readonly string[]): JsonWebKey;
Defined in: @gramota/jose/dist/x5c.d.ts:18
Extract the public JWK from x5c[0] (the leaf signing certificate).
This produces a JWK suitable for passing to verifyJws. It does not
validate the chain or the cert's trust — use validateX5cChain for that.
Parameters
x5c
readonly string[]
Returns
validateX5cChain()
function validateX5cChain(x5c: readonly string[], options: ChainValidationOptions): ChainValidationResult;
Defined in: @gramota/jose/dist/x5c.d.ts:47
Validate an x5c chain against trust anchors.
Rules enforced:
- Every cert in
x5cis currently within its validity window. - Each cert is cryptographically signed by the next in
x5c. - The last cert in
x5cis signed by (or equal to) one oftrustAnchors.
Throws JoseError with code: "jose.x5c_chain_invalid" or
"jose.x5c_no_trust_anchor" if validation fails.
Returns the leaf certificate (for further inspection) and the trust anchor that validated the chain.
Parameters
x5c
readonly string[]