Documentation
¶
Index ¶
- Constants
- Variables
- func ForcedScope(client fosite.Client) (string, bool)
- func InvalidBearerTokenError(cause error) error
- func IsDynamicClient(c fosite.Client) bool
- func LegacyPKCEFactory(config fosite.Configurator, storage any, strategy any) any
- func RefreshTokenGrantFactory(config fosite.Configurator, store any, strategy any) any
- func TokenGrantTypes() []string
- func ValidateDynamicClientPKCE(client fosite.Client, challenge, method string) error
- type AccessTokenDenylister
- type Config
- type FositeStorage
- type IntrospectionResponse
- type Issuer
- func (i *Issuer) IssueAuthorizationCode(ctx context.Context, w http.ResponseWriter, ar fosite.AuthorizeRequester, ...) error
- func (i *Issuer) IssueFromFederationSession(ctx context.Context, w http.ResponseWriter, session *federation.Session, ...) error
- func (i *Issuer) WriteErrorFromFederationSession(ctx context.Context, w http.ResponseWriter, session *federation.Session, ...) error
- type LegacyPKCEHandler
- type Provider
- func (p *Provider) AccessTokenAudience() string
- func (p *Provider) ClientByID(ctx context.Context, id string) (fosite.Client, error)
- func (p *Provider) CreateSession(userInfo identity.UserInfo, scopes []string) *Session
- func (p *Provider) GetConfig() *fosite.Config
- func (p *Provider) Introspect(ctx context.Context, r *http.Request) (*IntrospectionResponse, error)
- func (p *Provider) NewAccessToken(ctx context.Context, r *http.Request, session fosite.Session) (fosite.AccessRequester, fosite.AccessResponder, error)
- func (p *Provider) NewAuthorizeRequest(ctx context.Context, r *http.Request) (fosite.AuthorizeRequester, error)
- func (p *Provider) NewAuthorizeResponse(ctx context.Context, ar fosite.AuthorizeRequester, session fosite.Session) (fosite.AuthorizeResponder, error)
- func (p *Provider) NewIssuer() *Issuer
- func (p *Provider) WriteAuthorizeError(ctx context.Context, rw http.ResponseWriter, ar fosite.AuthorizeRequester, ...)
- type RefreshTokenGrantHandler
- type Session
- type UserInfoFetcher
Constants ¶
const ( AuthorizeCodePrefix = "gliamac-" AccessTokenPrefix = "gliamat-" RefreshTokenPrefix = "gliamrt-" )
Prefixes identify IAM-issued tokens by inspection, without decoding them. See docs/routable-tokens.md for the byte layout and why Workhorse and the Cells HTTP Router each care.
const RefreshTokenNeverExpires time.Duration = 0
RefreshTokenNeverExpires is the RefreshTokenLifespan of a refresh token that never expires, as doorkeeper's do.
Variables ¶
var ( ErrTokenNotOwned = errors.New("token was not issued to the calling client") // A token minted for an exchange that failed after the claims fetch is // denylisted but keeps its row, so the lookup alone reports it active. ErrTokenRevoked = errors.New("access token is on the revocation denylist") )
Inactive reasons are for the operator log only: RFC 7662 §2.2 forbids telling the client why a token is inactive.
var ErrInvalidBearerToken = errors.New("bearer token did not resolve")
ErrInvalidBearerToken marks a bearer caller whose token did not resolve. fosite compares RFC errors by code and status, which ErrTokenExpired shares, so a plain sentinel is needed to tell this one apart.
var ErrLookupFailed = errors.New("token lookup failed")
ErrLookupFailed is a server error, never an inactive answer: active=false would make the resource server deny a token that may be fine, and it may cache that answer.
var ErrPKCERequiredForDynamicClients = fosite.ErrInvalidRequest.
WithHint("This client must use PKCE with code_challenge_method=S256 when performing the authorize code flow.").
WithDebug("PKCE with S256 is required for dynamically registered clients.")
Functions ¶
func InvalidBearerTokenError ¶ added in v1.93.0
InvalidBearerTokenError is the 401 for a bearer that did not resolve. The client gets one fixed description; the reason goes into the wrapped cause for the log.
func IsDynamicClient ¶ added in v1.55.3
func LegacyPKCEFactory ¶
func LegacyPKCEFactory(config fosite.Configurator, storage any, strategy any) any
LegacyPKCEFactory satisfies the fosite compose.Factory signature. The storage and strategy parameters are interface{} because fosite's compose pipeline passes them untyped. The Factory signature does not allow returning an error, so incorrect wiring panics at startup with a clear message rather than silently producing a broken handler.
func RefreshTokenGrantFactory ¶ added in v1.82.0
func RefreshTokenGrantFactory(config fosite.Configurator, store any, strategy any) any
RefreshTokenGrantFactory builds fosite's refresh handler over a storage that hides inactive tokens from its reuse detection.
func TokenGrantTypes ¶ added in v1.79.0
func TokenGrantTypes() []string
TokenGrantTypes returns the grant_type values the token endpoint handlers composed in NewOAuthProviderWithConfig accept. A test fails if the two drift.
Types ¶
type AccessTokenDenylister ¶ added in v1.77.1
type AccessTokenDenylister interface {
DenylistAccessToken(ctx context.Context, jti uuid.UUID, expiresAt time.Time) error
IsAccessTokenRevoked(ctx context.Context, jti uuid.UUID) (bool, error)
}
AccessTokenDenylister records a minted access token as revoked and reports whether one is. Storage implements it over the revoked_tokens table.
type Config ¶
type Config struct {
AccessTokenLifespan time.Duration
// <= 0 never expires, stored as a NULL expires_at. Doorkeeper parity.
RefreshTokenLifespan time.Duration
AuthorizeCodeLifespan time.Duration
IDTokenLifespan time.Duration
HashCost int
HMACSecret []byte
AllowInsecureHTTP bool
EnforcePKCE bool
EnableOpenIDConnect bool
IssuerURL string
LoginURL string
ConsentURL string
// AccessTokenAudience identifies the resource server that consumes IAM-issued
// JWT access tokens. Set as the `aud` claim per RFC 9068 §2.2.
AccessTokenAudience string
ClientSecretsHasher fosite.Hasher
JwkProvider jwk.Provider
IDTokenClaimsFetcher UserInfoFetcher
}
func NewOAuthConfigFromConfig ¶
func NewOAuthConfigFromConfig(cfg authconfig.OAuthServerConfig) (*Config, error)
type FositeStorage ¶
type FositeStorage interface {
fosite.ClientManager
oauth2.AuthorizeCodeStorage
oauth2.AccessTokenStorage
oauth2.RefreshTokenStorage
oauth2.TokenRevocationStorage
openid.OpenIDConnectRequestStorage
pkce.PKCERequestStorage
}
FositeStorage is the union of fosite storage interfaces that the OAuth provider needs to drive the authorize and token endpoints. It lives here (rather than in the concrete storage package) so the provider depends on the contract, not on a specific storage implementation.
type IntrospectionResponse ¶ added in v1.81.0
type IntrospectionResponse struct {
Active bool
CallerClientID string
TokenUse fosite.TokenUse
Requester fosite.AccessRequester
InactiveReason error
}
CallerClientID is the client that authenticated, which with Basic credentials is not the form's client_id. InactiveReason is the token's validation error, ErrTokenRevoked, or ErrTokenNotOwned.
type Issuer ¶
type Issuer struct {
// contains filtered or unexported fields
}
Issuer turns a resolved user identity into an OAuth authorization-code redirect. It is the small slice of the authorize flow that runs after the user has been authenticated — granting requested scopes, attaching a session to the request, and writing the response that carries the code back to the client.
Flows that already have a user identity in hand — notably the federated login callback — use the Issuer to emit a code directly, without round-tripping the user agent through /oauth/authorize.
func (*Issuer) IssueAuthorizationCode ¶
func (i *Issuer) IssueAuthorizationCode(ctx context.Context, w http.ResponseWriter, ar fosite.AuthorizeRequester, userInfo identity.UserInfo) error
func (*Issuer) IssueFromFederationSession ¶
func (i *Issuer) IssueFromFederationSession(ctx context.Context, w http.ResponseWriter, session *federation.Session, userInfo identity.UserInfo, additionalParams map[string]string) error
IssueFromFederationSession synthesizes an authorize request from the stored federation session and issues an authorization code for userInfo. Callers use this to skip the extra /oauth/authorize round trip that would otherwise be needed after a federated login completes.
func (*Issuer) WriteErrorFromFederationSession ¶ added in v1.10.0
func (i *Issuer) WriteErrorFromFederationSession(ctx context.Context, w http.ResponseWriter, session *federation.Session, err error) error
WriteErrorFromFederationSession synthesizes an authorize request from the stored federation session and asks fosite to write an RFC 6749 error response to w. In the normal case fosite emits a 303 redirect back to the session's redirect URI; if it cannot reconstruct a valid client and redirect URI on the AuthorizeRequester (e.g. the referenced client has since been removed), it degrades to a JSON body instead. That degradation is intentional — we accept it rather than re-implementing a parallel redirect path. The returned error, when non-nil, describes a failure encountered while preparing the response and is intended for the caller to log; the response itself is best-effort.
type LegacyPKCEHandler ¶
LegacyPKCEHandler replaces fosite's stock PKCE handler so the authorization code flow matches the doorkeeper provider IAM is replacing: doorkeeper is laxer than fosite's RFC 7636-strict checks and clients rely on that. The specific deviations are documented on the forked methods below.
It does not enforce PKCE; that belongs at the authorize endpoint via fosite's EnforcePKCE, which rejects a no-challenge request before a code is minted. The exception is dynamically registered clients, which must always use S256.
func (*LegacyPKCEHandler) HandleAuthorizeEndpointRequest ¶
func (c *LegacyPKCEHandler) HandleAuthorizeEndpointRequest(ctx context.Context, ar fosite.AuthorizeRequester, resp fosite.AuthorizeResponder) error
HandleAuthorizeEndpointRequest rejects a challenge that omits code_challenge_method before delegating to fosite: doorkeeper requires an explicit method (it does not default to "plain"), whereas fosite with plain enabled would treat an empty method as plain.
func (*LegacyPKCEHandler) HandleTokenEndpointRequest ¶
func (c *LegacyPKCEHandler) HandleTokenEndpointRequest(ctx context.Context, request fosite.AccessRequester) error
type Provider ¶
type Provider struct {
fosite.OAuth2Provider
// contains filtered or unexported fields
}
func NewOAuthProviderWithConfig ¶
func NewOAuthProviderWithConfig(store FositeStorage, oauthConfig *Config) (*Provider, error)
func (*Provider) AccessTokenAudience ¶ added in v1.10.0
AccessTokenAudience returns the resource-server identifier set as the `aud` claim on issued JWT access tokens (RFC 9068 §2.2).
func (*Provider) ClientByID ¶ added in v1.55.3
func (*Provider) CreateSession ¶
func (*Provider) Introspect ¶ added in v1.81.0
Introspect answers an RFC 7662 request. fosite's NewIntrospectionRequest admits confidential clients only and never says which client authenticated, so Doorkeeper's rules cannot be applied through it: public clients introspect on client_id alone, and a token the caller does not own is inactive.
func (*Provider) NewAccessToken ¶ added in v1.52.0
func (p *Provider) NewAccessToken(ctx context.Context, r *http.Request, session fosite.Session) (fosite.AccessRequester, fosite.AccessResponder, error)
NewAccessToken runs a token endpoint request. Every outbound call the exchange needs happens before fosite writes (see claimsEnrichingStrategy), so a failure leaves nothing to compensate for except the access token the claims fetch sent to Rails, which is denylisted.
func (*Provider) NewAuthorizeRequest ¶ added in v1.68.1
func (p *Provider) NewAuthorizeRequest(ctx context.Context, r *http.Request) (fosite.AuthorizeRequester, error)
doorkeeper treats state as optional and leaves a blank one out of the redirect. fosite requires state, but checks it last in NewAuthorizeRequest, so ignoring that one error keeps every other check.
func (*Provider) NewAuthorizeResponse ¶ added in v1.68.1
func (p *Provider) NewAuthorizeResponse(ctx context.Context, ar fosite.AuthorizeRequester, session fosite.Session) (fosite.AuthorizeResponder, error)
Every authorize path issues its code here, so this is where a code without a subject is refused. fosite checks the subject only for ID tokens.
func (*Provider) NewIssuer ¶
NewIssuer returns an authorization-code issuer wired up against this provider. The handler packages call this so they don't need to construct the issuer (or know how this provider builds sessions) themselves.
func (*Provider) WriteAuthorizeError ¶ added in v1.68.1
func (p *Provider) WriteAuthorizeError(ctx context.Context, rw http.ResponseWriter, ar fosite.AuthorizeRequester, err error)
fosite writes the error redirect itself, so a blank state is removed from its Location header.
type RefreshTokenGrantHandler ¶ added in v1.82.0
type RefreshTokenGrantHandler struct {
*oauth2.RefreshTokenGrantHandler
}
RefreshTokenGrantHandler rejects a reused or revoked refresh token on its own, as GitLab's Doorkeeper does (gitlab-org/gitlab#604892). fosite treats it as theft per RFC 6819 §5.2.2.3 and revokes the grant's whole token family. With no revocation, the log line is the only trace of a stolen token.
func (*RefreshTokenGrantHandler) HandleTokenEndpointRequest ¶ added in v1.82.0
func (c *RefreshTokenGrantHandler) HandleTokenEndpointRequest(ctx context.Context, request fosite.AccessRequester) error
HandleTokenEndpointRequest restores the hint for an inactive token, which fosite answered as not found, and logs it.
func (*RefreshTokenGrantHandler) PopulateTokenEndpointResponse ¶ added in v1.82.0
func (c *RefreshTokenGrantHandler) PopulateTokenEndpointResponse(ctx context.Context, request fosite.AccessRequester, responder fosite.AccessResponder) error
PopulateTokenEndpointResponse maps ErrInactiveToken from the rotation write, which is how a de-authorization during the exchange surfaces, to invalid_grant as Doorkeeper does. fosite answers invalid_request with a retry hint.
type Session ¶
func NewEmptySession ¶
func NewEmptySession() *Session
NewEmptySession returns a zero-valued Session with nested pointer fields pre-allocated. It is used as the unmarshal target when restoring a stored session from the authorization-code store — the session type must implement JWTSessionContainer so fosite's JWT strategy can mint access tokens.
func NewSessionWithClaims ¶
func (*Session) Clone ¶ added in v1.39.0
Clone deep-copies the session while preserving the concrete *Session type, so the JWT access-token strategy still sees a JWTSessionContainer. Without it the embedded *openid.DefaultSession.Clone() is promoted and returns a bare *openid.DefaultSession, which breaks the refresh-token grant.
func (*Session) GetJWTClaims ¶
func (s *Session) GetJWTClaims() jwt.JWTClaimsContainer