Authentication · SDK design
jwkit
2026One JWT verification contract, implemented in Go and TypeScript
- Go
- TypeScript
- Gin
- Express
- JWKS
- 01
Parse & constrain
Canonical encoding, size limits, algorithm allow-list. - 02
Find key & verify
Cached JWKS, bounded refresh, signature check. - 03
Validate claims
Issuer, audience, expiry; the same typed errors.
An API written in Go and one written in TypeScript should agree on whether a token is valid. Their underlying libraries can disagree on malformed JSON, base64 encoding and which error to report first. I built jwkit to make that boundary explicit: one verification policy, two implementations, and a shared suite that checks they behave the same way.
What I built
Go and TypeScript verifiers, Gin and Express middleware, typed errors, and a JWKS cache that handles signing-key rotation. The verifier accepts RS256 and ES256, checks the signature before claims, and validates issuer, audience and time claims in a fixed order.
Technical decisions
- Own the policy, reuse the cryptography. jwkit handles parsing, validation order and errors; jwx and jose handle signature verification and JWK decoding. This makes behavior consistent without implementing cryptographic primitives.
- Bind each key to an algorithm. A token cannot choose a different verification algorithm for its key. An explicit allow-list and strict encoding checks reject ambiguous inputs early.
- Bound key refreshes. Concurrent callers share one fetch. Unknown key IDs can trigger a refresh, subject to a cooldown. An expired cache waits for refresh, so a withdrawn key is not silently served while an update runs.
- Distinguish infrastructure failure. Unavailable signing keys produce a service-unavailable response rather than telling a client that a potentially valid session is unauthorized.
Quick walkthrough
- A service configures its trusted issuer, audience and JWKS endpoint, then attaches the Gin or Express middleware.
- A request passes through parsing, key lookup and signature verification before any claims are trusted.
- A valid token exposes claims to the handler. An expired token returns
ErrExpired; missing scope returns a forbidden response. Both language implementations follow the same contract.
The example servers run against an in-repository issuer. The conformance suite compares acceptance and typed errors across implementations. The repository also includes fuzzing, property tests and cache-rotation tests.
Scope and limits
jwkit verifies compact signed JWTs; it does not provide login, encrypted tokens or every signing algorithm. Tokens must include a key ID and expiry. The TypeScript package targets Node.js; edge runtimes have not been validated. Rigforge provides a concrete integration of the Go middleware.