Starting points for your own code. Run them in the environment shown below.
Node.js 22+ · ES module (.mjs)
Current inputs apply only to fields used by the example. Selected files are not embedded.
Install dependencies
npm install jose
import { SignJWT, decodeJwt, jwtVerify } from "jose";
const algorithm = "HS256";
// Test key only. Load your real key from a secret store.
const key = new TextEncoder().encode("demo-key-".padEnd(64, "x"));
const expiryMinutes = Number("30");
const token = await new SignJWT({ sub: "user-123" })
.setProtectedHeader({ alg: algorithm })
.setIssuedAt()
.setExpirationTime(Math.floor(Date.now() / 1000) + expiryMinutes * 60)
.sign(key);
console.log(token);
console.log(decodeJwt(token)); // Decoding does not verify trust.
const verified = await jwtVerify(token, key, { algorithms: [algorithm] });
console.log(verified.payload); // Signature and exp/nbf checked.
// Set issuer and audience options when your service requires them.
Put it to work
Inspect exp in a test-server JWT, then verify its signature with your shared secret.
Details that matter
Format and meaning
Decoding is not authentication. Anyone can read the header and payload.
Using it correctly
HS256, HS384, and HS512 use a UTF-8 shared secret. Use at least 32, 48, or 64 bytes respectively.
Common pitfalls
Signature, exp expiry, and nbf activation are reported separately. Issuer and audience validation are outside this tool’s scope.
Supported scope and limits
100 KB token/JSON limit; HMAC algorithms only
Split inputs that exceed the limit. If a format or algorithm is unsupported, choose a tool that matches your requirements instead of silently changing the format.