Documentation
¶
Overview ¶
Package jwt provides authit's token signing/verification, kept deliberately free of any notion of users, sessions, or storage — it knows only how to turn claims into a signed string and back.
Index ¶
- Constants
- Variables
- func JWKS(keys ...crypto.PublicKey) ([]byte, error)
- func KeyID(key crypto.PublicKey) (string, error)
- type AsymmetricSigner
- func (s *AsymmetricSigner) Defaults() Defaults
- func (s *AsymmetricSigner) Generate(claims Claims) (string, error)
- func (s *AsymmetricSigner) JWKS() ([]byte, error)
- func (s *AsymmetricSigner) KeyID() string
- func (s *AsymmetricSigner) PublicKey() crypto.PublicKey
- func (s *AsymmetricSigner) Sign(claims jwtlib.Claims) (string, error)
- func (s *AsymmetricSigner) Validate(token string) (Claims, error)
- func (s *AsymmetricSigner) Verifier() *PublicKeyVerifier
- func (s *AsymmetricSigner) Verify(token string, dst jwtlib.Claims) error
- type Claims
- type Defaults
- type HMACSigner
- type PublicKeyVerifier
- type Signer
- type Verifier
Constants ¶
const MinRSABits = 2048
MinRSABits is the smallest RSA modulus this package will sign or verify with.
Variables ¶
var ( // ErrUnsupportedKey is returned when a key is of a type this package // cannot sign or verify with. ErrUnsupportedKey = errors.New("authit/jwt: unsupported key type") // ErrWeakKey is returned for an RSA key too small to be safe. ErrWeakKey = errors.New("authit/jwt: RSA key must be at least 2048 bits") // ErrNoKeys is returned by NewVerifier when given no keys. ErrNoKeys = errors.New("authit/jwt: at least one public key is required") )
var ErrInvalidSecret = errors.New("authit/jwt: secret must be at least 32 bytes")
ErrInvalidSecret is returned by NewHMACSigner when the secret is too short to be a safe HMAC-SHA256 key.
Functions ¶
func JWKS ¶ added in v0.4.0
JWKS renders public keys as a JWK Set document (RFC 7517), the format a relying party fetches from a `.well-known/jwks.json` endpoint to learn which keys may sign tokens it should trust.
Serving one is what turns "every service shares the signing secret" into "one service signs, everyone else verifies". Publish it, point consumers at it, and rotate by adding the new key to the set before switching the signer over.
Only *rsa.PublicKey and ed25519.PublicKey are supported. Passing anything else — in particular a private key — is an error rather than a best-effort encoding, so this cannot quietly publish a secret.
func KeyID ¶ added in v0.4.0
KeyID returns the RFC 7638 JWK thumbprint of a public key: the base64url-encoded SHA-256 of a JSON object containing only the required members, with no whitespace and the members in lexicographic order.
Deriving the identifier from the key itself, rather than assigning one, means two parties compute the same `kid` for the same key without having to agree on anything.
Types ¶
type AsymmetricSigner ¶ added in v0.4.0
type AsymmetricSigner struct {
// contains filtered or unexported fields
}
AsymmetricSigner signs with a private key and verifies with the matching public one. Unlike HMACSigner, the verifying half can be handed out safely: call Verifier to get a value that can check tokens and nothing else, and PublicKey/JWKS to publish the key it needs.
func NewEd25519Signer ¶ added in v0.4.0
func NewEd25519Signer(priv ed25519.PrivateKey, defaults Defaults) (*AsymmetricSigner, error)
NewEd25519Signer returns a Signer using EdDSA (Ed25519).
func NewRS256Signer ¶ added in v0.4.0
func NewRS256Signer(priv *rsa.PrivateKey, defaults Defaults) (*AsymmetricSigner, error)
NewRS256Signer returns a Signer using RSASSA-PKCS1-v1_5 with SHA-256.
Prefer NewEd25519Signer for new deployments: the keys and signatures are far smaller and there is no parameter to get wrong. RS256 is here because it is what most existing JWT consumers and JWKS clients expect.
func (*AsymmetricSigner) Defaults ¶ added in v0.4.0
func (s *AsymmetricSigner) Defaults() Defaults
func (*AsymmetricSigner) Generate ¶ added in v0.4.0
func (s *AsymmetricSigner) Generate(claims Claims) (string, error)
Generate signs claims after applying issuer/TTL defaults for zero fields.
func (*AsymmetricSigner) JWKS ¶ added in v0.4.0
func (s *AsymmetricSigner) JWKS() ([]byte, error)
JWKS returns this signer's public key as a single-key JWK Set, ready to serve at a .well-known endpoint.
func (*AsymmetricSigner) KeyID ¶ added in v0.4.0
func (s *AsymmetricSigner) KeyID() string
KeyID returns the RFC 7638 thumbprint of the public key, which is the `kid` written into every token this signer produces.
func (*AsymmetricSigner) PublicKey ¶ added in v0.4.0
func (s *AsymmetricSigner) PublicKey() crypto.PublicKey
PublicKey returns the verifying half of the key pair, for publishing.
func (*AsymmetricSigner) Sign ¶ added in v0.4.0
func (s *AsymmetricSigner) Sign(claims jwtlib.Claims) (string, error)
Sign signs an arbitrary claims value, stamping the key's `kid` into the header so a verifier holding several keys can select the right one without trial and error — which is what makes key rotation possible.
func (*AsymmetricSigner) Validate ¶ added in v0.4.0
func (s *AsymmetricSigner) Validate(token string) (Claims, error)
Validate verifies token as Claims.
func (*AsymmetricSigner) Verifier ¶ added in v0.4.0
func (s *AsymmetricSigner) Verifier() *PublicKeyVerifier
Verifier returns a value that can check this signer's tokens but cannot produce any. This is what a second service should be given.
type Claims ¶
type Claims struct {
jwt.RegisteredClaims
Email string `json:"email,omitempty"`
// ActorID, when set, means this token was minted by an operator
// impersonating this Subject rather than the user logging in directly
// (see the superuser package's Impersonate). It carries no special
// privilege on its own — it exists purely so downstream audit/log code
// can record who was really acting.
ActorID string `json:"actor_id,omitempty"`
}
Claims is the default access-token claim set for regular users. Subject carries the user ID. Host applications that need extra fields (e.g. a team ID) can define their own struct embedding jwtlib.RegisteredClaims and sign it directly through Signer.Sign/Verify instead of using this type.
func (Claims) IsImpersonation ¶
IsImpersonation reports whether these claims were minted by an operator impersonating the subject rather than by the subject logging in themselves.
type Defaults ¶
Defaults are applied by Signer.Generate/Validate (the Claims-typed convenience methods); callers using Sign/Verify with a custom claims type apply them explicitly.
type HMACSigner ¶
type HMACSigner struct {
// contains filtered or unexported fields
}
HMACSigner is a Signer backed by HMAC-SHA256 with a shared secret.
func NewHMACSigner ¶
func NewHMACSigner(secret []byte, defaults Defaults) (*HMACSigner, error)
NewHMACSigner constructs an HMACSigner. secret must be at least 32 bytes.
func (*HMACSigner) Defaults ¶
func (s *HMACSigner) Defaults() Defaults
func (*HMACSigner) Generate ¶
func (s *HMACSigner) Generate(claims Claims) (string, error)
Generate signs claims after applying issuer/TTL defaults for any zero fields.
func (*HMACSigner) Sign ¶
func (s *HMACSigner) Sign(claims jwt.Claims) (string, error)
Sign signs an arbitrary claims value.
type PublicKeyVerifier ¶ added in v0.4.0
type PublicKeyVerifier struct {
// contains filtered or unexported fields
}
PublicKeyVerifier verifies tokens against one or more public keys. It has no signing method and holds no private key, so a service given one cannot mint a token no matter what it does with it — which is the property HMACSigner cannot offer.
Several keys may be supplied to span a rotation: publish the new key alongside the old, let every verifier pick both up, then switch the signer over and retire the old key once no unexpired token bears it.
func NewVerifier ¶ added in v0.4.0
func NewVerifier(keys ...crypto.PublicKey) (*PublicKeyVerifier, error)
NewVerifier returns a Verifier for the given public keys, which must be *rsa.PublicKey or ed25519.PublicKey. Keys are indexed by their RFC 7638 thumbprint, matching the `kid` AsymmetricSigner writes.
func (*PublicKeyVerifier) JWKS ¶ added in v0.4.0
func (v *PublicKeyVerifier) JWKS() ([]byte, error)
JWKS returns the public keys as a JWK Set document, ready to serve.
type Signer ¶
type Signer interface {
Verifier
Sign(claims jwt.Claims) (string, error)
Generate(claims Claims) (string, error)
Defaults() Defaults
}
Signer mints and verifies JWTs. The generic Sign/Verify methods work with any jwt.Claims-satisfying type, so a host application can define its own claims struct (e.g. embedding a team ID) without authit needing to know about it. Generate/Validate are a convenience pair fixed to Claims.
type Verifier ¶ added in v0.4.0
type Verifier interface {
// Verify parses and validates token into dst, which must be a pointer
// to a jwt.Claims-satisfying type.
Verify(token string, dst jwt.Claims) error
// Validate verifies token as the default Claims type.
Validate(token string) (Claims, error)
}
Verifier checks that a token is genuine and unexpired. It cannot mint one, and that is the whole point of it existing separately from Signer.
With HMACSigner, verifying and signing need the same secret, so any service that can check a token can also forge one — including an impersonation token, since Claims.ActorID is an ordinary field. Inside a single binary that is fine. Across two, it is not: the second service should hold a Verifier built from a public key (see NewVerifier), which is structurally incapable of issuing anything.
Take a Verifier, not a Signer, in any function that only needs to check a token — authithttp.Validate does. A Signer satisfies Verifier, so narrowing a parameter this way never breaks a caller.