-
Release v1.0.0 Stable
released this
2026-05-07 23:53:18 -06:00 | 1 commits to main since this releasev1.0.0
code.nochebuena.dev/go/httpauth-jwtOverview
httpauth-jwtprovides self-issued JWT authentication for Go HTTP services. It
covers the full token lifecycle: issuing access + refresh pairs, rotating refresh
tokens through aBlacklistinterface, and verifying Bearer tokens in an
AuthMiddlewarethat integrates directly with thehttpauthmiddleware stack.This is the JWT counterpart to
httpauth-firebase— both callhttpauth.SetTokenData
after token verification, making all downstream middleware (EnrichmentMiddleware,
AuthzMiddleware,ClaimsPermissionProvider) completely provider-agnostic.Released at v1.0.0 because it is designed for immediate production use across
multiple services.What's Included
Signing and verification
NewHMACSigner(secret []byte) Signer— HMAC-SHA256. For single-service
deployments.NewRSASigner(privateKey *rsa.PrivateKey) Signer— RSA-SHA256. For deployments
where the token issuer must be separated from verifiers.NewRSASignerFromPEM(pemKey []byte) (Signer, error)— loads PKCS#8 or PKCS#1
PEM private key from an environment variable or file.NewRSAPublicKeyVerifier(publicKey *rsa.PublicKey) Verifierand
NewRSAPublicKeyVerifierFromPEM(pemKey []byte) (Verifier, error)— for
microservices that verify tokens but never issue them.AuthMiddlewareaccepts the
Verifierinterface, so these services need no signing capability.Token issuance
IssueTokenPair(signer, uid, customClaims, cfg) (TokenPair, error)customClaimsare merged at the top level of the access token. This is the mechanism
for embedding per-resource permission masks:customClaims := map[string]any{ "permisos": map[string]any{"usuarios": int64(515)}, }The
httpauth.ClaimsPermissionProvider("permisos")reads these directly — no
database call on every request. On refresh, callers re-fetch fresh masks and embed
them in the new token.The refresh token carries only identity (
sub,iss,iat,exp,jti) and a
token family (fam). It never carries permission data.Refresh rotation
RefreshTokenPair(ctx, signer, refreshToken, blacklist, cfg, customClaims) (TokenPair, error)Full rotation protocol:
- Verify the refresh token signature and expiry.
- Check the JTI against the
Blacklist. - If revoked → return
ErrTokenRevoked(possible replay attack). - Revoke the old JTI with the token's remaining TTL.
- Issue a new pair with
customClaimsin the access token.
ErrTokenRevoked— sentinel error. Callers should respond with 401 and
prompt re-authentication when they see this.Blacklistinterface —IsRevokedandRevoke. Implementations backed by
Valkey or Redis are the expected use case. TTL onRevokekeeps the blacklist
bounded — entries naturally expire when the old token would have expired anyway.AuthMiddleware
AuthMiddleware(verifier Verifier, publicPaths []string) func(http.Handler) http.HandlerVerifies Bearer tokens and calls
httpauth.SetTokenData(ctx, uid, claims). Accepts
Verifier(notSigner) — the middleware only needs verification capability.Design Highlights
Verifier⊂Signer.AuthMiddlewareaccepts the narrowerVerifierinterface.
A service that receives tokens from a central issuer passes aNewRSAPublicKeyVerifier
— it never touches the private key. A service that also issues tokens passes its
Signerdirectly (which embedsVerifier).Claims at the top level. Access tokens use
jwt.MapClaimswith custom claims
merged at the top level, not nested under a wrapper key. This makes them directly
compatible withhttpauth.ClaimsPermissionProviderand any standard JWT introspection
tool.Refresh tokens carry no permission data. Permissions are re-fetched and
re-embedded on each rotation. This ensures role changes take effect on the next
refresh without requiring active token revocation for access tokens.Blacklist TTL = remaining token lifetime. Revoking a JTI with the token's
remaining lifetime means the blacklist entry expires at the exact moment the token
would have expired naturally. The blacklist stays small and requires no cleanup.ErrTokenRevokedis a sentinel. Callers useerrors.Is(err, httpauthjwt.ErrTokenRevoked)
to distinguish a replay attack from a general infrastructure error. Other errors
indicate connectivity or signing faults and may warrant different handling.Installation
go get code.nochebuena.dev/go/httpauth-jwt@v1.0.0 go get code.nochebuena.dev/go/httpauth@v1.0.0Downloads