python 42 lines · 7 steps

Building signed expiring tokens with HMAC

A minimal JWT-style scheme that signs a JSON payload with HMAC-SHA256 and verifies it in constant time.

Explained by highlit
1import base64
2import hashlib
3import hmac
4import json
5import time
6 
7 
8class TokenError(Exception):
9 pass
10 
11 
12def _b64encode(raw: bytes) -> str:
13 return base64.urlsafe_b64encode(raw).rstrip(b"=").decode()
14 
15 
16def _b64decode(data: str) -> bytes:
17 padding = "=" * (-len(data) % 4)
18 return base64.urlsafe_b64decode(data + padding)
19 
20 
21def issue_token(payload: dict, secret: str, ttl: int = 3600) -> str:
22 body = dict(payload, exp=int(time.time()) + ttl)
23 encoded = _b64encode(json.dumps(body, separators=(",", ":")).encode())
24 signature = hmac.new(secret.encode(), encoded.encode(), hashlib.sha256).digest()
25 return f"{encoded}.{_b64encode(signature)}"
26 
27 
28def verify_token(token: str, secret: str) -> dict:
29 try:
30 encoded, provided = token.split(".", 1)
31 except ValueError:
32 raise TokenError("malformed token")
33 
34 expected = hmac.new(secret.encode(), encoded.encode(), hashlib.sha256).digest()
35 if not hmac.compare_digest(expected, _b64decode(provided)):
36 raise TokenError("invalid signature")
37 
38 payload = json.loads(_b64decode(encoded))
39 if payload.get("exp", 0) < int(time.time()):
40 raise TokenError("token expired")
41 
42 return payload
01 / 01
STEP 01

Walkthrough

Space play step click any line
Three takeaways
  1. 1Signing a payload with a shared secret lets you trust its contents without storing server-side state.
  2. 2Always compare signatures with a constant-time function to avoid leaking bytes through timing.
  3. 3Baking an expiry into the signed body means a stolen token stops working on its own.

Related explainers

python
from flask import Blueprint, jsonify, request, abort
 
v1 = Blueprint("users_v1", __name__)
v2 = Blueprint("users_v2", __name__)

Versioning a Flask API with Blueprints

api versioning blueprints rest
Intermediate 7 steps
typescript
import { Injectable, UnauthorizedException } from '@nestjs/common';
import { PassportStrategy } from '@nestjs/passport';
import { ExtractJwt, Strategy } from 'passport-jwt';
import { ConfigService } from '@nestjs/config';

How a JWT strategy authenticates in NestJS

authentication jwt passport
Intermediate 7 steps
python
import time
import threading
from enum import Enum
from functools import wraps

Building a circuit breaker in Python

circuit-breaker resilience decorators
Advanced 7 steps
typescript
import { Injectable } from '@angular/core';
import { HttpInterceptor, HttpRequest, HttpHandler, HttpEvent, HttpErrorResponse } from '@angular/common/http';
import { BehaviorSubject, Observable, throwError } from 'rxjs';
import { catchError, filter, take, switchMap } from 'rxjs/operators';

Refreshing auth tokens in an Angular interceptor

http-interceptor token-refresh rxjs
Advanced 8 steps
go
package auth
 
import (
	"net/http"

Setting and reading secure session cookies in Go

cookies session-management security
Intermediate 6 steps
python
from django.contrib.postgres.search import SearchQuery, SearchRank, SearchVector
from django.core.cache import cache
from django.db.models import F
from django.http import JsonResponse

Ranked full-text search in Django

full-text-search debouncing caching
Advanced 9 steps

Share this explainer

Here's the card — post it anywhere.

Building signed expiring tokens with HMAC — share card
Made with highlit — turn any snippet into a walkthrough like this in about a minute.
Explain your code