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.
func (*TokenClaims) IsDelegated ¶ added in v1.9.1
func (c *TokenClaims) IsDelegated() bool
IsDelegated reports whether the token acts for its sub without being that user's own sign-in. It does when it carries an RFC 8693 act claim, or when it belongs to a principal other than a user and still names a user as sub. A machine token whose sub is the caller itself acts for nobody. An impersonated token carries no act claim (RFC 8693 section 1.1), so it reads as the user's own, the same as an impersonation session.