Skip to content
Agents tracked: 440 Downloads (7d): 202M down 19.5% GitHub stars: 10.4M Releases (7d): 607 Agent status: 3 with issuesSince last limit reset: Claude 28d · Codex 0d Updated Oct 21, 2026

Encrypt and decrypt a JWT with jose

A signed JWT can be read by anyone who holds it; an encrypted JWT hides its claims so only holders of the key can read them.

All jose examples jose: usage and stats

Encrypt and decrypt a JWT with jose: screenshot of the example below running
The code below, running in Chromium on our server (Oct 12, 2026). Press Run to see it live and change it.

How it works

  1. Create a 256-bit key. The code fills 32 random bytes with crypto.getRandomValues to use as a direct symmetric key for A256GCM. [1]
  2. Encrypt with EncryptJWT. new EncryptJWT(claims) with setProtectedHeader({ alg: 'dir', enc: 'A256GCM' }) and encrypt(key) returns a compact token. The code prints its five parts to show it differs from a signed JWT. [1]
  3. Decrypt and validate. jwtDecrypt(jwe, key, { issuer }) decrypts the token and checks the issuer claim, then returns the payload. A second call with a wrong key fails and the code prints the error code. [1]

Run and edit it

It runs in your browser, in a sandboxed frame that cannot see this site; the packages load from esm.sh only after you press Run.

import { EncryptJWT, jwtDecrypt } from 'jose'

const key = crypto.getRandomValues(new Uint8Array(32))
const wrongKey = crypto.getRandomValues(new Uint8Array(32))

const jwe = await new EncryptJWT({ email: 'ada@example.com', plan: 'pro' })
  .setProtectedHeader({ alg: 'dir', enc: 'A256GCM' })
  .setIssuedAt()
  .setIssuer('urn:example:issuer')
  .setExpirationTime('2h')
  .encrypt(key)

const parts = jwe.split('.')
console.log('token:', jwe.slice(0, 44) + '...')
console.log('parts:', parts.length, '(lengths ' + parts.map((p) => p.length).join(', ') + ')')

const { payload, protectedHeader } = await jwtDecrypt(jwe, key, {
  issuer: 'urn:example:issuer',
})
console.log('header:', JSON.stringify(protectedHeader))
console.log('email:', payload.email)
console.log('plan:', payload.plan)
console.log('iss:', payload.iss)

try {
  await jwtDecrypt(jwe, wrongKey)
} catch (err) {
  console.log('wrong key:', err.code)
}
Ctrl/⌘ + Enter runs

Free for signed-in members, with a daily limit. Describe the change in plain words; the new code runs only in your browser.

Add more claims to the payload and see the encrypted token grow while its contents stay hidden. Code written by AgentGid [1].

Checked Oct 12, 2026: it starts without errors in Chromium with jose 6.2.12. Needs WebGL.

More jose examples

Sign a JWT with jose Verify a JWT with jose Generate keys and export a JWK with jose Decode a JWT with jose

Sources

  1. jose README (GitHub)

Explanations written by AgentGid from the official docs; the numbers in brackets point here. The code marked "written by AgentGid" is ours and free to copy.

Building something with jose? Ask AgentGid →