Sikau

Security · Day 7 · about 40 min

Signed tokens

HMAC-signed JWTs: tamper-evident, expiring session tokens.

Data the client can't fake

A JWT is header.payload.signature, each part base64url-encoded. The signature is an HMAC of the first two parts with a server secret: anyone can *read* the payload, but changing a single character breaks the signature. Add an expiry (exp) and you have a stateless session token.

Sign and verify

const b64url = (bytes) => btoa(String.fromCharCode(...bytes)).replace(/\+/g, "-").replace(/\//g, "_").replace(/=+$/, "");
const fromB64url = (s) => Uint8Array.from(atob(s.replace(/-/g, "+").replace(/_/g, "/")), (c) => c.charCodeAt(0));
const jsonPart = (value) => b64url(new TextEncoder().encode(JSON.stringify(value)));

async function hmac(secret, data) {
  const key = await crypto.subtle.importKey("raw", new TextEncoder().encode(secret), { name: "HMAC", hash: "SHA-256" }, false, ["sign"]);
  return new Uint8Array(await crypto.subtle.sign("HMAC", key, new TextEncoder().encode(data)));
}

async function signToken(payload, secret) {
  const head = jsonPart({ alg: "HS256", typ: "JWT" });
  const body = jsonPart(payload);
  return `${head}.${body}.${b64url(await hmac(secret, `${head}.${body}`))}`;
}

async function verifyToken(token, secret, now = Date.now() / 1000) {
  const parts = String(token).split(".");
  if (parts.length !== 3) throw new Error("Malformed token");
  const expected = b64url(await hmac(secret, `${parts[0]}.${parts[1]}`));
  if (!safeEqual(expected, parts[2])) throw new Error("Bad signature");
  const payload = JSON.parse(new TextDecoder().decode(fromB64url(parts[1])));
  if (payload.exp !== undefined && now >= payload.exp) throw new Error("Expired");
  return payload;
}

The rest of this lesson

  • Reading a JWTexplain
  • Verifying safelyexplain
  • Can you read it?predict
  • Token check to failurematch
  • Tokenswrite code
  • Badge quizquiz
  • What you learnedrecap

Continue this lesson free — run real code in your browser

Continue with Google

See all 8 lessons of Security