Documentation
¶
Overview ¶
Package jwt issues and verifies JSON Web Tokens (RFC 7519) with the standard library: HS256, RS256, ES256 and EdDSA, an algorithm allowlist tied to each key (no algorithm confusion, never "none"), key ids with static or remote (JWKS) key sets that rotate, strict claim checks, a Guard for package auth, and rotating refresh tokens with reuse detection.
tok, err := jwt.Sign(key, jwt.Claims{Subject: u.AuthID(), ExpiresAt: time.Now().Add(15 * time.Minute)})
claims, err := verifier.Verify(ctx, tok)
See https://github.com/AlexGalhardo/vukem/blob/main/docs/authentication.md.
Example ¶
package main
import (
"context"
"crypto/ed25519"
"crypto/rand"
"fmt"
"time"
"github.com/AlexGalhardo/vukem/auth/jwt"
)
func main() {
_, priv, _ := ed25519.GenerateKey(rand.Reader)
key := jwt.EdDSA("2026-10", priv)
tok, _ := jwt.Sign(key, jwt.Claims{
Subject: "42", Issuer: "https://api.example.com", Audience: []string{"api"},
ExpiresAt: time.Now().Add(15 * time.Minute),
})
v := jwt.NewVerifier(jwt.StaticKeys(key), jwt.VerifyOptions{Issuer: "https://api.example.com", Audience: "api"})
claims, err := v.Verify(context.Background(), tok)
fmt.Println(claims.Subject, err)
}
Output: 42 <nil>
Index ¶
- Constants
- Variables
- func RefreshTokensMigration(id string) schema.Migration
- func Sign(k Key, c Claims) (string, error)
- type Claims
- type Guard
- type JWK
- type JWKS
- type Key
- type KeySet
- type RefreshTokens
- func (r *RefreshTokens) Issue(ctx context.Context, userID string) (string, error)
- func (r *RefreshTokens) Revoke(ctx context.Context, plain string) error
- func (r *RefreshTokens) RevokeUser(ctx context.Context, userID string) error
- func (r *RefreshTokens) Rotate(ctx context.Context, plain string) (userID, next string, err error)
- type RemoteJWKS
- type SharedKeySet
- type Verifier
- type VerifyOptions
Examples ¶
Constants ¶
const ( AlgHS256 = "HS256" AlgRS256 = "RS256" AlgES256 = "ES256" AlgEdDSA = "EdDSA" )
Supported algorithms.
Variables ¶
var ErrInvalid = errors.New("jwt: invalid token")
ErrInvalid is returned for every token that fails verification; the cause is wrapped for logs and never shown to clients.
ErrKeysUnavailable is wrapped by Verify when the key set could not be consulted (a RemoteJWKS fetch failing with no cached key for the token): the token was not judged, so Guard answers 503 instead of 401. Custom KeySets wrap it for their own outages; any other KeySet error rejects the token as ErrInvalid.
var ErrRefreshReused = errors.New("jwt: refresh token reused; family revoked")
ErrRefreshReused is returned when a refresh token is presented again after it was rotated: the whole token family is revoked, since one of its copies was stolen.
Functions ¶
func RefreshTokensMigration ¶
RefreshTokensMigration creates the refresh_tokens table.
Types ¶
type Claims ¶
type Claims struct {
Issuer string
Subject string
Audience []string
ExpiresAt time.Time
NotBefore time.Time
IssuedAt time.Time
ID string
// Extra holds the other claims (decoded JSON values).
Extra map[string]any
}
Claims are the registered claims plus Extra private claims.
type Guard ¶
type Guard[U auth.Authenticatable] struct { // contains filtered or unexported fields }
Guard authenticates "Authorization: Bearer <jwt>" requests: the token must verify and its "sub" must name a user of the provider. It implements auth.Checker, so auth.Required(guard) protects routes.
func NewGuard ¶
func NewGuard[U auth.Authenticatable](name string, v *Verifier, p auth.UserProvider[U]) *Guard[U]
NewGuard returns a JWT guard named name. It panics when v.Err() reports a misconfiguration (a RemoteJWKS without Audience or Issuer), so the app fails at boot rather than refusing every token.
func (*Guard[U]) APIOnly ¶ added in v0.25.0
func (*Guard[U]) APIOnly()
APIOnly implements auth.APIGuard: auth.Required answers 401 problem+json and never redirects to a login page.
func (*Guard[U]) Authenticate ¶ added in v0.28.0
Authenticate implements auth.FailClosedChecker, so auth.Required answers a key set or user store outage with 503 instead of 401.
type JWK ¶
type JWK struct {
Kty string `json:"kty"`
Kid string `json:"kid,omitempty"`
Alg string `json:"alg,omitempty"`
Use string `json:"use,omitempty"`
N string `json:"n,omitempty"`
E string `json:"e,omitempty"`
Crv string `json:"crv,omitempty"`
X string `json:"x,omitempty"`
Y string `json:"y,omitempty"`
}
JWK is a public JSON Web Key (RFC 7517).
type JWKS ¶
type JWKS struct {
Keys []JWK `json:"keys"`
}
JWKS is a JSON Web Key Set.
func PublicJWKS ¶
PublicJWKS returns the public part of keys as a JWKS document, to serve at /.well-known/jwks.json. HMAC keys are never published.
type Key ¶
type Key struct {
// ID is the "kid" header.
ID string
// Alg is the only algorithm this key accepts.
Alg string
// Secret is the HS256 secret (at least 32 bytes).
Secret []byte
// Private signs (RSA, ECDSA P-256 or Ed25519); nil for verify-only keys.
Private crypto.Signer
// Public verifies.
Public crypto.PublicKey
}
Key is a signing or verification key bound to one algorithm.
type KeySet ¶
KeySet finds verification keys by id (kid); "" asks for the only key.
func StaticKeys ¶
StaticKeys is a fixed key set (current and previous keys during a rotation).
type RefreshTokens ¶
type RefreshTokens struct {
// TTL is the lifetime of each token (default 30 days).
TTL time.Duration
// Now is the clock (default time.Now).
Now func() time.Time
// contains filtered or unexported fields
}
RefreshTokens stores opaque, rotating refresh tokens (only their SHA-256 is kept). Each Rotate consumes the presented token and returns a new one of the same family; presenting a consumed token revokes the family.
func NewRefreshTokens ¶
func NewRefreshTokens(ex database.Executor) *RefreshTokens
NewRefreshTokens uses the refresh_tokens table (RefreshTokensMigration).
func (*RefreshTokens) Issue ¶
Issue starts a new token family for userID and returns the plain token.
func (*RefreshTokens) Revoke ¶
func (r *RefreshTokens) Revoke(ctx context.Context, plain string) error
Revoke ends the family of plain (logout); unknown tokens are ignored.
func (*RefreshTokens) RevokeUser ¶
func (r *RefreshTokens) RevokeUser(ctx context.Context, userID string) error
RevokeUser ends every refresh token of userID (password change).
type RemoteJWKS ¶
type RemoteJWKS struct {
URL string
// Client fetches the set (default: 10s timeout).
Client *http.Client
// MaxAge refreshes the cache after this long (default 1h).
MaxAge time.Duration
// MinRefresh limits refetches for unknown key ids (default 1m), so
// tokens with random kids cannot hammer the provider.
MinRefresh time.Duration
// Now is the clock (default time.Now).
Now func() time.Time
// contains filtered or unexported fields
}
RemoteJWKS fetches an identity provider's key set and refreshes it when a token names an unknown key id (rotation), at most once per MinRefresh.
func (*RemoteJWKS) SharedKeys ¶ added in v0.24.0
func (*RemoteJWKS) SharedKeys() bool
SharedKeys implements SharedKeySet: a provider's JWKS signs tokens for all of its clients.
type SharedKeySet ¶ added in v0.24.0
type SharedKeySet interface {
KeySet
}
SharedKeySet is a KeySet whose keys an identity provider also uses for its other clients (RemoteJWKS). A Verifier over one requires Audience and Issuer. Custom KeySets that wrap a RemoteJWKS should forward SharedKeys (embedding *RemoteJWKS does).
type Verifier ¶
type Verifier struct {
// contains filtered or unexported fields
}
Verifier checks tokens against a key set and claim rules.
func NewVerifier ¶
func NewVerifier(keys KeySet, opts VerifyOptions) *Verifier
NewVerifier returns a Verifier.
type VerifyOptions ¶
type VerifyOptions struct {
// Algorithms allowed (default: all supported); each key still only
// accepts its own algorithm.
Algorithms []string
// Issuer, when set, must equal "iss". Required with a *RemoteJWKS.
Issuer string
// Audience, when set, must be in "aud". Required with a *RemoteJWKS:
// an identity provider signs tokens for all of its clients with the
// same keys, so without it a token issued to another app is accepted.
Audience string
// InsecureSkipAudience accepts any "aud" with a *RemoteJWKS.
// Dangerous: tokens the provider issued to other apps pass.
InsecureSkipAudience bool
// InsecureSkipIssuer accepts any "iss" with a *RemoteJWKS.
// Dangerous: tokens from other tenants of a multi-tenant provider pass.
InsecureSkipIssuer bool
// Leeway tolerates clock skew (default 30s).
Leeway time.Duration
// AllowMissingExpiry accepts tokens without "exp" (off by default).
AllowMissingExpiry bool
// Now is the clock (default time.Now).
Now func() time.Time
}
VerifyOptions configures a Verifier.