Skip to main content

Import

import { jwt, sign, verify, decode } from 'hono/jwt'

Usage

const app = new Hono()

app.use(
  '/auth/*',
  jwt({
    secret: 'it-is-very-secret',
    alg: 'HS256',
  })
)

app.get('/auth/page', (c) => {
  const payload = c.get('jwtPayload')
  return c.text('You are authorized')
})

Options

The jwt middleware accepts an options object:
secret
SignatureKey
required
A value of your secret key. Required for token verification.
alg
SignatureAlgorithm
required
An algorithm type that is used for verifying. Available types are:
  • HS256, HS384, HS512 (HMAC)
  • RS256, RS384, RS512 (RSA)
  • PS256, PS384, PS512 (RSA-PSS)
  • ES256, ES384, ES512 (ECDSA)
  • EdDSA (EdDSA)
If this value is set, then the JWT is retrieved from the cookie using the specified key. Can be:
  • A string: the cookie name
  • An object with key, optional secret for signed cookies, and optional prefixOptions
headerName
string
default:"Authorization"
The name of the header to look for the JWT token.
verification
VerifyOptions
Additional options for JWT payload verification (e.g., audience, issuer, expiration checks).

Signature

jwt(options: {
  secret: SignatureKey
  cookie?: string | {
    key: string
    secret?: string | BufferSource
    prefixOptions?: CookiePrefixOptions
  }
  alg: SignatureAlgorithm
  headerName?: string
  verification?: VerifyOptions
}): MiddlewareHandler

Context Variable

The middleware sets the decoded JWT payload in the context:
app.get('/auth/page', (c) => {
  const payload = c.get('jwtPayload')
  console.log(payload) // Decoded JWT payload
  return c.json({ user: payload.sub })
})

Examples

Basic usage with secret

app.use(
  '/auth/*',
  jwt({
    secret: 'it-is-very-secret',
    alg: 'HS256',
  })
)

Custom header name

app.use(
  '/auth/*',
  jwt({
    secret: 'it-is-very-secret',
    alg: 'HS256',
    headerName: 'x-custom-auth-header',
  })
)
app.use(
  '/auth/*',
  jwt({
    secret: 'it-is-very-secret',
    alg: 'HS256',
    cookie: 'jwt_token',
  })
)
app.use(
  '/auth/*',
  jwt({
    secret: 'it-is-very-secret',
    alg: 'HS256',
    cookie: {
      key: 'jwt_token',
      secret: 'cookie-secret',
    },
  })
)

With verification options

app.use(
  '/auth/*',
  jwt({
    secret: 'it-is-very-secret',
    alg: 'HS256',
    verification: {
      audience: 'myapp',
      issuer: 'https://myapp.com',
    },
  })
)

Using RSA keys

const publicKey = await crypto.subtle.importKey(
  'spki',
  pemToArrayBuffer(publicKeyPem),
  { name: 'RSASSA-PKCS1-v1_5', hash: 'SHA-256' },
  false,
  ['verify']
)

app.use(
  '/auth/*',
  jwt({
    secret: publicKey,
    alg: 'RS256',
  })
)

Utility Functions

sign

Sign a JWT token.
import { sign } from 'hono/jwt'

const token = await sign(
  { sub: 'user123', exp: Math.floor(Date.now() / 1000) + 60 * 60 },
  'secret',
  'HS256'
)

verify

Verify a JWT token.
import { verify } from 'hono/jwt'

const payload = await verify(token, 'secret', 'HS256')

decode

Decode a JWT token without verification.
import { decode } from 'hono/jwt'

const { header, payload } = decode(token)

verifyWithJwks

Verify a JWT token using JWKS.
import { verifyWithJwks } from 'hono/jwt'

const payload = await verifyWithJwks(token, jwksUrl)

Behavior

  • Expects JWT in Authorization header with format: Bearer <token>
  • Returns 401 Unauthorized if:
    • No authorization header or cookie is present
    • Token format is invalid (not exactly 2 parts)
    • Token verification fails
  • Sets WWW-Authenticate header with error details on failure
  • Stores decoded payload in context as jwtPayload
  • Requires crypto.subtle.importKey to be available in runtime
  • Supports both header-based and cookie-based token retrieval

Build docs developers (and LLMs) love