mirror of https://github.com/ory/hydra
448 lines
18 KiB
Go
448 lines
18 KiB
Go
// Copyright © 2025 Ory Corp
|
|
// SPDX-License-Identifier: Apache-2.0
|
|
|
|
package fosite
|
|
|
|
import (
|
|
"context"
|
|
"net/http"
|
|
"net/url"
|
|
"time"
|
|
|
|
"golang.org/x/text/language"
|
|
)
|
|
|
|
type TokenUse = TokenType
|
|
|
|
type TokenType string
|
|
|
|
type GrantType string
|
|
|
|
const (
|
|
AccessToken TokenType = "access_token"
|
|
RefreshToken TokenType = "refresh_token"
|
|
AuthorizeCode TokenType = "authorize_code"
|
|
IDToken TokenType = "id_token"
|
|
UserCode TokenType = "user_code"
|
|
DeviceCode TokenType = "device_code"
|
|
// PushedAuthorizeRequestContext represents the PAR context object
|
|
PushedAuthorizeRequestContext TokenType = "par_context"
|
|
|
|
GrantTypeImplicit GrantType = "implicit"
|
|
GrantTypeRefreshToken GrantType = "refresh_token"
|
|
GrantTypeAuthorizationCode GrantType = "authorization_code"
|
|
GrantTypePassword GrantType = "password"
|
|
GrantTypeClientCredentials GrantType = "client_credentials"
|
|
GrantTypeJWTBearer GrantType = "urn:ietf:params:oauth:grant-type:jwt-bearer" //nolint:gosec // this is not a hardcoded credential
|
|
GrantTypeDeviceCode GrantType = "urn:ietf:params:oauth:grant-type:device_code" //nolint:gosec // this is not a hardcoded credential
|
|
|
|
BearerAccessToken string = "bearer"
|
|
)
|
|
|
|
// OAuth2Provider is an interface that enables you to write OAuth2 handlers with only a few lines of code.
|
|
// Check Fosite for an implementation of this interface.
|
|
type OAuth2Provider interface {
|
|
// NewAuthorizeRequest returns an AuthorizeRequest.
|
|
//
|
|
// The following specs must be considered in any implementation of this method:
|
|
// * https://tools.ietf.org/html/rfc6749#section-3.1
|
|
// Extension response types MAY contain a space-delimited (%x20) list of
|
|
// values, where the order of values does not matter (e.g., response
|
|
// type "a b" is the same as "b a"). The meaning of such composite
|
|
// response types is defined by their respective specifications.
|
|
// * https://tools.ietf.org/html/rfc6749#section-3.1.2
|
|
// The redirection endpoint URI MUST be an absolute URI as defined by
|
|
// [RFC3986] Section 4.3. The endpoint URI MAY include an
|
|
// "application/x-www-form-urlencoded" formatted (per Appendix B) query
|
|
// component ([RFC3986] Section 3.4), which MUST be retained when adding
|
|
// additional query parameters. The endpoint URI MUST NOT include a
|
|
// fragment component.
|
|
// * https://tools.ietf.org/html/rfc6749#section-3.1.2.2 (everything MUST be implemented)
|
|
NewAuthorizeRequest(ctx context.Context, req *http.Request) (AuthorizeRequester, error)
|
|
|
|
// NewAuthorizeResponse iterates through all response type handlers and returns their result or
|
|
// ErrUnsupportedResponseType if none of the handlers were able to handle it.
|
|
//
|
|
// The following specs must be considered in any implementation of this method:
|
|
// * https://tools.ietf.org/html/rfc6749#section-3.1.1
|
|
// Extension response types MAY contain a space-delimited (%x20) list of
|
|
// values, where the order of values does not matter (e.g., response
|
|
// type "a b" is the same as "b a"). The meaning of such composite
|
|
// response types is defined by their respective specifications.
|
|
// If an authorization request is missing the "response_type" parameter,
|
|
// or if the response type is not understood, the authorization server
|
|
// MUST return an error response as described in Section 4.1.2.1.
|
|
NewAuthorizeResponse(ctx context.Context, requester AuthorizeRequester, session Session) (AuthorizeResponder, error)
|
|
|
|
// WriteAuthorizeError returns the error codes to the redirection endpoint or shows the error to the user, if no valid
|
|
// redirect uri was given. Implements rfc6749#section-4.1.2.1
|
|
//
|
|
// The following specs must be considered in any implementation of this method:
|
|
// * https://tools.ietf.org/html/rfc6749#section-3.1.2
|
|
// The redirection endpoint URI MUST be an absolute URI as defined by
|
|
// [RFC3986] Section 4.3. The endpoint URI MAY include an
|
|
// "application/x-www-form-urlencoded" formatted (per Appendix B) query
|
|
// component ([RFC3986] Section 3.4), which MUST be retained when adding
|
|
// additional query parameters. The endpoint URI MUST NOT include a
|
|
// fragment component.
|
|
// * https://tools.ietf.org/html/rfc6749#section-4.1.2.1 (everything)
|
|
// * https://tools.ietf.org/html/rfc6749#section-3.1.2.2 (everything MUST be implemented)
|
|
WriteAuthorizeError(ctx context.Context, rw http.ResponseWriter, requester AuthorizeRequester, err error)
|
|
|
|
// WriteAuthorizeResponse persists the AuthorizeSession in the store and redirects the user agent to the provided
|
|
// redirect url or returns an error if storage failed.
|
|
//
|
|
// The following specs must be considered in any implementation of this method:
|
|
// * https://tools.ietf.org/html/rfc6749#rfc6749#section-4.1.2.1
|
|
// After completing its interaction with the resource owner, the
|
|
// authorization server directs the resource owner's user-agent back to
|
|
// the client. The authorization server redirects the user-agent to the
|
|
// client's redirection endpoint previously established with the
|
|
// authorization server during the client registration process or when
|
|
// making the authorization request.
|
|
// * https://tools.ietf.org/html/rfc6749#section-3.1.2.2 (everything MUST be implemented)
|
|
WriteAuthorizeResponse(ctx context.Context, rw http.ResponseWriter, requester AuthorizeRequester, responder AuthorizeResponder)
|
|
|
|
// NewAccessRequest creates a new access request object and validates
|
|
// various parameters.
|
|
//
|
|
// The following specs must be considered in any implementation of this method:
|
|
// * https://tools.ietf.org/html/rfc6749#section-3.2 (everything)
|
|
// * https://tools.ietf.org/html/rfc6749#section-3.2.1 (everything)
|
|
//
|
|
// Furthermore the registered handlers should implement their specs accordingly.
|
|
NewAccessRequest(ctx context.Context, req *http.Request, session Session) (AccessRequester, error)
|
|
|
|
// NewAccessResponse creates a new access response and validates that access_token and token_type are set.
|
|
//
|
|
// The following specs must be considered in any implementation of this method:
|
|
// https://tools.ietf.org/html/rfc6749#section-5.1
|
|
NewAccessResponse(ctx context.Context, requester AccessRequester) (AccessResponder, error)
|
|
|
|
// WriteAccessError writes an access request error response.
|
|
//
|
|
// The following specs must be considered in any implementation of this method:
|
|
// * https://tools.ietf.org/html/rfc6749#section-5.2 (everything)
|
|
WriteAccessError(ctx context.Context, rw http.ResponseWriter, requester Requester, err error)
|
|
|
|
// WriteAccessResponse writes the access response.
|
|
//
|
|
// The following specs must be considered in any implementation of this method:
|
|
// https://tools.ietf.org/html/rfc6749#section-5.1
|
|
WriteAccessResponse(ctx context.Context, rw http.ResponseWriter, requester AccessRequester, responder AccessResponder)
|
|
|
|
// NewDeviceRequest validate the OAuth 2.0 Device Authorization Flow Request
|
|
//
|
|
// The following specs must be considered in any implementation of this method:
|
|
// * https://www.rfc-editor.org/rfc/rfc8628#section-3.1 (everything MUST be implemented)
|
|
// Parameters sent without a value MUST be treated as if they were
|
|
// omitted from the request. The authorization server MUST ignore
|
|
// unrecognized request parameters. Request and response parameters
|
|
// MUST NOT be included more than once.
|
|
NewDeviceRequest(ctx context.Context, req *http.Request) (DeviceRequester, error)
|
|
|
|
// NewDeviceResponse persists the DeviceCodeSession and UserCodeSession in the store
|
|
//
|
|
// The following specs must be considered in any implementation of this method:
|
|
// * https://www.rfc-editor.org/rfc/rfc8628#section-3.2 (everything MUST be implemented)
|
|
// In response, the authorization server generates a unique device
|
|
// verification code and an end-user code that are valid for a limited
|
|
// time
|
|
NewDeviceResponse(ctx context.Context, requester DeviceRequester, session Session) (DeviceResponder, error)
|
|
|
|
// WriteDeviceResponse return to the user both codes and
|
|
// some configuration information in a JSON formatted manner
|
|
//
|
|
// The following specs must be considered in any implementation of this method:
|
|
// * https://www.rfc-editor.org/rfc/rfc8628#section-3.2 (everything MUST be implemented)
|
|
// Response is an HTTP response body using the
|
|
// "application/json" format [RFC8259] with a 200 (OK) status code.
|
|
WriteDeviceResponse(ctx context.Context, rw http.ResponseWriter, requester DeviceRequester, responder DeviceResponder)
|
|
|
|
// NewRevocationRequest handles incoming token revocation requests and validates various parameters.
|
|
//
|
|
// The following specs must be considered in any implementation of this method:
|
|
// https://tools.ietf.org/html/rfc7009#section-2.1
|
|
NewRevocationRequest(ctx context.Context, r *http.Request) error
|
|
|
|
// WriteRevocationResponse writes the revoke response.
|
|
//
|
|
// The following specs must be considered in any implementation of this method:
|
|
// https://tools.ietf.org/html/rfc7009#section-2.2
|
|
WriteRevocationResponse(ctx context.Context, rw http.ResponseWriter, err error)
|
|
|
|
// IntrospectToken returns token metadata, if the token is valid. Tokens generated by the authorization endpoint,
|
|
// such as the authorization code, can not be introspected.
|
|
IntrospectToken(ctx context.Context, token string, tokenUse TokenUse, session Session, scope ...string) (TokenUse, AccessRequester, error)
|
|
|
|
// NewIntrospectionRequest initiates token introspection as defined in
|
|
// https://tools.ietf.org/html/rfc7662#section-2.1
|
|
NewIntrospectionRequest(ctx context.Context, r *http.Request, session Session) (IntrospectionResponder, error)
|
|
|
|
// WriteIntrospectionError responds with an error if token introspection failed as defined in
|
|
// https://tools.ietf.org/html/rfc7662#section-2.3
|
|
WriteIntrospectionError(ctx context.Context, rw http.ResponseWriter, err error)
|
|
|
|
// WriteIntrospectionResponse responds with token metadata discovered by token introspection as defined in
|
|
// https://tools.ietf.org/html/rfc7662#section-2.2
|
|
WriteIntrospectionResponse(ctx context.Context, rw http.ResponseWriter, r IntrospectionResponder)
|
|
|
|
// NewPushedAuthorizeRequest validates the request and produces an AuthorizeRequester object that can be stored
|
|
NewPushedAuthorizeRequest(ctx context.Context, r *http.Request) (AuthorizeRequester, error)
|
|
|
|
// NewPushedAuthorizeResponse executes the handlers and builds the response
|
|
NewPushedAuthorizeResponse(ctx context.Context, ar AuthorizeRequester, session Session) (PushedAuthorizeResponder, error)
|
|
|
|
// WritePushedAuthorizeResponse writes the PAR response
|
|
WritePushedAuthorizeResponse(ctx context.Context, rw http.ResponseWriter, ar AuthorizeRequester, resp PushedAuthorizeResponder)
|
|
|
|
// WritePushedAuthorizeError writes the PAR error
|
|
WritePushedAuthorizeError(ctx context.Context, rw http.ResponseWriter, ar AuthorizeRequester, err error)
|
|
}
|
|
|
|
// IntrospectionResponder is the response object that will be returned when token introspection was successful,
|
|
// for example when the client is allowed to perform token introspection. Refer to
|
|
// https://tools.ietf.org/html/rfc7662#section-2.2 for more details.
|
|
type IntrospectionResponder interface {
|
|
// IsActive returns true if the introspected token is active and false otherwise.
|
|
IsActive() bool
|
|
|
|
// GetAccessRequester returns nil when IsActive() is false and the original access request object otherwise.
|
|
GetAccessRequester() AccessRequester
|
|
|
|
// GetTokenUse optionally returns the type of the token that was introspected. This could be "access_token", "refresh_token",
|
|
// or if the type can not be determined an empty string.
|
|
GetTokenUse() TokenUse
|
|
|
|
// GetAccessTokenType optionally returns the type of the access token that was introspected. This could be "bearer", "mac",
|
|
// or empty string if the type of the token is refresh token.
|
|
GetAccessTokenType() string
|
|
}
|
|
|
|
// Requester is an abstract interface for handling requests in Fosite.
|
|
type Requester interface {
|
|
// SetID sets the unique identifier.
|
|
SetID(id string)
|
|
|
|
// GetID returns a unique identifier.
|
|
GetID() string
|
|
|
|
// GetRequestedAt returns the time the request was created.
|
|
GetRequestedAt() (requestedAt time.Time)
|
|
|
|
// GetClient returns the request's client.
|
|
GetClient() (client Client)
|
|
|
|
// GetRequestedScopes returns the request's scopes.
|
|
GetRequestedScopes() (scopes Arguments)
|
|
|
|
// GetRequestedAudience returns the requested audiences for this request.
|
|
GetRequestedAudience() (audience Arguments)
|
|
|
|
// SetRequestedScopes sets the request's scopes.
|
|
SetRequestedScopes(scopes Arguments)
|
|
|
|
// SetRequestedAudience sets the requested audience.
|
|
SetRequestedAudience(audience Arguments)
|
|
|
|
// AppendRequestedScope appends a scope to the request.
|
|
AppendRequestedScope(scope string)
|
|
|
|
// GetGrantedScopes returns all granted scopes.
|
|
GetGrantedScopes() (grantedScopes Arguments)
|
|
|
|
// GetGrantedAudience returns all granted audiences.
|
|
GetGrantedAudience() (grantedAudience Arguments)
|
|
|
|
// GrantScope marks a request's scope as granted.
|
|
GrantScope(scope string)
|
|
|
|
// GrantAudience marks a request's audience as granted.
|
|
GrantAudience(audience string)
|
|
|
|
// GetSession returns a pointer to the request's session or nil if none is set.
|
|
GetSession() (session Session)
|
|
|
|
// SetSession sets the request's session pointer.
|
|
SetSession(session Session)
|
|
|
|
// GetRequestForm returns the request's form input.
|
|
GetRequestForm() url.Values
|
|
|
|
// Merge merges the argument into the method receiver.
|
|
Merge(requester Requester)
|
|
|
|
// Sanitize returns a sanitized clone of the request which can be used for storage.
|
|
Sanitize(allowedParameters []string) Requester
|
|
}
|
|
|
|
// AccessRequester is a token endpoint's request context.
|
|
type AccessRequester interface {
|
|
// GetGrantTypes returns the requests grant type.
|
|
GetGrantTypes() (grantTypes Arguments)
|
|
|
|
Requester
|
|
}
|
|
|
|
// DeviceRequester is an device endpoint's request context.
|
|
type DeviceRequester interface {
|
|
// GetUserCodeState returns the state of the user code
|
|
GetUserCodeState() UserCodeState
|
|
|
|
// SetUserCodeState sets the state of the user code
|
|
SetUserCodeState(state UserCodeState)
|
|
|
|
Requester
|
|
}
|
|
|
|
// AuthorizeRequester is an authorize endpoint's request context.
|
|
type AuthorizeRequester interface {
|
|
// GetResponseTypes returns the requested response types
|
|
GetResponseTypes() (responseTypes Arguments)
|
|
|
|
// SetResponseTypeHandled marks a response_type (e.g. token or code) as handled indicating that the response type
|
|
// is supported.
|
|
SetResponseTypeHandled(responseType string)
|
|
|
|
// DidHandleAllResponseTypes returns if all requested response types have been handled correctly
|
|
DidHandleAllResponseTypes() (didHandle bool)
|
|
|
|
// GetRedirectURI returns the requested redirect URI
|
|
GetRedirectURI() (redirectURL *url.URL)
|
|
|
|
// IsRedirectURIValid returns false if the redirect is not rfc-conform (i.e. missing client, not on white list,
|
|
// or malformed)
|
|
IsRedirectURIValid() (isValid bool)
|
|
|
|
// GetState returns the request's state.
|
|
GetState() (state string)
|
|
|
|
// GetResponseMode returns response_mode of the authorization request
|
|
GetResponseMode() ResponseModeType
|
|
|
|
// SetDefaultResponseMode sets default response mode for a response type in a flow
|
|
SetDefaultResponseMode(responseMode ResponseModeType)
|
|
|
|
// GetDefaultResponseMode gets default response mode for a response type in a flow
|
|
GetDefaultResponseMode() ResponseModeType
|
|
|
|
Requester
|
|
}
|
|
|
|
// AccessResponder is a token endpoint's response.
|
|
type AccessResponder interface {
|
|
// SetExtra sets a key value pair for the access response.
|
|
SetExtra(key string, value interface{})
|
|
|
|
// GetExtra returns a key's value.
|
|
GetExtra(key string) interface{}
|
|
|
|
SetExpiresIn(time.Duration)
|
|
|
|
SetScopes(scopes Arguments)
|
|
|
|
// SetAccessToken sets the responses mandatory access token.
|
|
SetAccessToken(token string)
|
|
|
|
// SetTokenType set's the responses mandatory token type
|
|
SetTokenType(tokenType string)
|
|
|
|
// GetAccessToken returns the responses access token.
|
|
GetAccessToken() (token string)
|
|
|
|
// GetTokenType returns the responses token type.
|
|
GetTokenType() (token string)
|
|
|
|
// ToMap converts the response to a map.
|
|
ToMap() map[string]interface{}
|
|
}
|
|
|
|
// AuthorizeResponder is an authorization endpoint's response.
|
|
type AuthorizeResponder interface {
|
|
// GetCode returns the response's authorize code if set.
|
|
GetCode() string
|
|
|
|
// GetHeader returns the response's header
|
|
GetHeader() (header http.Header)
|
|
|
|
// AddHeader adds a header key value pair to the response
|
|
AddHeader(key, value string)
|
|
|
|
// GetParameters returns the response's parameters
|
|
GetParameters() (query url.Values)
|
|
|
|
// AddParameter adds key value pair to the response
|
|
AddParameter(key, value string)
|
|
}
|
|
|
|
// PushedAuthorizeResponder is the response object for PAR
|
|
type PushedAuthorizeResponder interface {
|
|
// GetRequestURI returns the request_uri
|
|
GetRequestURI() string
|
|
// SetRequestURI sets the request_uri
|
|
SetRequestURI(requestURI string)
|
|
// GetExpiresIn gets the expires_in
|
|
GetExpiresIn() int
|
|
// SetExpiresIn sets the expires_in
|
|
SetExpiresIn(seconds int)
|
|
|
|
// GetHeader returns the response's header
|
|
GetHeader() (header http.Header)
|
|
|
|
// AddHeader adds a header key value pair to the response
|
|
AddHeader(key, value string)
|
|
|
|
// SetExtra sets a key value pair for the response.
|
|
SetExtra(key string, value interface{})
|
|
|
|
// GetExtra returns a key's value.
|
|
GetExtra(key string) interface{}
|
|
|
|
// ToMap converts the response to a map.
|
|
ToMap() map[string]interface{}
|
|
}
|
|
|
|
// G11NContext is the globalization context
|
|
type G11NContext interface {
|
|
// GetLang returns the current language in the context
|
|
GetLang() language.Tag
|
|
}
|
|
|
|
// DeviceResponder is the device authorization endpoint's response
|
|
type DeviceResponder interface {
|
|
// GetDeviceCode returns the device_code
|
|
GetDeviceCode() string
|
|
// SetDeviceCode sets the device_code
|
|
SetDeviceCode(code string)
|
|
|
|
// GetUserCode returns the user_code
|
|
GetUserCode() string
|
|
// SetUserCode sets the user_code
|
|
SetUserCode(code string)
|
|
|
|
// GetVerificationURI returns the verification_uri
|
|
GetVerificationURI() string
|
|
// SetVerificationURI sets the verification_uri
|
|
SetVerificationURI(uri string)
|
|
|
|
// GetVerificationURIComplete returns the verification_uri_complete
|
|
GetVerificationURIComplete() string
|
|
// SetVerificationURIComplete sets the verification_uri_complete
|
|
SetVerificationURIComplete(uri string)
|
|
|
|
// GetExpiresIn returns the expires_in
|
|
GetExpiresIn() int64
|
|
// SetExpiresIn sets the expires_in
|
|
SetExpiresIn(seconds int64)
|
|
|
|
// GetInterval returns the interval
|
|
GetInterval() int
|
|
// SetInterval sets the interval
|
|
SetInterval(seconds int)
|
|
|
|
// GetHeader returns the response's header
|
|
GetHeader() (header http.Header)
|
|
// AddHeader adds a header key value pair to the response
|
|
AddHeader(key, value string)
|
|
}
|