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

  1. Serve the app over HTTPS, or use http://localhost / http://127.0.0.1 which are secure contexts
  2. On Node <18, upgrade to Node 18+ where crypto.subtle is global; alternatively polyfill via node:crypto's webcrypto export
  3. In test environments, polyfill crypto.subtle (e.g. import 'crypto' webcrypto assignment) before importing the middleware
  4. 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

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


AI-assisted analysis of honojs/hono@e2740d5a1b (2026-08-28). Data as JSON: /api/errors/ecbe9c9af8dacac7. Report an issue: GitHub.