Help us shape the future of Go! Take the 2026 Go Developer Survey

oauthcore

package
v0.21.0 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Oct 26, 2026 License: MIT Imports: 14 Imported by: 0

Documentation

Overview

Package oauthcore holds what SystemForge's two Fosite-based authorization servers, identity/systemauth and identity/oauth, share to issue and accept DPoP-bound tokens (RFC 9449) and to rotate signing keys N / N-1:

  • KeyRing: a signing key plus the verification keys tokens are checked against by kid, its JWKS and its Fosite signer;
  • ParseTokenRequest, VerifyTokenRequestProof, CheckCodeDPoPBinding, BindDPoPGrant, DPoPProofRequiredError and AuthorizationDPoPJKT for the token and authorization endpoints;
  • AuthenticateAccessToken, AccessTokenCredentials, TokenAuthError and WriteTokenAuthError for resource endpoints;
  • SetConfirmationClaim for the cnf claim of JWT access tokens.

The package is internal: it is not part of SystemForge's public API. It does not depend on either server's session type; callers pass the DPoP binding of their own sessions (a key thumbprint, or a function that reads it).

Index

Constants

View Source
const DPoPNonceHeader = "DPoP-Nonce"

DPoPNonceHeader is the response header carrying a server nonce.

View Source
const MaxTokenRequestBody = 1 << 20

MaxTokenRequestBody is the largest token request body accepted, ample for any grant.

Variables

This section is empty.

Functions

func AccessTokenCredentials

func AccessTokenCredentials(r *http.Request) (scheme, token string)

AccessTokenCredentials returns the scheme ("bearer" or "dpop") and the access token of a request: the Authorization header, or for Bearer the form body of a POST (RFC 6750 §2.2). Query parameters are not accepted.

func AuthorizationDPoPJKT

func AuthorizationDPoPJKT(ar fosite.AuthorizeRequester) (string, error)

AuthorizationDPoPJKT returns the dpop_jkt of an authorization request (RFC 9449 §10), which binds the code to that key, or an error for a malformed one.

func BearerChallenge

func BearerChallenge(code, description, scope string) string

BearerChallenge is an RFC 6750 §3 Bearer challenge.

func BindDPoPGrant

func BindDPoPGrant(bound *string, proof *dpopproof.Proof, requireProof bool) error

BindDPoPGrant applies the DPoP policy to a token request Fosite has parsed and binds the tokens it issues to the proof's key. bound is the DPoP binding of the request's session (its key thumbprint field), which is set to the proof's key:

  • without a proof, the request is refused when requireProof is set (the client has no bearer-token exception);
  • a code issued for a dpop_jkt, and a refresh token bound to a key, need a proof by that key (RFC 9449 §10, §5);
  • with a proof, the access and refresh tokens are bound to its key.

func CheckCodeDPoPBinding

func CheckCodeDPoPBinding(ctx context.Context, codes CodeStorage, strategy oauth2.AuthorizeCodeStrategy, r *http.Request, proof *dpopproof.Proof, session fosite.Session, jkt SessionJKT) error

CheckCodeDPoPBinding refuses an authorization_code token request whose code is bound to a DPoP key (dpop_jkt, RFC 9449 §10) when the request carries no proof by that key. Call it before Fosite handles the request: Fosite's grant handling has side effects even when the exchange is then refused (it deletes the code's PKCE request, which with some storage makes the code unusable), so a refused exchange must not reach it. The code is looked up by its signature under codes into session, an empty session of the server's type, and jkt reads its binding. A request for another grant, or a code that cannot be read, is left to Fosite.

func DPoPChallenge

func DPoPChallenge(code, description string) string

DPoPChallenge is the DPoP WWW-Authenticate challenge (RFC 9449 §7.1).

func DPoPProofRequiredError

func DPoPProofRequiredError() *fosite.RFC6749Error

DPoPProofRequiredError is the token endpoint error for a client that must use DPoP and sent no proof.

func ParseTokenRequest

func ParseTokenRequest(w http.ResponseWriter, r *http.Request) error

ParseTokenRequest limits the body of a token endpoint request to MaxTokenRequestBody and parses its form. Call it before anything reads the request (FormValue, PostFormValue or Fosite), so a large body cannot exhaust memory. The error is an RFC 6749 error to write with WriteAccessError.

func SetConfirmationClaim

func SetConfirmationClaim(jkt string, claims *jwt.JWTClaims)

SetConfirmationClaim sets or clears the cnf claim of JWT access tokens to match jkt, the session's DPoP binding.

func VerifyTokenRequestProof

