Relevant source files
The following files were used as context for generating this wiki page:
- src/utils/jwt/jwt.ts
- src/utils/jwt/jws.ts
- src/middleware/jwk/jwk.ts
- src/middleware/secure-headers/secure-headers.ts
- src/middleware/jwk/keys.test.json
- src/middleware/jwt/jwt.ts
- src/middleware/bearer-auth/index.ts
- src/middleware/cache/index.ts
- src/middleware/basic-auth/index.ts
- src/middleware/csrf/index.ts
- src/middleware/etag/index.ts
- src/middleware/ip-restriction/index.ts
- src/helper/cookie/index.ts
- src/utils/jwt/types.ts
- src/request.ts
- src/adapter/lambda-edge/handler.ts
- src/middleware/jwk/index.ts
- src/utils/crypto.ts
- src/middleware/language/language.ts
- src/utils/jwt/index.ts
- src/client/fetch-result-please.ts
- src/middleware/jwt/index.ts
- package.json
- src/context.ts
- src/utils/cookie.ts
- src/utils/jwt/jwa.ts
- src/utils/jwt/utf8.ts
- jsr.json
- src/router/linear-router/index.ts
- src/helper/accepts/index.ts
Overview
Hono provides native JSON Web Token (JWT) and JSON Web Key (JWK) utilities alongside dedicated middleware handlers to authenticate HTTP requests securely across Web Standard runtimes. By leveraging crypto.subtle, the implementation handles cryptographic signing, signature verification, and dynamic key resolution without external cryptographic dependencies.
Sources: src/utils/jwt/jwt.ts:1-29, src/utils/jwt/jws.ts:1-12, src/middleware/jwt/jwt.ts:1-20, src/middleware/jwk/jwk.ts:1-16
Token Signing and Key Resolution
Signing Workflow and Serialisation
Token generation begins by serialising the payload and header objects into Base64Url-encoded strings without padding. The sign function first encodes the JWTPayload object using encodeJwtPart, which converts the JSON-serialised payload into UTF-8 bytes and transforms it via encodeBase64Url, stripping any trailing = characters.
const encodeJwtPart = (part: unknown): string =>
encodeBase64Url(utf8Encoder.encode(JSON.stringify(part)).buffer).replace(/=/g, '')
const encodeSignaturePart = (buf: ArrayBufferLike): string => encodeBase64Url(buf).replace(/=/g, '')Sources: src/utils/jwt/jwt.ts:31-34
Next, the header is constructed. If the supplied privateKey is an object containing an alg property, the algorithm and optional kid (Key ID) are extracted into the header. Otherwise, the provided algorithm parameter (defaulting to 'HS256') is used alongside typ: 'JWT'. The header and payload are concatenated with a dot delimiter (${encodedHeader}.${encodedPayload}) to form the partial token.
export const sign = async (
payload: JWTPayload,
privateKey: SignatureKey,
alg: SignatureAlgorithm = 'HS256'
): Promise<string> => {
const encodedPayload = encodeJwtPart(payload)
let encodedHeader
if (typeof privateKey === 'object' && 'alg' in privateKey) {
alg = privateKey.alg as SignatureAlgorithm
encodedHeader = encodeJwtPart({ alg, typ: 'JWT', kid: privateKey.kid })
} else {
encodedHeader = encodeJwtPart({ alg, typ: 'JWT' })
}
const partialToken = `${encodedHeader}.${encodedPayload}`
const signaturePart = await signing(privateKey, alg, utf8Encoder.encode(partialToken))
const signature = encodeSignaturePart(signaturePart)
return `${partialToken}.${signature}`
}Sources: src/utils/jwt/jwt.ts:56-76
Call-Chain Execution Walkthrough
When sign invokes the signing routine, execution flows through the JWS and Web Crypto resolution layers.
signinsrc/utils/jwt/jwt.tsinvokessigningwith the private key, algorithm identifier, and encoded partial token bytes.signinginsrc/utils/jwt/jws.tscallsgetKeyAlgorithm(alg)to map the string algorithm to a Web CryptoKeyAlgorithmparameter structure.getKeyAlgorithminspects the algorithm name; if an unsupported identifier is supplied, it falls through or throwsJwtAlgorithmNotImplemented.signingthen callsimportPrivateKey(privateKey, algorithm)to prepare the cryptographic key instance.crypto.subtle.signexecutes against the importedCryptoKeyto produce the raw signature buffer, which is subsequently encoded and appended to the token string.
Sources: src/utils/jwt/jwt.ts:72-72, src/utils/jwt/jws.ts:29-37, src/utils/jwt/jws.ts:122-179, src/utils/jwt/types.ts:6-11
Web Crypto Key Resolution
The importPrivateKey function handles diverse key representations, including raw strings, PEM-encoded structures, JWK objects, and existing CryptoKey instances. It ensures that the resulting key is assigned the CryptoKeyUsage.Sign usage flag.
async function importPrivateKey(key: SignatureKey, alg: KeyImporterAlgorithm): Promise<CryptoKey> {
if (!crypto.subtle || !crypto.subtle.importKey) {
throw new Error('`crypto.subtle.importKey` is undefined. JWT auth middleware requires it.')
}
if (isCryptoKey(key)) {
if (key.type !== 'private' && key.type !== 'secret') {
throw new Error(
`unexpected key type: CryptoKey.type is ${key.type}, expected private or secret`
)
}
return key
}
const usages = [CryptoKeyUsage.Sign]
if (typeof key === 'object') {
return await crypto.subtle.importKey('jwk', key, alg, false, usages)
}
if (key.includes('PRIVATE')) {
return await crypto.subtle.importKey('pkcs8', pemToBinary(key), alg, false, usages)
}
return await crypto.subtle.importKey('raw', utf8Encoder.encode(key), alg, false, usages)
}Sources: src/utils/jwt/jws.ts:54-76
Warning
When passing an existing CryptoKey to importPrivateKey, its type property must be either 'private' or 'secret'. Passing a public key where a private or secret key is required causes an immediate error.
Sources: src/utils/jwt/jws.ts:58-64
Token Verification and Claims Validation
Claims Verification Options
Token validation begins with structural decoding and signature verification, separating cryptographic checks from payload claims assertions. The decode function splits the token by periods, verifying that exactly three parts exist before parsing the header and payload components.
Sources: src/utils/jwt/jwt.ts:264-279
export const decode = (token: string): { header: TokenHeader; payload: JWTPayload } => {
const parts = token.split('.')
if (parts.length !== 3) {
throw new JwtTokenInvalid(token)
}
try {
const header = decodeJwtPart(parts[0]) as TokenHeader
const payload = decodeJwtPart(parts[1]) as JWTPayload
return {
header,
payload,
}
} catch {
throw new JwtTokenInvalid(token)
}
}Sources: src/utils/jwt/jwt.ts:264-279
The VerifyOptions type configures how standard registered claims are validated against the current system time and expected constraints during token verification.
Sources: src/utils/jwt/jwt.ts:78-94
| Option | Type | Default | Purpose |
|---|---|---|---|
iss | string | RegExp | — | Expected issuer used for verifying the iss claim |
nbf | boolean | true | Verify that the current timestamp is after or equal to the nbf claim |
exp | boolean | true | Verify that the current timestamp is before the exp claim |
iat | boolean | true | Verify that the iat claim is a valid number not in the future |
aud | string | string[] | RegExp | — | Expected audience used for verifying the aud claim |
alg | SignatureAlgorithm | — | Required signature algorithm for verification |
Sources: src/utils/jwt/jwt.ts:78-94
Note
If the alg option is omitted during verification, the verifier immediately throws a JwtAlgorithmRequired error to prevent algorithm substitution vulnerabilities.
Sources: src/utils/jwt/jwt.ts:99-101
JSON Web Key Set Middleware
JWKS Retrieval and Key Matching
The verifyWithJwks function retrieves and validates JSON Web Key Sets (JWKS) to verify incoming tokens without pre-configured static keys. It first decodes the token header and checks for a valid header structure and a mandatory kid (Key ID) property. If the header lacks a kid, JwtHeaderRequiresKid is thrown.
Sources: src/utils/jwt/jwt.ts:209-216
export const verifyWithJwks = async (
token: string,
options: {
keys?: HonoJsonWebKey[]
jwks_uri?: string
verification?: VerifyOptions
allowedAlgorithms: readonly AsymmetricAlgorithm[]
},
init?: RequestInit
): Promise<JWTPayload> => {
const verifyOpts = options.verification || {}
const header = decodeHeader(token)
if (!isTokenHeader(header)) {
throw new JwtHeaderInvalid(header)
}
if (!header.kid) {
throw new JwtHeaderRequiresKid(header)
}Sources: src/utils/jwt/jwt.ts:197-216
Warning
Symmetric algorithms such as HS256, HS384, and HS512 are explicitly prohibited during JWKS verification. Any token header using a symmetric algorithm causes verifyWithJwks to throw a JwtSymmetricAlgorithmNotAllowed error to prevent algorithm confusion attacks.
Sources: src/utils/jwt/jwt.ts:190-221
// Reject symmetric algorithms (HS256, HS384, HS512) to prevent algorithm confusion attacks
if (symmetricAlgorithms.includes(header.alg as SymmetricAlgorithm)) {
throw new JwtSymmetricAlgorithmNotAllowed(header.alg)
}
// Validate against allowed algorithms
if (!options.allowedAlgorithms.includes(header.alg as AsymmetricAlgorithm)) {
throw new JwtAlgorithmNotAllowed(header.alg, options.allowedAlgorithms)
}Sources: src/utils/jwt/jwt.ts:218-226
When an options.jwks_uri is supplied, verifyWithJwks fetches the URI using the optional RequestInit init argument, parses the JSON response, and ensures that a keys array exists. These fetched keys are merged with any statically provided options.keys. The verifier then finds the specific key where key.kid === header.kid.
Sources: src/utils/jwt/jwt.ts:228-251
let verifyKeys = options.keys ? [...options.keys] : undefined
if (options.jwks_uri) {
const response = await fetch(options.jwks_uri, init)
if (!response.ok) {
throw new Error(`failed to fetch JWKS from ${options.jwks_uri}`)
}
const data = (await response.json()) as { keys?: JsonWebKey[] }
if (!data.keys) {
throw new Error('invalid JWKS response. "keys" field is missing')
}
if (!Array.isArray(data.keys)) {
throw new Error('invalid JWKS response. "keys" field is not an array')
}
verifyKeys ??= []
verifyKeys.push(...(data.keys as HonoJsonWebKey[]))
} else if (!verifyKeys) {
throw new Error('verifyWithJwks requires options for either "keys" or "jwks_uri" or both')
}
const matchingKey = verifyKeys.find((key) => key.kid === header.kid)
if (!matchingKey) {
throw new JwtTokenInvalid(token)
}
// Verify that JWK's alg matches JWT header's alg when JWK has alg field
if (matchingKey.alg && matchingKey.alg !== header.alg) {
throw new JwtAlgorithmMismatch(matchingKey.alg, header.alg)
}
return await verify(token, matchingKey, {
alg: header.alg,
...verifyOpts,
})
}Sources: src/utils/jwt/jwt.ts:228-262
Verification Execution and Call Chain
The execution flow for JWKS-based verification proceeds through structural header inspection and key resolution.
verifyWithJwks— Entry point for verifying tokens against local keys or a remote JWKS endpoint. Sources: src/utils/jwt/jwt.ts:197-210decodeHeader— Splits the token by periods and decodes the first segment into aTokenHeaderobject. Sources: src/utils/jwt/jwt.ts:281-290JwtTokenInvalid— Thrown if the token does not contain exactly three parts or if part decoding fails. Sources: src/utils/jwt/jwt.ts:265-268, src/utils/jwt/jwt.ts:283-289
Sources: src/utils/jwt/jwt.ts:209-210, src/utils/jwt/jwt.ts:281-290
Note
If a matching JWK defines an explicit alg property, it must strictly match the alg declared in the JWT header. A mismatch causes a JwtAlgorithmMismatch exception.
Sources: src/utils/jwt/jwt.ts:253-256
JWT Authentication Middleware Flow
Credential Extraction and Token Resolution
The jwt middleware intercepts incoming HTTP requests to locate and validate JSON Web Tokens from configured request headers or cookies. It first checks the request headers using options.headerName (defaulting to 'Authorization'). When credentials are found in the header, the middleware splits the string by whitespace and expects exactly two parts where the first segment case-insensitively equals 'bearer'. If the credential structure is invalid, it throws an HTTPException with status code 401.
Sources: src/middleware/jwt/jwt.ts:76-95
return async function jwt(ctx, next) {
const headerName = options.headerName || 'Authorization'
const credentials = ctx.req.raw.headers.get(headerName)
let token
if (credentials) {
const parts = credentials.split(/\s+/)
if (parts.length !== 2 || parts[0].toLowerCase() !== 'bearer') {
const errDescription = 'invalid credentials structure'
throw new HTTPException(401, {
message: errDescription,
res: unauthorizedResponse({
ctx,
error: 'invalid_request',
errDescription,
}),
})
} else {
token = parts[1]
}Sources: src/middleware/jwt/jwt.ts:76-95
If no header credentials exist, the middleware evaluates options.cookie. The cookie option can be provided as a plain string key or an object containing key, an optional secret for signed cookies, and optional prefixOptions. Depending on these options, the token is extracted using getCookie or getSignedCookie.
Sources: src/middleware/jwt/jwt.ts:96-117
} else if (options.cookie) {
if (typeof options.cookie == 'string') {
token = getCookie(ctx, options.cookie)
} else if (options.cookie.secret) {
if (options.cookie.prefixOptions) {
token = await getSignedCookie(
ctx,
options.cookie.secret,
options.cookie.key,
options.cookie.prefixOptions
)
} else {
token = await getSignedCookie(ctx, options.cookie.secret, options.cookie.key)
}
} else {
if (options.cookie.prefixOptions) {
token = getCookie(ctx, options.cookie.key, options.cookie.prefixOptions)
} else {
token = getCookie(ctx, options.cookie.key)
}
}
}Sources: src/middleware/jwt/jwt.ts:96-117
Warning
If neither header credentials nor a valid cookie yields a token string, the middleware immediately rejects the request with a 401 HTTP exception and an invalid_request error description.
Sources: src/middleware/jwt/jwt.ts:119-129
if (!token) {
const errDescription = 'no authorization included in request'
throw new HTTPException(401, {
message: errDescription,
res: unauthorizedResponse({
ctx,
error: 'invalid_request',
errDescription,
}),
})
}Sources: src/middleware/jwt/jwt.ts:119-129
Verification, Context Injection, and 401 Responses
Once a token is extracted, the middleware invokes Jwt.verify with the configured secret, alg, and verification options. If verification fails or throws an error, a 401 exception is raised with an invalid_token error and a WWW-Authenticate challenge header. Upon successful verification, the decoded payload is stored in the execution context under the key 'jwtPayload', and control proceeds to the next handler via await next().
Sources: src/middleware/jwt/jwt.ts:131-157
let payload
let cause
try {
payload = await Jwt.verify(token, options.secret, {
alg: options.alg,
...verifyOpts,
})
} catch (e) {
cause = e
}
if (!payload) {
throw new HTTPException(401, {
message: 'Unauthorized',
res: unauthorizedResponse({
ctx,
error: 'invalid_token',
statusText: 'Unauthorized',
errDescription: 'token verification failure',
}),
cause,
})
}
ctx.set('jwtPayload', payload)
await next()
}Sources: src/middleware/jwt/jwt.ts:131-157
Unauthorized responses are constructed via unauthorizedResponse, which formats a 401 status response carrying a WWW-Authenticate header containing the request URL, error type, and error description.
Sources: src/middleware/jwt/jwt.ts:160-173
function unauthorizedResponse(opts: {
ctx: Context
error: string
errDescription: string
statusText?: string
}) {
return new Response('Unauthorized', {
status: 401,
statusText: opts.statusText,
headers: {
'WWW-Authenticate': `Bearer realm="${opts.ctx.req.url}",error="${opts.error}",error_description="${opts.errDescription}"`,
},
})
}Sources: src/middleware/jwt/jwt.ts:160-173
Supported Cryptographic Algorithms and Errors
Supported JWA Signature Algorithms and Web Crypto Mappings
JSON Web Algorithms (JWA) are mapped to underlying Web Crypto API operations via getKeyAlgorithm in the JWS utility module. Supported algorithms encompass HMAC-SHA variants (HS256, HS384, HS512), RSA signatures using PKCS#1 v1.5 (RS256, RS384, RS512), RSA Probabilistic Signature Scheme (PS256, PS384, PS512), and Elliptic Curve Digital Signature Algorithm variants (ES256, ES384, ES512) alongside EdDSA.
| Algorithm | JWA Category | Web Crypto Algorithm Name | Hash Function | Additional Parameters |
|---|---|---|---|---|
HS256 | Symmetric | HMAC | SHA-256 | — |
HS384 | Symmetric | HMAC | SHA-384 | — |
HS512 | Symmetric | HMAC | SHA-512 | — |
RS256 | Asymmetric | RSASSA-PKCS1-v1_5 | SHA-256 | — |
RS384 | Asymmetric | RSASSA-PKCS1-v1_5 | SHA-384 | — |
RS512 | Asymmetric | RSASSA-PKCS1-v1_5 | SHA-512 | — |
PS256 | Asymmetric | RSA-PSS | SHA-256 | saltLength: 32 |
PS384 | Asymmetric | RSA-PSS | SHA-384 | saltLength: 48 |
PS512 | Asymmetric | RSA-PSS | SHA-512 | saltLength: 64 |
ES256 | Asymmetric | ECDSA | SHA-256 | namedCurve: 'P-256' |
ES384 | Asymmetric | ECDSA | SHA-384 | namedCurve: 'P-384' |
ES512 | Asymmetric | ECDSA | SHA-512 | namedCurve: 'P-521' |
EdDSA | Asymmetric | Ed25519 | — | — |
Sources: src/utils/jwt/jwa.ts:7-21, src/utils/jwt/jws.ts:122-211
function getKeyAlgorithm(name: SignatureAlgorithm): KeyAlgorithm {
switch (name) {
case 'HS256':
return {
name: 'HMAC',
hash: {
name: 'SHA-256',
},
} satisfies HmacImportParams
case 'HS384':
return {
name: 'HMAC',
hash: {
name: 'SHA-384',
},
} satisfies HmacImportParams
case 'HS512':
return {
name: 'HMAC',
hash: {
name: 'SHA-512',
},
} satisfies HmacImportParams
case 'RS256':
return {
name: 'RSASSA-PKCS1-v1_5',
hash: {
name: 'SHA-256',
},
} satisfies RsaHashedImportParams
case 'RS384':
return {
name: 'RSASSA-PKCS1-v1_5',
hash: {
name: 'SHA-384',
},
} satisfies RsaHashedImportParams
case 'RS512':
return {
name: 'RSASSA-PKCS1-v1_5',
hash: {
name: 'SHA-512',
},
} satisfies RsaHashedImportParams
case 'PS256':
return {
name: 'RSA-PSS',
hash: {
name: 'SHA-256',
},
saltLength: 32, // 256 >> 3
} satisfies RsaPssParams & RsaHashedImportParams
case 'PS384':
return {
name: 'RSA-PSS',
hash: {
name: 'SHA-384',
},
saltLength: 48, // 384 >> 3
} satisfies RsaPssParams & RsaHashedImportParams
case 'PS512':
return {
name: 'RSA-PSS',
hash: {
name: 'SHA-512',
},
saltLength: 64, // 512 >> 3,
} satisfies RsaPssParams & RsaHashedImportParams
case 'ES256':
return {
name: 'ECDSA',
hash: {
name: 'SHA-256',
},
namedCurve: 'P-256',
} satisfies EcdsaParams & EcKeyImportParams
case 'ES384':
return {
name: 'ECDSA',
hash: {Sources: src/utils/jwt/jws.ts:122-211
Caution
For PS256, PS384, and PS512, the salt length is strictly derived from the hash bit length divided by eight (256 >> 3 yields 32, 384 >> 3 yields 48, and 512 >> 3 yields 64). Specifying a custom or non-standard salt length outside these Web Crypto mappings will cause signature verification or generation to fail.
Sources: src/utils/jwt/jws.ts:166-194
Custom Error Types
JWT parsing, validation, and cryptographic routines throw specialized error classes defined in src/utils/jwt/types.ts. Each error sets a descriptive message and assigns its name property to match the class identifier.
| Error Class | Constructor Arguments | Error Message Pattern |
|---|---|---|
JwtAlgorithmNotImplemented | alg: string | ${alg} is not an implemented algorithm |
JwtAlgorithmRequired | none | JWT verification requires "alg" option to be specified |
JwtAlgorithmMismatch | expected: string, actual: string | JWT algorithm mismatch: expected "${expected}", got "${actual}" |
JwtTokenInvalid | token: string | invalid JWT token: ${token} |
JwtTokenNotBefore | token: string | token (${token}) is being used before it's valid |
JwtTokenExpired | token: string | token (${token}) expired |
JwtTokenIssuedAt | currentTimestamp: number, iat: number | Invalid "iat" claim, must be a valid number lower than "${currentTimestamp}" (iat: "${iat}") |
JwtTokenIssuer | expected: string | RegExp, iss: string | null | expected issuer "${expected}", got ${iss ? "${iss}": 'none'} |
JwtHeaderInvalid | header: object | jwt header is invalid: ${JSON.stringify(header)} |
JwtHeaderRequiresKid | header: object | required "kid" in jwt header: ${JSON.stringify(header)} |
JwtSymmetricAlgorithmNotAllowed | alg: string | symmetric algorithm "${alg}" is not allowed for JWK verification |
JwtAlgorithmNotAllowed | alg: string, allowedAlgorithms: readonly string[] | algorithm "${alg}" is not in the allowed list: [...] |
JwtTokenSignatureMismatched | token: string | token(${token}) signature mismatched |
JwtPayloadRequiresAud | payload: object | required "aud" in jwt payload: ${JSON.stringify(payload)} |
JwtTokenAudience | expected: string | string[] | RegExp, aud: string | string[] | expected audience "${...}", got "${aud}" |
Sources: src/utils/jwt/types.ts:6-115
export class JwtAlgorithmNotImplemented extends Error {
constructor(alg: string) {
super(`${alg} is not an implemented algorithm`)
this.name = 'JwtAlgorithmNotImplemented'
}
}
export class JwtAlgorithmRequired extends Error {
constructor() {
super('JWT verification requires "alg" option to be specified')
this.name = 'JwtAlgorithmRequired'
}
}
export class JwtAlgorithmMismatch extends Error {
constructor(expected: string, actual: string) {
super(`JWT algorithm mismatch: expected "${expected}", got "${actual}"`)
this.name = 'JwtAlgorithmMismatch'
}
}
export class JwtTokenInvalid extends Error {
constructor(token: string) {
super(`invalid JWT token: ${token}`)
this.name = 'JwtTokenInvalid'
}
}
export class JwtTokenNotBefore extends Error {
constructor(token: string) {
super(`token (${token}) is being used before it's valid`)
this.name = 'JwtTokenNotBefore'
}
}
export class JwtTokenExpired extends Error {
constructor(token: string) {
super(`token (${token}) expired`)
this.name = 'JwtTokenExpired'
}
}
export class JwtTokenIssuedAt extends Error {
constructor(currentTimestamp: number, iat: number) {
super(
`Invalid "iat" claim, must be a valid number lower than "${currentTimestamp}" (iat: "${iat}")`
)
this.name = 'JwtTokenIssuedAt'
}
}
export class JwtTokenIssuer extends Error {
constructor(expected: string | RegExp, iss: string | null) {
super(`expected issuer "${expected}", got ${iss ? `"${iss}"` : 'none'} `)
this.name = 'JwtTokenIssuer'
}
}
export class JwtHeaderInvalid extends Error {
constructor(header: object) {
super(`jwt header is invalid: ${JSON.stringify(header)}`)
this.name = 'JwtHeaderInvalid'
}
}
export class JwtHeaderRequiresKid extends Error {
constructor(header: object) {
super(`required "kid" in jwt header: ${JSON.stringify(header)}`)
this.name = 'JwtHeaderRequiresKid'
}
}
export class JwtSymmetricAlgorithmNotAllowed extends Error {
constructor(alg: string) {
super(`symmetric algorithm "${alg}" is not allowed for JWK verification`)
this.name = 'JwtSymmetricAlgorithmNotAllowed'
}
}
export class JwtAlgorithmNotAllowed extends Error {Sources: src/utils/jwt/types.ts:6-115
Related
On This Page
OverviewToken Signing and Key ResolutionSigning Workflow and SerialisationCall-Chain Execution WalkthroughWeb Crypto Key ResolutionToken Verification and Claims ValidationClaims Verification OptionsJSON Web Key Set MiddlewareJWKS Retrieval and Key MatchingVerification Execution and Call ChainJWT Authentication Middleware FlowCredential Extraction and Token ResolutionVerification, Context Injection, and 401 ResponsesSupported Cryptographic Algorithms and ErrorsSupported JWA Signature Algorithms and Web Crypto MappingsCustom Error TypesRelated