Documentation
¶
Overview ¶
Package tokenexchange implements RFC 8693 token exchange for the authorization server. It provides validation of subject tokens that were issued by the same authorization server, enabling agents to act on behalf of users through delegation.
Index ¶
- func Factory(delegationLifespan time.Duration) (server.Factory, error)
- type Handler
- func (*Handler) CanHandleTokenEndpointRequest(_ context.Context, requester fosite.AccessRequester) bool
- func (*Handler) CanSkipClientAuth(_ context.Context, _ fosite.AccessRequester) bool
- func (h *Handler) HandleTokenEndpointRequest(ctx context.Context, requester fosite.AccessRequester) error
- func (h *Handler) PopulateTokenEndpointResponse(ctx context.Context, requester fosite.AccessRequester, ...) error
- type MayActClaim
- type SubjectTokenValidator
- type ValidatedClaims
Constants ¶
This section is empty.
Variables ¶
This section is empty.
Functions ¶
func Factory ¶
Factory returns a server.Factory that creates a token exchange Handler. The delegationLifespan parameter sets the maximum lifetime for delegated tokens; the actual lifetime is the minimum of this value and the subject token's remaining lifetime. Returns an error if delegationLifespan is not in (0, server.MaxAccessTokenLifespan]: a zero or negative value would produce delegated tokens with an expiry already in the past, and a value above the access token ceiling would only be caught at request time by the per-request cap.
Types ¶
type Handler ¶
type Handler struct {
*oauth2.HandleHelper
// contains filtered or unexported fields
}
Handler implements RFC 8693 token exchange for user-to-agent delegation.
When an authenticated OAuth client (the acting agent) presents a user's JWT as subject_token, the handler validates the token and issues a delegated JWT with sub=user and an act claim containing the client's identity, per RFC 8693 Section 4.1.
Subject tokens are intentionally reusable within their lifetime: per RFC 8693's security considerations, a token exchange does not invalidate the subject token, so the same subject token may be exchanged more than once. Replay is bounded by the delegated token's lifetime cap (min(subject-remaining, delegation)), not by single-use tracking; per-jti single-use enforcement is deferred to the broader M2M/sender-constrained- token effort.
func (*Handler) CanHandleTokenEndpointRequest ¶
func (*Handler) CanHandleTokenEndpointRequest(_ context.Context, requester fosite.AccessRequester) bool
CanHandleTokenEndpointRequest returns true if the request's grant_type is the RFC 8693 token exchange grant type.
func (*Handler) CanSkipClientAuth ¶
CanSkipClientAuth returns false because client authentication is required for all token exchange requests.
func (*Handler) HandleTokenEndpointRequest ¶
func (h *Handler) HandleTokenEndpointRequest(ctx context.Context, requester fosite.AccessRequester) error
HandleTokenEndpointRequest validates the token exchange request parameters, verifies the subject token, and constructs a delegated session with the act claim.
The delegated token's lifetime is the minimum of the subject token's remaining lifetime and the configured delegation lifespan.
func (*Handler) PopulateTokenEndpointResponse ¶
func (h *Handler) PopulateTokenEndpointResponse( ctx context.Context, requester fosite.AccessRequester, responder fosite.AccessResponder, ) error
PopulateTokenEndpointResponse issues the delegated access token and sets the RFC 8693 issued_token_type in the response.
type MayActClaim ¶
type MayActClaim struct {
Sub string `json:"sub"`
}
MayActClaim represents the RFC 8693 §4.1 may_act claim from a subject token. It identifies the party authorized to act on behalf of the subject.
type SubjectTokenValidator ¶
type SubjectTokenValidator struct {
// contains filtered or unexported fields
}
SubjectTokenValidator validates subject tokens presented during RFC 8693 token exchange. It verifies that the token was issued by this authorization server by checking the signature against the server's own JWKS, and validates standard JWT claims.
func NewSubjectTokenValidator ¶
func NewSubjectTokenValidator( jwks *jose.JSONWebKeySet, issuer string, allowedAudiences []string, ) (*SubjectTokenValidator, error)
NewSubjectTokenValidator creates a new validator for subject tokens. The jwks parameter must be non-nil and contain only the authorization server's public signing keys (e.g. AuthorizationServerConfig.PublicJWKS) — the validator only ever verifies signatures, so it must not be handed private key material. The issuer parameter is the expected "iss" claim value. allowedAudiences is the set of audiences this server accepts in a subject token's "aud" claim; per the same secure default as AuthorizationServerConfig.AllowedAudiences, an empty allowedAudiences rejects every subject token rather than skipping the check.
func (*SubjectTokenValidator) Validate ¶
func (v *SubjectTokenValidator) Validate(_ context.Context, rawToken string) (*ValidatedClaims, error)
Validate parses and verifies a raw JWT subject token. It checks the signature against the server's JWKS, validates issuer and audience, ensures the token is not expired, and requires a subject claim for delegation.
The subject token's "aud" claim is checked against allowedAudiences, but this validator deliberately does not require that the authorization server itself (i.e. the token endpoint) be among those audiences. RFC 8693 leaves subject-token validation criteria out of scope, and ToolHive's vMCP flow legitimately exchanges tokens addressed to a downstream/upstream resource rather than the AS. The residual cross-resource risk is mitigated elsewhere: the token is still pinned to the server-wide allowedAudiences, and the handler enforces the requested resource against the client's registered audiences.
Returns the validated claims on success, or a descriptive error on failure.
type ValidatedClaims ¶
type ValidatedClaims struct {
// Subject is the user identity from the "sub" claim (required for delegation).
Subject string
// Issuer is the token issuer from the "iss" claim.
Issuer string
// Audience is the list of intended recipients from the "aud" claim.
Audience []string
// Expiry is the token expiration time from the "exp" claim.
Expiry time.Time
// IssuedAt is the token issuance time from the "iat" claim.
IssuedAt time.Time
// JWTID is the unique token identifier from the "jti" claim.
JWTID string
// Name is the user's display name from the custom "name" claim.
Name string
// Email is the user's email address from the custom "email" claim.
Email string
// ClientID is the OAuth client ID from the custom "client_id" claim.
ClientID string
// Scopes is the space-delimited scope string from the "scope" claim.
// Empty if the subject token carries no scope claim.
Scopes string
// MayAct holds the authorized actor from the "may_act" claim (RFC 8693 §4.1).
// Nil when the subject token does not carry a may_act claim.
MayAct *MayActClaim
// Extra contains all non-standard claims not captured by other fields.
Extra map[string]any
}
ValidatedClaims holds the verified claims extracted from a subject token. All fields are populated from a successfully validated JWT that was issued by this authorization server.