func VerifyTokenRequestProof(ctx context.Context, checker *dpopproof.Checker, r *http.Request, tokenURL string) (*dpopproof.Proof, error)

VerifyTokenRequestProof verifies the DPoP proof of a token request to the token endpoint at tokenURL (its external URL, as discovery advertises it), if the request carries one. A request without one returns nil: whether a proof is required depends on the client. The error is an RFC 6749 error to write with WriteAccessError: invalid_dpop_proof, use_dpop_nonce or server_error.

func WriteChallengeError

func WriteChallengeError(w http.ResponseWriter, status int, challenges []string, code, description string)

WriteChallengeError writes an error response with WWW-Authenticate challenges and, when code is set, an error body.

func WriteTokenAuthError

func WriteTokenAuthError(w http.ResponseWriter, e *TokenAuthError)

WriteTokenAuthError writes the response for a request whose access token AuthenticateAccessToken did not accept: 401 with WWW-Authenticate challenges (RFC 6750 §3, RFC 9449 §7.1) and a DPoP-Nonce to retry with when one is needed, or 500 when the token could not be checked.

Types

type CodeStorage

type CodeStorage interface {
	GetAuthorizeCodeSession(ctx context.Context, signature string, session fosite.Session) (fosite.Requester, error)
}

CodeStorage reads authorization codes for CheckCodeDPoPBinding.

type KeyRing

type KeyRing struct {
	// contains filtered or unexported fields
}

KeyRing holds a signing key and the keys tokens are verified with: the signing key and every verification key, by kid. A token's kid selects the key that verifies it, and a token with any other kid is refused.

func NewKeyRing

func NewKeyRing(signing *rsa.PrivateKey, signingKID string, verification []VerificationKey) *KeyRing

NewKeyRing returns a key ring that signs with signing, published under signingKID, and verifies also with the verification keys. The caller has resolved and checked the keys: every kid is set, and no two keys share one.

func (*KeyRing) JWKS

func (r *KeyRing) JWKS() jose.JSONWebKeySet

JWKS returns the published key set: the signing key first, then the verification keys.

func (*KeyRing) Signer

func (r *KeyRing) Signer() jwt.Signer

Signer returns the Fosite JWT signer of the ring: it signs with the signing key and verifies with the key the token's kid selects.

func (*KeyRing) SigningKeyID

func (r *KeyRing) SigningKeyID() string

SigningKeyID returns the kid of the signing key, to set in the header of every JWT signed with the ring.

type SessionJKT

type SessionJKT func(fosite.Session) string

SessionJKT returns the RFC 7638 thumbprint of the DPoP key a Fosite session is bound to: "" for an unbound session, a nil one, or one of another type than the server's own.

type TokenAuthError

type TokenAuthError struct {
	// contains filtered or unexported fields
}

TokenAuthError is why a request's access token was not accepted.

func AuthenticateAccessToken

func AuthenticateAccessToken(r *http.Request, checker *dpopproof.Checker, htu string, introspect func(ctx context.Context, token string) (fosite.AccessRequester, error), jkt SessionJKT) (fosite.AccessRequester, *TokenAuthError)

AuthenticateAccessToken validates the access token of a request to a resource at htu, the request's external URL without query. introspect returns the request of an active, acceptable access token, or an error to refuse it with invalid_token; jkt reads the DPoP binding of its session. A DPoP-bound token must come with the DPoP scheme and a fresh, unreplayed proof by its key (checked by checker) for the request's method and htu, carrying the token's hash; an unbound token must come with the Bearer scheme. A request without a token returns a *TokenAuthError for which NoToken reports true. Write a refusal with WriteTokenAuthError.

func UnauthorizedError

func UnauthorizedError(code, description string) *TokenAuthError

UnauthorizedError is a 401 refusal with the RFC 6750 error code and description (both may be empty), challenging with both schemes.

func (*TokenAuthError) Error

func (e *TokenAuthError) Error() string

func (*TokenAuthError) NoToken

func (e *TokenAuthError) NoToken() bool

NoToken reports whether the request carried no access token.

func (*TokenAuthError) StatusCode

func (e *TokenAuthError) StatusCode() int

StatusCode is the HTTP status of the refusal: 401, or 500 when the token could not be checked.

func (*TokenAuthError) Unwrap

func (e *TokenAuthError) Unwrap() error

type VerificationKey

type VerificationKey struct {
	KeyID     string
	PublicKey *rsa.PublicKey
}

VerificationKey is a key tokens are verified with besides the signing key: the previous signing key N-1 after a rotation, or a pre-staged next key.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL