typescript 46 lines · 8 steps

Decoding a JWT to check expiry

Parse a JWT's payload without a library and decide whether it has expired, allowing for clock skew.

Explained by highlit
1interface JwtPayload {
2 exp?: number;
3 iat?: number;
4 sub?: string;
5 [key: string]: unknown;
6}
7 
8function decodeBase64Url(segment: string): string {
9 const padded = segment.replace(/-/g, "+").replace(/_/g, "/");
10 const withPadding = padded.padEnd(padded.length + ((4 - (padded.length % 4)) % 4), "=");
11 if (typeof atob === "function") {
12 return decodeURIComponent(
13 atob(withPadding)
14 .split("")
15 .map((c) => "%" + c.charCodeAt(0).toString(16).padStart(2, "0"))
16 .join(""),
17 );
18 }
19 return Buffer.from(withPadding, "base64").toString("utf-8");
20}
21 
22function parseJwtPayload(token: string): JwtPayload {
23 const parts = token.split(".");
24 if (parts.length !== 3) {
25 throw new Error("Malformed JWT: expected 3 segments");
26 }
27 let payload: unknown;
28 try {
29 payload = JSON.parse(decodeBase64Url(parts[1]));
30 } catch {
31 throw new Error("Malformed JWT: payload is not valid JSON");
32 }
33 if (typeof payload !== "object" || payload === null) {
34 throw new Error("Malformed JWT: payload is not an object");
35 }
36 return payload as JwtPayload;
37}
38 
39export function isTokenExpired(token: string, clockSkewSeconds = 30): boolean {
40 const { exp } = parseJwtPayload(token);
41 if (typeof exp !== "number" || !Number.isFinite(exp)) {
42 throw new Error("JWT is missing a valid 'exp' claim");
43 }
44 const nowSeconds = Math.floor(Date.now() / 1000);
45 return nowSeconds >= exp - clockSkewSeconds;
46}
01 / 01
STEP 01

Walkthrough

Space play step click any line
Three takeaways
  1. 1JWTs are just base64url-encoded JSON, so you can inspect claims without a crypto library.
  2. 2Validate structure at each stage and narrow unknown types before trusting decoded data.
  3. 3A clock-skew allowance prevents flapping expiry decisions between machines with slightly different clocks.

Related explainers

Share this explainer

Here's the card — post it anywhere.

Decoding a JWT to check expiry — share card
Made with highlit — turn any snippet into a walkthrough like this in about a minute.
Explain your code