honojs/hono · error · Error

JWK auth middleware requires options for either "keys" or "j

Error message

JWK auth middleware requires options for either "keys" or "jwks_uri" or both

What it means

The JWK auth middleware requires at least one source of verification keys: either static 'keys' (an array of JWKs / a JWKS), a 'jwks_uri' to fetch keys from, or both. If neither is present in the options object, construction fails immediately with this error.

Source

Thrown at src/middleware/jwk/jwk.ts:71

    allow_anon?: boolean
    cookie?:
      | string
      | { key: string; secret?: string | BufferSource; prefixOptions?: CookiePrefixOptions }

    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({

View on GitHub (pinned to e2740d5a1b)

Solutions

  1. Pass options.keys (JWKS/JWK array) or options.jwks_uri (e.g. your IdP's https://.../jwks.json endpoint)
  2. If keys come from env/config, validate at startup that at least one source resolves to a truthy value before creating the middleware
  3. Check spelling: the field is jwks_uri (snake_case), not jwksUri or jwksUrl
  4. If you fetch keys dynamically, point jwks_uri at the issuer's JWKS endpoint rather than passing a fetched array conditionally

Example fix

// before
app.use('/api/*', jwk({ realm: 'api' }))
// after
app.use('/api/*', jwk({ jwks_uri: 'https://issuer.example.com/.well-known/jwks.json', realm: 'api' }))
Defensive patterns

Strategy: type-guard

Validate before calling

const hasKeySource = (o: { keys?: unknown; jwks_uri?: string } | undefined): boolean =>
  !!o && (!!o.keys || !!o.jwks_uri)
if (!hasKeySource(opts)) throw new Error('JWK config incomplete')

Type guard

interface JwkOpts { keys?: unknown[]; jwks_uri?: string }
const hasJwkKeySource = (o: JwkOpts | undefined): o is Required<Pick<JwkOpts,'keys'|'jwks_uri'>> & JwkOpts =>
  !!o && (!!o.keys || !!o.jwks_uri)

Try / catch

try { app.use(jwk(opts)) } catch (e) { if (e instanceof Error && /keys.*jwks_uri/.test(e.message)) { /* fail startup with clear config error */ } throw e }

Prevention

When it happens

Trigger: Calling jwk() with an empty options object, only a 'realm'/'headerName' setting, or only cookie options — i.e., omitting both options.keys and options.jwks_uri.

Common situations: Loading keys conditionally from env and accidentally passing undefined (e.g. keys: process.env.JWKS ? JSON.parse(...) : undefined) when the env var is missing; typos like 'key' or 'jwksUrl'; refactors that moved key config into a nested 'verification' object, which does not count as the key source.

Related errors


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