Documentation
¶
Overview ¶
Package tokenformat provides pluggable token generation strategies. Access tokens can be opaque (hex) or JWT; refresh tokens are always opaque.
Index ¶
- Variables
- func GenerateRefreshToken() (string, error)
- func IsJWT(token string) bool
- type ActClaim
- type Confirmation
- type Format
- type JWT
- func (j *JWT) Algorithm() string
- func (j *JWT) ConfiguredAudience() string
- func (j *JWT) GenerateAccessToken(claims TokenClaims) (string, error)
- func (j *JWT) HMACKey() ([]byte, bool)
- func (j *JWT) KeyID() string
- func (j *JWT) Name() string
- func (j *JWT) PublicKey() any
- func (j *JWT) ValidateAccessToken(tokenStr string) (*TokenClaims, error)
- type JWTConfig
- type Opaque
- type TokenClaims
Constants ¶
This section is empty.
Variables ¶
var ( ErrInvalidToken = errors.New("tokenformat: invalid token") ErrTokenExpired = errors.New("tokenformat: token expired") ErrUnsignedToken = errors.New("tokenformat: unsigned or tampered token") )
Sentinel errors.
Functions ¶
func GenerateRefreshToken ¶
GenerateRefreshToken creates an opaque refresh token. This is a standalone function because refresh tokens are always opaque regardless of the access token format.
Types ¶
type ActClaim ¶
ActClaim is the RFC 8693 `act` claim: the party acting on behalf of the subject. It nests, so a chain of delegations is a chain of ActClaims with the immediate actor outermost. Subject carries a principal.Ref in its "kind:id" string form.
type Confirmation ¶
type Confirmation struct {
JKT string `json:"jkt,omitempty"`
}
Confirmation is the RFC 7800 cnf claim. Only the jkt member is used.
type Format ¶
type Format interface {
// Name returns the format identifier ("opaque" or "jwt").
Name() string
// GenerateAccessToken produces a token string from claims.
GenerateAccessToken(claims TokenClaims) (string, error)
// ValidateAccessToken parses and validates a token, returning the
// embedded claims. Returns ErrInvalidToken or ErrTokenExpired on
// failure.
ValidateAccessToken(token string) (*TokenClaims, error)
}
Format generates and validates access tokens. Refresh tokens are always opaque (must be revocable via the store) so they are not part of this interface.
type JWT ¶
type JWT struct {
// contains filtered or unexported fields
}
JWT generates and validates JWT access tokens.
func (*JWT) ConfiguredAudience ¶
ConfiguredAudience returns the app-wide default "aud" this format stamps on tokens whose claims carry no audience of their own. Empty when unset.
Callers need it so a session row can record the same audience the minted token actually carries. When the row says nothing and the claim says "https://api.example.com", the two disagree, and any check that reads one after the other gets two different answers about the same credential.
func (*JWT) GenerateAccessToken ¶
func (j *JWT) GenerateAccessToken(claims TokenClaims) (string, error)
func (*JWT) HMACKey ¶
HMACKey returns the symmetric signing key when this JWT format uses an HMAC algorithm (HS256/HS384/HS512). The second return is false for RSA or ECDSA configurations, where no symmetric key exists.
This is intended for callers that need a process-wide symmetric secret to derive other HMAC keys from (e.g. the dashboard nonce signer). Never expose the returned bytes over the network.
func (*JWT) ValidateAccessToken ¶
func (j *JWT) ValidateAccessToken(tokenStr string) (*TokenClaims, error)
type JWTConfig ¶
type JWTConfig struct {
// SigningMethod is the JWT signing algorithm (e.g., jwt.SigningMethodRS256,
// jwt.SigningMethodES256, jwt.SigningMethodHS256).
SigningMethod jwt.SigningMethod
// SigningKey is the private key used to sign tokens.
// - RSA: *rsa.PrivateKey
// - ECDSA: *ecdsa.PrivateKey
// - HMAC: []byte
SigningKey any
// VerifyKey is the public key used to verify tokens.
// For HMAC, this should be the same as SigningKey.
// - RSA: *rsa.PublicKey
// - ECDSA: *ecdsa.PublicKey
// - HMAC: []byte
VerifyKey any
// KeyID is the "kid" header value for key rotation support.
KeyID string
// Issuer is the "iss" claim (e.g., "https://auth.example.com").
Issuer string
// Audience is the "aud" claim (e.g., "api-service").
Audience string
}
JWTConfig configures the JWT token format.
type Opaque ¶
type Opaque struct{}
Opaque generates 64-character hex tokens (the default AuthSome format). Validation always returns ErrInvalidToken because opaque tokens must be resolved via the session store — they carry no embedded claims.
func (Opaque) GenerateAccessToken ¶
func (Opaque) GenerateAccessToken(_ TokenClaims) (string, error)
func (Opaque) ValidateAccessToken ¶
func (Opaque) ValidateAccessToken(_ string) (*TokenClaims, error)
type TokenClaims ¶
type TokenClaims struct {
UserID string `json:"sub"`
AppID string `json:"app_id"`
EnvID string `json:"env_id,omitempty"`
OrgID string `json:"org_id,omitempty"`
SessionID string `json:"sid"`
Scopes []string `json:"scopes,omitempty"`
// Audience holds the resource identifiers this token was granted for
// (RFC 8707), emitted as the `aud` claim. Empty means unrestricted.
//
// This overrides JWTConfig.Audience when set. The config value is a single
// static audience for a whole app, which predates resource indicators and
// stays as the fallback so existing deployments are unaffected.
Audience []string `json:"aud,omitempty"`
// DPoPJKT is the RFC 7638 thumbprint this token is bound to (RFC 9449).
// Empty means an unbound bearer token. Serialised as the cnf.jkt claim.
DPoPJKT string `json:"-"`
IssuedAt time.Time
ExpiresAt time.Time
// Act is present for a delegated token and nil otherwise. Impersonation
// emits no act claim at all (RFC 8693 section 1.1), which is why the full
// record lives on the session row rather than in the token.
Act *ActClaim `json:"act,omitempty"`
// PrincipalKind names the kind of caller this token belongs to, using the
// values the principal package defines: "user", "agent", "workload" or
// "service_account". Empty means user, so tokens minted before this field
// existed keep validating.
//
// It is a string and not principal.Kind because tokenformat sits below
// principal in the import graph, and a claims struct is a wire format that
// should not drag a domain package in behind it.
PrincipalKind string `json:"pk,omitempty"`
// PrincipalID is the caller's id, duplicating what sub carries. It is here
// so a consumer can read the caller without first deciding whether sub
// means a user. That ambiguity is what made the JWT auth path parse an
// empty sub and panic on a machine token.
PrincipalID string `json:"pid,omitempty"`
// ClientID names the OAuth2 client a token was issued to, and is empty
// for a first-party login. It is the RFC 9068 client_id claim. The auth
// middleware reads it to park a third-party token until a RequireScope
// route admits it; an opaque token gets the same answer from its session
// row, but a JWT is validated without loading one, so the token has to
// say it.
ClientID string `json:"client_id,omitempty"`
}
TokenClaims carries the identity payload embedded in an access token.