@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

JwkSignerOptions

Returns

JwkSigner

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

Signer.publicKey

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

Signer.alg

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

Signer.sign


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

JoseErrorCode

message

string

options?
cause?

unknown

Returns

JoseError

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

VerifyJwsOptions.algorithms

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

VerifiedJws.header

payload
payload: Record<string, unknown>;

Defined in: @gramota/jose/dist/types.d.ts:35

Decoded JWS payload (parsed as JSON).

Inherited from

VerifiedJws.payload

alg
alg: SupportedAlg;

Defined in: @gramota/jose/dist/types.d.ts:37

The exact algorithm that verified successfully.

Inherited from

VerifiedJws.alg

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

JsonWebKey

alg

SupportedAlg

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=none is impossible to request via this API: SupportedAlg never contains "none".
  • The provided alg is set in the protected header — jose will refuse to sign if the JWK can't perform that algorithm, so a typo is loud.

Parameters

payload

Record<string, unknown>

privateKey

JsonWebKey

options

SignJwsOptions

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

Signer


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

JsonWebKey

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 x5c
  • jose.x5c_empty — x5c is an empty array
  • jose.x5c_parse_failed — a cert in x5c is malformed
  • jose.x5c_chain_invalid — chain check failed (validity, signature)
  • jose.x5c_no_trust_anchor — last cert doesn't lead to a trusted root
  • jose.signature_invalid — JWS signature didn't verify against x5c[0]
  • all the standard jose.* codes from verifyJws

Parameters

jws

string

options?

VerifyJwsX5cOptions

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=none is rejected unconditionally, regardless of the caller's allowlist.
  • The header alg MUST appear in options.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

JsonWebKey

options?

VerifyJwsOptions

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 JWS
  • jose.malformed_jws, jose.malformed_header — pre-flight parse failures
  • jose.alg_missing, jose.alg_none_disallowed, jose.alg_not_allowed
  • jose.key_import_failed — JWK couldn't be imported for this alg
  • jose.signature_invalid — cryptographic verification failed
  • jose.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

JsonWebKey


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:

  1. Every cert in x5c is currently within its validity window.
  2. Each cert is cryptographically signed by the next in x5c.
  3. The last cert in x5c is signed by (or equal to) one of trustAnchors.

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[]

options

ChainValidationOptions

Returns

ChainValidationResult