honojs/hono · error · Error
`crypto.subtle.importKey` is undefined. JWK auth middleware
Error message
`crypto.subtle.importKey` is undefined. JWK auth middleware requires it.
What it means
The JWK middleware verifies tokens with WebCrypto (crypto.subtle.importKey). If the runtime's global crypto.subtle is absent — typically because the page/worker is not in a secure context (not HTTPS/localhost) or the runtime lacks WebCrypto — the middleware refuses to initialize.
Source
Thrown at src/middleware/jwk/jwk.ts:75
headerName?: string
alg: AsymmetricAlgorithm[]
realm?: string
verification?: VerifyOptions
},
init?: RequestInit
): MiddlewareHandler => {
const verifyOpts = options.verification || {}
if (!options || !(options.keys || options.jwks_uri)) {
throw new Error('JWK auth middleware requires options for either "keys" or "jwks_uri" or both')
}
if (!crypto.subtle || !crypto.subtle.importKey) {
throw new Error('`crypto.subtle.importKey` is undefined. JWK auth middleware requires it.')
}
return async function jwk(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,
realm: options.realm,View on GitHub (pinned to e2740d5a1b)
Solutions
- Serve the app over HTTPS, or use http://localhost / http://127.0.0.1 which are secure contexts
- On Node <18, upgrade to Node 18+ where crypto.subtle is global; alternatively polyfill via node:crypto's webcrypto export
- In test environments, polyfill crypto.subtle (e.g. import 'crypto' webcrypto assignment) before importing the middleware
- Verify with a quick runtime check: typeof crypto !== 'undefined' && !!crypto.subtle
Example fix
// before (Node 16, crypto.subtle undefined)
// after: run on Node 18+, or add:
import { webcrypto } from 'node:crypto'
if (!globalThis.crypto?.subtle) (globalThis as any).crypto = webcrypto Defensive patterns
Strategy: validation
Validate before calling
const hasWebCrypto = (): boolean =>
typeof crypto !== 'undefined' && !!crypto.subtle && typeof crypto.subtle.importKey === 'function'
if (!hasWebCrypto()) { /* polyfill or refuse to start */ } Type guard
const supportsJwkRuntime = (): boolean => typeof crypto !== 'undefined' && typeof crypto.subtle?.importKey === 'function'
Try / catch
try { app.use('/api/*', jwk({ jwks_uri })) } catch (e) { if (e instanceof Error && e.message.includes('crypto.subtle')) { throw new Error('Serve over HTTPS/localhost or polyfill webcrypto') } throw e } Prevention
- Run on Node 18+ or polyfill node:crypto webcrypto
- Use HTTPS or localhost in dev; non-localhost plain HTTP is not a secure context
- Include a crypto.subtle availability check in health checks for restricted runtimes
When it happens
Trigger: Running the app in a plain-HTTP non-localhost environment (secure context required for crypto.subtle), an older Node without WebCrypto (pre-18 or without node:crypto webcrypto shimming), or a browser/worker sandbox where crypto.subtle is undefined.
Common situations: Local testing over http:// on a LAN IP or a container accessed by IP; deploying behind a TLS-terminating proxy to a non-secure origin; using an embedded JS engine or old runtime without WebCrypto; certain edge/testing setups (jsdom-based tests) that stub crypto incompletely.
Related errors
- JWK auth middleware requires options for either "keys" or "j
- invalid credentials structure
- no authorization included in request
- Unauthorized
AI-assisted analysis of honojs/hono@e2740d5a1b (2026-08-28).
Data as JSON: /api/errors/ecbe9c9af8dacac7.
Report an issue: GitHub.