diff options
Diffstat (limited to 'internal')
79 files changed, 1087 insertions, 10473 deletions
diff --git a/internal/configuration/schema/identity_providers.go b/internal/configuration/schema/identity_providers.go index ac75460cd..be4186cce 100644 --- a/internal/configuration/schema/identity_providers.go +++ b/internal/configuration/schema/identity_providers.go @@ -27,7 +27,8 @@ type IdentityProvidersOpenIDConnect struct {  	DiscoverySignedResponseAlg   string `koanf:"discovery_signed_response_alg" json:"discovery_signed_response_alg" jsonschema:"default=none,enum=none,enum=RS256,enum=RS384,enum=RS512,enum=ES256,enum=ES384,enum=ES512,enum=PS256,enum=PS384,enum=PS512,title=Discovery Response Signing Algorithm" jsonschema_description:"The Algorithm this provider uses to sign the Discovery and Metadata Document responses."`  	DiscoverySignedResponseKeyID string `koanf:"discovery_signed_response_key_id" json:"discovery_signed_response_key_id" jsonschema:"title=Discovery Response Signing Key ID" jsonschema_description:"The Key ID this provider uses to sign the Discovery and Metadata Document responses (overrides the 'discovery_signed_response_alg')."` -	PAR  IdentityProvidersOpenIDConnectPAR  `koanf:"pushed_authorizations" json:"pushed_authorizations" jsonschema:"title=Pushed Authorizations" jsonschema_description:"Configuration options for Pushed Authorization Requests."` +	RequirePushedAuthorizationRequests bool `koanf:"require_pushed_authorization_requests" json:"require_pushed_authorization_requests" jsonschema:"title=Require Pushed Authorization Requests" jsonschema_description:"Requires Pushed Authorization Requests for all clients for this Issuer."` +  	CORS IdentityProvidersOpenIDConnectCORS `koanf:"cors" json:"cors" jsonschema:"title=CORS" jsonschema_description:"Configuration options for Cross-Origin Request Sharing."`  	Clients []IdentityProvidersOpenIDConnectClient `koanf:"clients" json:"clients" jsonschema:"title=Clients" jsonschema_description:"OpenID Connect 1.0 clients registry."` @@ -98,12 +99,6 @@ type IdentityProvidersOpenIDConnectLifespanToken struct {  	RefreshToken  time.Duration `koanf:"refresh_token" json:"refresh_token" jsonschema:"default=90 minutes,title=Refresh Token Lifespan" jsonschema_description:"The duration a Refresh Token is valid for."`  } -// IdentityProvidersOpenIDConnectPAR represents an OpenID Connect 1.0 PAR config. -type IdentityProvidersOpenIDConnectPAR struct { -	Enforce         bool          `koanf:"enforce" json:"enforce" jsonschema:"default=false,title=Enforce" jsonschema_description:"Enforce the use of PAR for all requests on all clients."` -	ContextLifespan time.Duration `koanf:"context_lifespan" json:"context_lifespan" jsonschema:"default=5 minutes,title=Context Lifespan" jsonschema_description:"How long a PAR context is valid for."` -} -  // IdentityProvidersOpenIDConnectCORS represents an OpenID Connect 1.0 CORS config.  type IdentityProvidersOpenIDConnectCORS struct {  	Endpoints      []string   `koanf:"endpoints" json:"endpoints" jsonschema:"uniqueItems,enum=authorization,enum=pushed-authorization-request,enum=token,enum=introspection,enum=revocation,enum=userinfo,title=Endpoints" jsonschema_description:"List of endpoints to enable CORS handling for."` diff --git a/internal/configuration/schema/keys.go b/internal/configuration/schema/keys.go index f0642dc43..30dae5f84 100644 --- a/internal/configuration/schema/keys.go +++ b/internal/configuration/schema/keys.go @@ -29,8 +29,7 @@ var Keys = []string{  	"identity_providers.oidc.enable_jwt_access_token_stateless_introspection",  	"identity_providers.oidc.discovery_signed_response_alg",  	"identity_providers.oidc.discovery_signed_response_key_id", -	"identity_providers.oidc.pushed_authorizations.enforce", -	"identity_providers.oidc.pushed_authorizations.context_lifespan", +	"identity_providers.oidc.require_pushed_authorization_requests",  	"identity_providers.oidc.cors.endpoints",  	"identity_providers.oidc.cors.allowed_origins",  	"identity_providers.oidc.cors.allowed_origins_from_client_redirect_uris", diff --git a/internal/configuration/schema/types.go b/internal/configuration/schema/types.go index af69cac0f..e97739b9e 100644 --- a/internal/configuration/schema/types.go +++ b/internal/configuration/schema/types.go @@ -9,6 +9,7 @@ import (  	"crypto/tls"  	"crypto/x509"  	"encoding/pem" +	"errors"  	"fmt"  	"regexp"  	"strings" @@ -93,6 +94,21 @@ func (d *PasswordDigest) IsPlainText() (is bool) {  	}  } +// GetPlainTextValue returns a *plaintext.Digest's byte value from Key() and an error. If the PasswordDigest is not a +// plaintext.Digest then it returns nil and an error, otherwise it returns the value and nil. +func (d *PasswordDigest) GetPlainTextValue() (value []byte, err error) { +	if d == nil || d.Digest == nil { +		return nil, errors.New("error: nil value") +	} + +	switch digest := d.Digest.(type) { +	case *plaintext.Digest: +		return digest.Key(), nil +	default: +		return nil, errors.New("error: digest isn't plaintext") +	} +} +  func (d *PasswordDigest) UnmarshalYAML(value *yaml.Node) (err error) {  	digestRaw := "" diff --git a/internal/configuration/validator/identity_providers.go b/internal/configuration/validator/identity_providers.go index 931960bfc..40b56cc4e 100644 --- a/internal/configuration/validator/identity_providers.go +++ b/internal/configuration/validator/identity_providers.go @@ -8,7 +8,7 @@ import (  	"sort"  	"strconv" -	"github.com/ory/fosite" +	oauthelia2 "authelia.com/provider/oauth2"  	"github.com/authelia/authelia/v4/internal/configuration/schema"  	"github.com/authelia/authelia/v4/internal/oidc" @@ -37,9 +37,9 @@ func validateOIDC(config *schema.IdentityProvidersOpenIDConnect, validator *sche  	case config.MinimumParameterEntropy == -1:  		validator.PushWarning(fmt.Errorf(errFmtOIDCProviderInsecureDisabledParameterEntropy))  	case config.MinimumParameterEntropy <= 0: -		config.MinimumParameterEntropy = fosite.MinParameterEntropy -	case config.MinimumParameterEntropy < fosite.MinParameterEntropy: -		validator.PushWarning(fmt.Errorf(errFmtOIDCProviderInsecureParameterEntropyUnsafe, fosite.MinParameterEntropy, config.MinimumParameterEntropy)) +		config.MinimumParameterEntropy = oauthelia2.MinParameterEntropy +	case config.MinimumParameterEntropy < oauthelia2.MinParameterEntropy: +		validator.PushWarning(fmt.Errorf(errFmtOIDCProviderInsecureParameterEntropyUnsafe, oauthelia2.MinParameterEntropy, config.MinimumParameterEntropy))  	}  	switch config.EnforcePKCE { diff --git a/internal/configuration/validator/util.go b/internal/configuration/validator/util.go index 3945c4298..f9f4a4be3 100644 --- a/internal/configuration/validator/util.go +++ b/internal/configuration/validator/util.go @@ -9,7 +9,7 @@ import (  	"fmt"  	"strings" -	"github.com/go-jose/go-jose/v3" +	"github.com/go-jose/go-jose/v4"  	"golang.org/x/net/publicsuffix"  	"github.com/authelia/authelia/v4/internal/configuration/schema" diff --git a/internal/handlers/handler_authz_authn.go b/internal/handlers/handler_authz_authn.go index 5a8b80224..86f6ddda4 100644 --- a/internal/handlers/handler_authz_authn.go +++ b/internal/handlers/handler_authz_authn.go @@ -10,7 +10,7 @@ import (  	"strings"  	"time" -	"github.com/ory/fosite" +	oauthelia2 "authelia.com/provider/oauth2"  	"github.com/sirupsen/logrus"  	"github.com/valyala/fasthttp" @@ -467,27 +467,27 @@ func handleVerifyGETAuthorizationBearer(ctx *middlewares.AutheliaCtx, authn *Aut  type AuthzBearerIntrospectionProvider interface {  	GetFullClient(ctx context.Context, id string) (client oidc.Client, err error) -	GetAudienceStrategy(ctx context.Context) (strategy fosite.AudienceMatchingStrategy) -	IntrospectToken(ctx context.Context, token string, tokenUse fosite.TokenUse, session fosite.Session, scope ...string) (fosite.TokenUse, fosite.AccessRequester, error) +	GetAudienceStrategy(ctx context.Context) (strategy oauthelia2.AudienceMatchingStrategy) +	IntrospectToken(ctx context.Context, token string, tokenUse oauthelia2.TokenUse, session oauthelia2.Session, scope ...string) (oauthelia2.TokenUse, oauthelia2.AccessRequester, error)  }  func handleVerifyGETAuthorizationBearerIntrospection(ctx context.Context, provider AuthzBearerIntrospectionProvider, authn *Authn, object *authorization.Object) (username, clientID string, ccs bool, level authentication.Level, err error) {  	var ( -		use       fosite.TokenUse -		requester fosite.AccessRequester +		use       oauthelia2.TokenUse +		requester oauthelia2.AccessRequester  	) -	authn.Header.Error = &fosite.RFC6749Error{ +	authn.Header.Error = &oauthelia2.RFC6749Error{  		ErrorField:       "invalid_token",  		DescriptionField: "The access token is expired, revoked, malformed, or invalid for other reasons. The client can obtain a new access token and try again.",  	} -	if use, requester, err = provider.IntrospectToken(ctx, authn.Header.Authorization.Value(), fosite.AccessToken, oidc.NewSession(), oidc.ScopeAutheliaBearerAuthz); err != nil { +	if use, requester, err = provider.IntrospectToken(ctx, authn.Header.Authorization.Value(), oauthelia2.AccessToken, oidc.NewSession(), oidc.ScopeAutheliaBearerAuthz); err != nil {  		return "", "", false, authentication.NotAuthenticated, fmt.Errorf("error performing token introspection: %w", err)  	} -	if use != fosite.AccessToken { -		authn.Header.Error = fosite.ErrInvalidRequest +	if use != oauthelia2.AccessToken { +		authn.Header.Error = oauthelia2.ErrInvalidRequest  		return "", "", false, authentication.NotAuthenticated, fmt.Errorf("token is not an access token")  	} diff --git a/internal/handlers/handler_authz_types.go b/internal/handlers/handler_authz_types.go index ec6192a87..af3475e74 100644 --- a/internal/handlers/handler_authz_types.go +++ b/internal/handlers/handler_authz_types.go @@ -3,7 +3,7 @@ package handlers  import (  	"net/url" -	"github.com/ory/fosite" +	oauthelia2 "authelia.com/provider/oauth2"  	"github.com/authelia/authelia/v4/internal/authentication"  	"github.com/authelia/authelia/v4/internal/authorization" @@ -83,7 +83,7 @@ type HeaderAuthorization struct {  	Authorization *model.Authorization  	Realm         string  	Scope         string -	Error         *fosite.RFC6749Error +	Error         *oauthelia2.RFC6749Error  }  // AuthzConfig represents the configuration elements of the Authz type. diff --git a/internal/handlers/handler_configuration_password_policy_test.go b/internal/handlers/handler_configuration_password_policy_test.go index a9a5b2fb7..9b64329c0 100644 --- a/internal/handlers/handler_configuration_password_policy_test.go +++ b/internal/handlers/handler_configuration_password_policy_test.go @@ -1,7 +1,6 @@  package handlers  import ( -	// "strings".  	"encoding/json"  	"testing" diff --git a/internal/handlers/handler_oauth_introspection.go b/internal/handlers/handler_oauth_introspection.go index 67fa1fb83..8a623f4c6 100644 --- a/internal/handlers/handler_oauth_introspection.go +++ b/internal/handlers/handler_oauth_introspection.go @@ -6,9 +6,9 @@ import (  	"net/url"  	"time" +	oauthelia2 "authelia.com/provider/oauth2" +	"authelia.com/provider/oauth2/token/jwt"  	"github.com/google/uuid" -	"github.com/ory/fosite" -	"github.com/ory/fosite/token/jwt"  	"github.com/pkg/errors"  	"github.com/valyala/fasthttp" @@ -22,12 +22,12 @@ import (  func OAuthIntrospectionPOST(ctx *middlewares.AutheliaCtx, rw http.ResponseWriter, req *http.Request) {  	var (  		requestID uuid.UUID -		responder fosite.IntrospectionResponder +		responder oauthelia2.IntrospectionResponder  		err       error  	)  	if requestID, err = uuid.NewRandom(); err != nil { -		ctx.Providers.OpenIDConnect.WriteIntrospectionError(ctx, rw, fosite.ErrServerError) +		ctx.Providers.OpenIDConnect.WriteIntrospectionError(ctx, rw, oauthelia2.ErrServerError)  		return  	} @@ -46,7 +46,7 @@ func OAuthIntrospectionPOST(ctx *middlewares.AutheliaCtx, rw http.ResponseWriter  	ctx.Logger.Tracef("Introspection Request with id '%s' yielded a %s (active: %t) requested at %s created with request id '%s' on client with id '%s'", requestID, responder.GetTokenUse(), responder.IsActive(), responder.GetAccessRequester().GetRequestedAt().String(), responder.GetAccessRequester().GetID(), responder.GetAccessRequester().GetClient().GetID()) -	aud, introspection := oidc.IntrospectionResponseToMap(responder) +	aud, introspection := responder.ToMap()  	var (  		client oidc.Client @@ -54,9 +54,9 @@ func OAuthIntrospectionPOST(ctx *middlewares.AutheliaCtx, rw http.ResponseWriter  	)  	if client, ok = responder.GetAccessRequester().GetClient().(oidc.Client); !ok { -		ctx.Logger.Errorf("Introspection Request with id '%s' failed with error: %s", requestID, oidc.ErrorToDebugRFC6749Error(fosite.ErrInvalidClient.WithDebugf("The client does not implement the correct type as it's a '%T'", responder.GetAccessRequester().GetClient()))) +		ctx.Logger.Errorf("Introspection Request with id '%s' failed with error: %s", requestID, oidc.ErrorToDebugRFC6749Error(oauthelia2.ErrInvalidClient.WithDebugf("The client does not implement the correct type as it's a '%T'", responder.GetAccessRequester().GetClient()))) -		ctx.Providers.OpenIDConnect.WriteIntrospectionError(ctx, rw, fosite.ErrInvalidClient) +		ctx.Providers.OpenIDConnect.WriteIntrospectionError(ctx, rw, oauthelia2.ErrInvalidClient)  		return  	} @@ -80,7 +80,7 @@ func OAuthIntrospectionPOST(ctx *middlewares.AutheliaCtx, rw http.ResponseWriter  		if issuer, err = ctx.IssuerURL(); err != nil {  			ctx.Logger.WithError(err).Errorf("Error occurred determining issuer") -			ctx.Providers.OpenIDConnect.WriteIntrospectionError(ctx, rw, errors.WithStack(fosite.ErrServerError.WithHint("Failed to lookup required information to perform this request.").WithDebugf("The issuer could not be determined with error %+v.", err))) +			ctx.Providers.OpenIDConnect.WriteIntrospectionError(ctx, rw, errors.WithStack(oauthelia2.ErrServerError.WithHint("Failed to lookup required information to perform this request.").WithDebugf("The issuer could not be determined with error %+v.", err)))  			return  		} @@ -88,7 +88,7 @@ func OAuthIntrospectionPOST(ctx *middlewares.AutheliaCtx, rw http.ResponseWriter  		if jwk = ctx.Providers.OpenIDConnect.KeyManager.Get(ctx, client.GetIntrospectionSignedResponseKeyID(), alg); jwk == nil {  			ctx.Logger.WithError(err).Errorf("Introspection Request with id '%s' failed to lookup key for key manager due to likely no support for the key algorithm", requestID) -			ctx.Providers.OpenIDConnect.WriteIntrospectionError(ctx, rw, errors.WithStack(fosite.ErrServerError.WithHint("Failed to lookup required information to perform this request.").WithDebugf("The JWK matching algorithm '%s' and key id '%s' could not be found.", alg, client.GetIntrospectionSignedResponseKeyID()))) +			ctx.Providers.OpenIDConnect.WriteIntrospectionError(ctx, rw, errors.WithStack(oauthelia2.ErrServerError.WithHint("Failed to lookup required information to perform this request.").WithDebugf("The JWK matching algorithm '%s' and key id '%s' could not be found.", alg, client.GetIntrospectionSignedResponseKeyID())))  			return  		} @@ -96,7 +96,7 @@ func OAuthIntrospectionPOST(ctx *middlewares.AutheliaCtx, rw http.ResponseWriter  		if jti, err = uuid.NewRandom(); err != nil {  			ctx.Logger.WithError(err).Errorf("Introspection Request with id '%s' failed to generate a JTI", requestID) -			ctx.Providers.OpenIDConnect.WriteIntrospectionError(ctx, rw, errors.WithStack(fosite.ErrServerError.WithHint("Failed to lookup required information to perform this request.").WithDebugf("The JTI could not be generated for the Introspection JWT response type with error %+v.", err))) +			ctx.Providers.OpenIDConnect.WriteIntrospectionError(ctx, rw, errors.WithStack(oauthelia2.ErrServerError.WithHint("Failed to lookup required information to perform this request.").WithDebugf("The JTI could not be generated for the Introspection JWT response type with error %+v.", err)))  			return  		} @@ -122,7 +122,7 @@ func OAuthIntrospectionPOST(ctx *middlewares.AutheliaCtx, rw http.ResponseWriter  		if token, _, err = jwk.Strategy().Generate(ctx, claims, headers); err != nil {  			ctx.Logger.WithError(err).Errorf("Introspection Request with id '%s' failed to generate the Introspection JWT response", requestID) -			ctx.Providers.OpenIDConnect.WriteIntrospectionError(ctx, rw, errors.WithStack(fosite.ErrServerError.WithHint("Failed to generate the response.").WithDebugf("The Introspection JWT itself could not be generated with error %+v.", err))) +			ctx.Providers.OpenIDConnect.WriteIntrospectionError(ctx, rw, errors.WithStack(oauthelia2.ErrServerError.WithHint("Failed to generate the response.").WithDebugf("The Introspection JWT itself could not be generated with error %+v.", err)))  			return  		} diff --git a/internal/handlers/handler_oauth_revocation.go b/internal/handlers/handler_oauth_revocation.go index 7e135c71b..8c197788f 100644 --- a/internal/handlers/handler_oauth_revocation.go +++ b/internal/handlers/handler_oauth_revocation.go @@ -3,8 +3,8 @@ package handlers  import (  	"net/http" +	oauthelia2 "authelia.com/provider/oauth2"  	"github.com/google/uuid" -	"github.com/ory/fosite"  	"github.com/authelia/authelia/v4/internal/middlewares"  	"github.com/authelia/authelia/v4/internal/oidc" @@ -20,7 +20,7 @@ func OAuthRevocationPOST(ctx *middlewares.AutheliaCtx, rw http.ResponseWriter, r  	)  	if requestID, err = uuid.NewRandom(); err != nil { -		ctx.Providers.OpenIDConnect.WriteRevocationResponse(ctx, rw, fosite.ErrServerError) +		ctx.Providers.OpenIDConnect.WriteRevocationResponse(ctx, rw, oauthelia2.ErrServerError)  		return  	} diff --git a/internal/handlers/handler_oidc_authorization.go b/internal/handlers/handler_oidc_authorization.go index 778812b5e..faba2a288 100644 --- a/internal/handlers/handler_oidc_authorization.go +++ b/internal/handlers/handler_oidc_authorization.go @@ -6,7 +6,7 @@ import (  	"net/url"  	"time" -	"github.com/ory/fosite" +	oauthelia2 "authelia.com/provider/oauth2"  	"github.com/authelia/authelia/v4/internal/authorization"  	"github.com/authelia/authelia/v4/internal/middlewares" @@ -20,8 +20,8 @@ import (  // https://openid.net/specs/openid-connect-core-1_0.html#AuthorizationEndpoint  func OpenIDConnectAuthorization(ctx *middlewares.AutheliaCtx, rw http.ResponseWriter, r *http.Request) {  	var ( -		requester fosite.AuthorizeRequester -		responder fosite.AuthorizeResponder +		requester oauthelia2.AuthorizeRequester +		responder oauthelia2.AuthorizeResponder  		client    oidc.Client  		authTime  time.Time  		issuer    *url.URL @@ -41,7 +41,7 @@ func OpenIDConnectAuthorization(ctx *middlewares.AutheliaCtx, rw http.ResponseWr  	ctx.Logger.Debugf("Authorization Request with id '%s' on client with id '%s' is being processed", requester.GetID(), clientID)  	if client, err = ctx.Providers.OpenIDConnect.GetFullClient(ctx, clientID); err != nil { -		if errors.Is(err, fosite.ErrNotFound) { +		if errors.Is(err, oauthelia2.ErrNotFound) {  			ctx.Logger.Errorf("Authorization Request with id '%s' on client with id '%s' could not be processed: client was not found", requester.GetID(), clientID)  		} else {  			ctx.Logger.Errorf("Authorization Request with id '%s' on client with id '%s' could not be processed: failed to find client: %s", requester.GetID(), clientID, oidc.ErrorToDebugRFC6749Error(err)) @@ -52,23 +52,7 @@ func OpenIDConnectAuthorization(ctx *middlewares.AutheliaCtx, rw http.ResponseWr  		return  	} -	if err = client.ValidatePARPolicy(requester, ctx.Providers.OpenIDConnect.GetPushedAuthorizeRequestURIPrefix(ctx)); err != nil { -		ctx.Logger.Errorf("Authorization Request with id '%s' on client with id '%s' failed to validate the PAR policy: %s", requester.GetID(), clientID, oidc.ErrorToDebugRFC6749Error(err)) - -		ctx.Providers.OpenIDConnect.WriteAuthorizeError(ctx, rw, requester, err) - -		return -	} -  	if !oidc.IsPushedAuthorizedRequest(requester, ctx.Providers.OpenIDConnect.GetPushedAuthorizeRequestURIPrefix(ctx)) { -		if err = client.ValidatePKCEPolicy(requester); err != nil { -			ctx.Logger.Errorf("Authorization Request with id '%s' on client with id '%s' failed to validate the PKCE policy: %s", requester.GetID(), client.GetID(), oidc.ErrorToDebugRFC6749Error(err)) - -			ctx.Providers.OpenIDConnect.WriteAuthorizeError(ctx, rw, requester, err) - -			return -		} -  		if err = client.ValidateResponseModePolicy(requester); err != nil {  			ctx.Logger.Errorf("Authorization Request with id '%s' on client with id '%s' failed to validate the Response Mode: %s", requester.GetID(), client.GetID(), oidc.ErrorToDebugRFC6749Error(err)) @@ -78,16 +62,6 @@ func OpenIDConnectAuthorization(ctx *middlewares.AutheliaCtx, rw http.ResponseWr  		}  	} -	if err = client.ValidatePKCEPolicy(requester); err != nil { -		ctx.Logger.Errorf("Authorization Request with id '%s' on client with id '%s' failed to validate the PKCE policy: %s", requester.GetID(), clientID, oidc.ErrorToDebugRFC6749Error(err)) - -		ctx.Providers.OpenIDConnect.WriteAuthorizeError(ctx, rw, requester, err) - -		return -	} - -	client.ApplyRequestedAudiencePolicy(requester) -  	var (  		userSession session.UserSession  		consent     *model.OAuth2ConsentSession @@ -97,7 +71,7 @@ func OpenIDConnectAuthorization(ctx *middlewares.AutheliaCtx, rw http.ResponseWr  	if userSession, err = ctx.GetSession(); err != nil {  		ctx.Logger.Errorf("Authorization Request with id '%s' on client with id '%s' could not be processed: error occurred obtaining session information: %+v", requester.GetID(), client.GetID(), err) -		ctx.Providers.OpenIDConnect.WriteAuthorizeError(ctx, rw, requester, fosite.ErrServerError.WithHint("Could not obtain the user session.")) +		ctx.Providers.OpenIDConnect.WriteAuthorizeError(ctx, rw, requester, oauthelia2.ErrServerError.WithHint("Could not obtain the user session."))  		return  	} @@ -113,7 +87,7 @@ func OpenIDConnectAuthorization(ctx *middlewares.AutheliaCtx, rw http.ResponseWr  	if authTime, err = userSession.AuthenticatedTime(client.GetAuthorizationPolicyRequiredLevel(authorization.Subject{Username: userSession.Username, Groups: userSession.Groups, IP: ctx.RemoteIP()})); err != nil {  		ctx.Logger.Errorf("Authorization Request with id '%s' on client with id '%s' could not be processed: error occurred checking authentication time: %+v", requester.GetID(), client.GetID(), err) -		ctx.Providers.OpenIDConnect.WriteAuthorizeError(ctx, rw, requester, fosite.ErrServerError.WithHint("Could not obtain the authentication time.")) +		ctx.Providers.OpenIDConnect.WriteAuthorizeError(ctx, rw, requester, oauthelia2.ErrServerError.WithHint("Could not obtain the authentication time."))  		return  	} @@ -157,8 +131,8 @@ func OpenIDConnectAuthorization(ctx *middlewares.AutheliaCtx, rw http.ResponseWr  // RFC9126 https://www.rfc-editor.org/rfc/rfc9126.html  func OpenIDConnectPushedAuthorizationRequest(ctx *middlewares.AutheliaCtx, rw http.ResponseWriter, r *http.Request) {  	var ( -		requester fosite.AuthorizeRequester -		responder fosite.PushedAuthorizeResponder +		requester oauthelia2.AuthorizeRequester +		responder oauthelia2.PushedAuthorizeResponder  		err       error  	) @@ -175,7 +149,7 @@ func OpenIDConnectPushedAuthorizationRequest(ctx *middlewares.AutheliaCtx, rw ht  	clientID := requester.GetClient().GetID()  	if client, err = ctx.Providers.OpenIDConnect.GetFullClient(ctx, clientID); err != nil { -		if errors.Is(err, fosite.ErrNotFound) { +		if errors.Is(err, oauthelia2.ErrNotFound) {  			ctx.Logger.Errorf("Pushed Authorization Request with id '%s' on client with id '%s' could not be processed: client was not found", requester.GetID(), clientID)  		} else {  			ctx.Logger.Errorf("Pushed Authorization Request with id '%s' on client with id '%s' could not be processed: failed to find client: %+v", requester.GetID(), clientID, err) @@ -186,14 +160,6 @@ func OpenIDConnectPushedAuthorizationRequest(ctx *middlewares.AutheliaCtx, rw ht  		return  	} -	if err = client.ValidatePKCEPolicy(requester); err != nil { -		ctx.Logger.Errorf("Pushed Authorization Request with id '%s' on client with id '%s' failed to validate the PKCE policy: %s", requester.GetID(), client.GetID(), oidc.ErrorToDebugRFC6749Error(err)) - -		ctx.Providers.OpenIDConnect.WritePushedAuthorizeError(ctx, rw, requester, err) - -		return -	} -  	if err = client.ValidateResponseModePolicy(requester); err != nil {  		ctx.Logger.Errorf("Pushed Authorization Request with id '%s' on client with id '%s' failed to validate the Response Mode: %s", requester.GetID(), client.GetID(), oidc.ErrorToDebugRFC6749Error(err)) diff --git a/internal/handlers/handler_oidc_authorization_consent.go b/internal/handlers/handler_oidc_authorization_consent.go index f6ae3738c..8b269d01e 100644 --- a/internal/handlers/handler_oidc_authorization_consent.go +++ b/internal/handlers/handler_oidc_authorization_consent.go @@ -8,8 +8,8 @@ import (  	"path"  	"strings" +	oauthelia2 "authelia.com/provider/oauth2"  	"github.com/google/uuid" -	"github.com/ory/fosite"  	"github.com/authelia/authelia/v4/internal/authorization"  	"github.com/authelia/authelia/v4/internal/middlewares" @@ -20,7 +20,7 @@ import (  func handleOIDCAuthorizationConsent(ctx *middlewares.AutheliaCtx, issuer *url.URL, client oidc.Client,  	userSession session.UserSession, -	rw http.ResponseWriter, r *http.Request, requester fosite.AuthorizeRequester) (consent *model.OAuth2ConsentSession, handled bool) { +	rw http.ResponseWriter, r *http.Request, requester oauthelia2.AuthorizeRequester) (consent *model.OAuth2ConsentSession, handled bool) {  	var (  		subject uuid.UUID  		err     error @@ -35,8 +35,8 @@ func handleOIDCAuthorizationConsent(ctx *middlewares.AutheliaCtx, issuer *url.UR  	case userSession.IsAnonymous():  		handler = handleOIDCAuthorizationConsentNotAuthenticated  	case authorization.IsAuthLevelSufficient(userSession.AuthenticationLevel, level): -		if subject, err = ctx.Providers.OpenIDConnect.GetSubject(ctx, client.GetSectorIdentifier(), userSession.Username); err != nil { -			ctx.Logger.Errorf(logFmtErrConsentCantGetSubject, requester.GetID(), client.GetID(), client.GetConsentPolicy(), userSession.Username, client.GetSectorIdentifier(), err) +		if subject, err = ctx.Providers.OpenIDConnect.GetSubject(ctx, client.GetSectorIdentifierURI(), userSession.Username); err != nil { +			ctx.Logger.Errorf(logFmtErrConsentCantGetSubject, requester.GetID(), client.GetID(), client.GetConsentPolicy(), userSession.Username, client.GetSectorIdentifierURI(), err)  			ctx.Providers.OpenIDConnect.WriteAuthorizeError(ctx, rw, requester, oidc.ErrSubjectCouldNotLookup) @@ -53,7 +53,7 @@ func handleOIDCAuthorizationConsent(ctx *middlewares.AutheliaCtx, issuer *url.UR  		default:  			ctx.Logger.Errorf(logFmtErrConsentCantDetermineConsentMode, requester.GetID(), client.GetID()) -			ctx.Providers.OpenIDConnect.WriteAuthorizeError(ctx, rw, requester, fosite.ErrServerError.WithHint("Could not determine the client consent mode.")) +			ctx.Providers.OpenIDConnect.WriteAuthorizeError(ctx, rw, requester, oauthelia2.ErrServerError.WithHint("Could not determine the client consent mode."))  			return nil, true  		} @@ -66,8 +66,8 @@ func handleOIDCAuthorizationConsent(ctx *middlewares.AutheliaCtx, issuer *url.UR  			return nil, true  		} -		if subject, err = ctx.Providers.OpenIDConnect.GetSubject(ctx, client.GetSectorIdentifier(), userSession.Username); err != nil { -			ctx.Logger.Errorf(logFmtErrConsentCantGetSubject, requester.GetID(), client.GetID(), client.GetConsentPolicy(), userSession.Username, client.GetSectorIdentifier(), err) +		if subject, err = ctx.Providers.OpenIDConnect.GetSubject(ctx, client.GetSectorIdentifierURI(), userSession.Username); err != nil { +			ctx.Logger.Errorf(logFmtErrConsentCantGetSubject, requester.GetID(), client.GetID(), client.GetConsentPolicy(), userSession.Username, client.GetSectorIdentifierURI(), err)  			ctx.Providers.OpenIDConnect.WriteAuthorizeError(ctx, rw, requester, oidc.ErrSubjectCouldNotLookup) @@ -82,7 +82,7 @@ func handleOIDCAuthorizationConsent(ctx *middlewares.AutheliaCtx, issuer *url.UR  func handleOIDCAuthorizationConsentNotAuthenticated(ctx *middlewares.AutheliaCtx, issuer *url.URL, _ oidc.Client,  	_ session.UserSession, _ uuid.UUID, -	rw http.ResponseWriter, r *http.Request, requester fosite.AuthorizeRequester) (consent *model.OAuth2ConsentSession, handled bool) { +	rw http.ResponseWriter, r *http.Request, requester oauthelia2.AuthorizeRequester) (consent *model.OAuth2ConsentSession, handled bool) {  	redirectionURL := handleOIDCAuthorizationConsentGetRedirectionURL(ctx, issuer, nil, requester, r.Form)  	handleOIDCPushedAuthorizeConsent(ctx, requester, r.Form) @@ -94,7 +94,7 @@ func handleOIDCAuthorizationConsentNotAuthenticated(ctx *middlewares.AutheliaCtx  func handleOIDCAuthorizationConsentGenerate(ctx *middlewares.AutheliaCtx, issuer *url.URL, client oidc.Client,  	userSession session.UserSession, subject uuid.UUID, -	rw http.ResponseWriter, r *http.Request, requester fosite.AuthorizeRequester) (consent *model.OAuth2ConsentSession, handled bool) { +	rw http.ResponseWriter, r *http.Request, requester oauthelia2.AuthorizeRequester) (consent *model.OAuth2ConsentSession, handled bool) {  	var (  		err error  	) @@ -131,7 +131,7 @@ func handleOIDCAuthorizationConsentGenerate(ctx *middlewares.AutheliaCtx, issuer  }  func handleOIDCAuthorizationConsentRedirect(ctx *middlewares.AutheliaCtx, issuer *url.URL, consent *model.OAuth2ConsentSession, client oidc.Client, -	userSession session.UserSession, rw http.ResponseWriter, r *http.Request, requester fosite.AuthorizeRequester) { +	userSession session.UserSession, rw http.ResponseWriter, r *http.Request, requester oauthelia2.AuthorizeRequester) {  	var location *url.URL  	if client.IsAuthenticationLevelSufficient(userSession.AuthenticationLevel, authorization.Subject{Username: userSession.Username, Groups: userSession.Groups, IP: ctx.RemoteIP()}) { @@ -157,7 +157,7 @@ func handleOIDCAuthorizationConsentRedirect(ctx *middlewares.AutheliaCtx, issuer  	http.Redirect(rw, r, location.String(), http.StatusFound)  } -func handleOIDCPushedAuthorizeConsent(ctx *middlewares.AutheliaCtx, requester fosite.AuthorizeRequester, form url.Values) { +func handleOIDCPushedAuthorizeConsent(ctx *middlewares.AutheliaCtx, requester oauthelia2.AuthorizeRequester, form url.Values) {  	if !oidc.IsPushedAuthorizedRequest(requester, ctx.Providers.OpenIDConnect.GetPushedAuthorizeRequestURIPrefix(ctx)) {  		return  	} @@ -178,7 +178,7 @@ func handleOIDCPushedAuthorizeConsent(ctx *middlewares.AutheliaCtx, requester fo  	}  } -func handleOIDCAuthorizationConsentGetRedirectionURL(_ *middlewares.AutheliaCtx, issuer *url.URL, consent *model.OAuth2ConsentSession, requester fosite.AuthorizeRequester, form url.Values) (redirectURL *url.URL) { +func handleOIDCAuthorizationConsentGetRedirectionURL(_ *middlewares.AutheliaCtx, issuer *url.URL, consent *model.OAuth2ConsentSession, requester oauthelia2.AuthorizeRequester, form url.Values) (redirectURL *url.URL) {  	iss := issuer.String()  	if !strings.HasSuffix(iss, "/") { @@ -212,7 +212,7 @@ func handleOIDCAuthorizationConsentGetRedirectionURL(_ *middlewares.AutheliaCtx,  	return redirectURL  } -func handleOpenIDConnectNewConsentSession(subject uuid.UUID, requester fosite.AuthorizeRequester, prefixPAR string) (consent *model.OAuth2ConsentSession, err error) { +func handleOpenIDConnectNewConsentSession(subject uuid.UUID, requester oauthelia2.AuthorizeRequester, prefixPAR string) (consent *model.OAuth2ConsentSession, err error) {  	if oidc.IsPushedAuthorizedRequest(requester, prefixPAR) {  		form := url.Values{} @@ -238,7 +238,7 @@ func verifyOIDCUserAuthorizedForConsent(ctx *middlewares.AutheliaCtx, client oid  	}  	if sid = subject.ID(); sid == 0 { -		if subject, err = ctx.Providers.OpenIDConnect.GetSubject(ctx, client.GetSectorIdentifier(), userSession.Username); err != nil { +		if subject, err = ctx.Providers.OpenIDConnect.GetSubject(ctx, client.GetSectorIdentifierURI(), userSession.Username); err != nil {  			return fmt.Errorf("failed to lookup subject: %w", err)  		} @@ -258,7 +258,7 @@ func verifyOIDCUserAuthorizedForConsent(ctx *middlewares.AutheliaCtx, client oid  	}  	if consent.Subject.UUID.ID() != sid { -		return fmt.Errorf("the consent subject identifier '%s' isn't owned by user '%s' who has a subject identifier of '%s' with sector identifier '%s'", consent.Subject.UUID, userSession.Username, subject, client.GetSectorIdentifier()) +		return fmt.Errorf("the consent subject identifier '%s' isn't owned by user '%s' who has a subject identifier of '%s' with sector identifier '%s'", consent.Subject.UUID, userSession.Username, subject, client.GetSectorIdentifierURI())  	}  	return nil diff --git a/internal/handlers/handler_oidc_authorization_consent_explicit.go b/internal/handlers/handler_oidc_authorization_consent_explicit.go index 901806c9c..9e2662203 100644 --- a/internal/handlers/handler_oidc_authorization_consent_explicit.go +++ b/internal/handlers/handler_oidc_authorization_consent_explicit.go @@ -4,8 +4,8 @@ import (  	"net/http"  	"net/url" +	oauthelia2 "authelia.com/provider/oauth2"  	"github.com/google/uuid" -	"github.com/ory/fosite"  	"github.com/authelia/authelia/v4/internal/middlewares"  	"github.com/authelia/authelia/v4/internal/model" @@ -15,7 +15,7 @@ import (  func handleOIDCAuthorizationConsentModeExplicit(ctx *middlewares.AutheliaCtx, issuer *url.URL, client oidc.Client,  	userSession session.UserSession, subject uuid.UUID, -	rw http.ResponseWriter, r *http.Request, requester fosite.AuthorizeRequester) (consent *model.OAuth2ConsentSession, handled bool) { +	rw http.ResponseWriter, r *http.Request, requester oauthelia2.AuthorizeRequester) (consent *model.OAuth2ConsentSession, handled bool) {  	var (  		consentID uuid.UUID  		err       error @@ -41,7 +41,7 @@ func handleOIDCAuthorizationConsentModeExplicit(ctx *middlewares.AutheliaCtx, is  func handleOIDCAuthorizationConsentModeExplicitWithID(ctx *middlewares.AutheliaCtx, issuer *url.URL, client oidc.Client,  	userSession session.UserSession, subject uuid.UUID, consentID uuid.UUID, -	rw http.ResponseWriter, r *http.Request, requester fosite.AuthorizeRequester) (consent *model.OAuth2ConsentSession, handled bool) { +	rw http.ResponseWriter, r *http.Request, requester oauthelia2.AuthorizeRequester) (consent *model.OAuth2ConsentSession, handled bool) {  	var (  		err error  	) @@ -82,7 +82,7 @@ func handleOIDCAuthorizationConsentModeExplicitWithID(ctx *middlewares.AutheliaC  		if consent.Responded() {  			ctx.Logger.Errorf(logFmtErrConsentCantGrantRejected, requester.GetID(), client.GetID(), client.GetConsentPolicy(), consent.ChallengeID) -			ctx.Providers.OpenIDConnect.WriteAuthorizeError(ctx, rw, requester, fosite.ErrAccessDenied) +			ctx.Providers.OpenIDConnect.WriteAuthorizeError(ctx, rw, requester, oauthelia2.ErrAccessDenied)  			return nil, true  		} diff --git a/internal/handlers/handler_oidc_authorization_consent_implicit.go b/internal/handlers/handler_oidc_authorization_consent_implicit.go index 5db08d799..4b0ea6a9e 100644 --- a/internal/handlers/handler_oidc_authorization_consent_implicit.go +++ b/internal/handlers/handler_oidc_authorization_consent_implicit.go @@ -4,8 +4,8 @@ import (  	"net/http"  	"net/url" +	oauthelia2 "authelia.com/provider/oauth2"  	"github.com/google/uuid" -	"github.com/ory/fosite"  	"github.com/authelia/authelia/v4/internal/middlewares"  	"github.com/authelia/authelia/v4/internal/model" @@ -15,7 +15,7 @@ import (  func handleOIDCAuthorizationConsentModeImplicit(ctx *middlewares.AutheliaCtx, issuer *url.URL, client oidc.Client,  	userSession session.UserSession, subject uuid.UUID, -	rw http.ResponseWriter, r *http.Request, requester fosite.AuthorizeRequester) (consent *model.OAuth2ConsentSession, handled bool) { +	rw http.ResponseWriter, r *http.Request, requester oauthelia2.AuthorizeRequester) (consent *model.OAuth2ConsentSession, handled bool) {  	var (  		consentID uuid.UUID  		err       error @@ -39,7 +39,7 @@ func handleOIDCAuthorizationConsentModeImplicit(ctx *middlewares.AutheliaCtx, is  func handleOIDCAuthorizationConsentModeImplicitWithID(ctx *middlewares.AutheliaCtx, _ *url.URL, client oidc.Client,  	userSession session.UserSession, subject uuid.UUID, consentID uuid.UUID, -	rw http.ResponseWriter, _ *http.Request, requester fosite.AuthorizeRequester) (consent *model.OAuth2ConsentSession, handled bool) { +	rw http.ResponseWriter, _ *http.Request, requester oauthelia2.AuthorizeRequester) (consent *model.OAuth2ConsentSession, handled bool) {  	var (  		err error  	) @@ -91,7 +91,7 @@ func handleOIDCAuthorizationConsentModeImplicitWithID(ctx *middlewares.AutheliaC  func handleOIDCAuthorizationConsentModeImplicitWithoutID(ctx *middlewares.AutheliaCtx, _ *url.URL, client oidc.Client,  	_ session.UserSession, subject uuid.UUID, -	rw http.ResponseWriter, _ *http.Request, requester fosite.AuthorizeRequester) (consent *model.OAuth2ConsentSession, handled bool) { +	rw http.ResponseWriter, _ *http.Request, requester oauthelia2.AuthorizeRequester) (consent *model.OAuth2ConsentSession, handled bool) {  	var (  		err error  	) diff --git a/internal/handlers/handler_oidc_authorization_consent_pre_configured.go b/internal/handlers/handler_oidc_authorization_consent_pre_configured.go index 0282c84f4..22cd7b2f3 100644 --- a/internal/handlers/handler_oidc_authorization_consent_pre_configured.go +++ b/internal/handlers/handler_oidc_authorization_consent_pre_configured.go @@ -7,8 +7,8 @@ import (  	"net/url"  	"strings" +	oauthelia2 "authelia.com/provider/oauth2"  	"github.com/google/uuid" -	"github.com/ory/fosite"  	"github.com/authelia/authelia/v4/internal/middlewares"  	"github.com/authelia/authelia/v4/internal/model" @@ -19,7 +19,7 @@ import (  func handleOIDCAuthorizationConsentModePreConfigured(ctx *middlewares.AutheliaCtx, issuer *url.URL, client oidc.Client,  	userSession session.UserSession, subject uuid.UUID, -	rw http.ResponseWriter, r *http.Request, requester fosite.AuthorizeRequester) (consent *model.OAuth2ConsentSession, handled bool) { +	rw http.ResponseWriter, r *http.Request, requester oauthelia2.AuthorizeRequester) (consent *model.OAuth2ConsentSession, handled bool) {  	var (  		consentID uuid.UUID  		err       error @@ -45,7 +45,7 @@ func handleOIDCAuthorizationConsentModePreConfigured(ctx *middlewares.AutheliaCt  func handleOIDCAuthorizationConsentModePreConfiguredWithID(ctx *middlewares.AutheliaCtx, issuer *url.URL, client oidc.Client,  	userSession session.UserSession, subject uuid.UUID, consentID uuid.UUID, -	rw http.ResponseWriter, r *http.Request, requester fosite.AuthorizeRequester) (consent *model.OAuth2ConsentSession, handled bool) { +	rw http.ResponseWriter, r *http.Request, requester oauthelia2.AuthorizeRequester) (consent *model.OAuth2ConsentSession, handled bool) {  	var (  		config *model.OAuth2ConsentPreConfig  		err    error @@ -111,7 +111,7 @@ func handleOIDCAuthorizationConsentModePreConfiguredWithID(ctx *middlewares.Auth  		if consent.Responded() {  			ctx.Logger.Errorf(logFmtErrConsentCantGrantRejected, requester.GetID(), client.GetID(), client.GetConsentPolicy(), consent.ChallengeID) -			ctx.Providers.OpenIDConnect.WriteAuthorizeError(ctx, rw, requester, fosite.ErrAccessDenied) +			ctx.Providers.OpenIDConnect.WriteAuthorizeError(ctx, rw, requester, oauthelia2.ErrAccessDenied)  			return nil, true  		} @@ -126,7 +126,7 @@ func handleOIDCAuthorizationConsentModePreConfiguredWithID(ctx *middlewares.Auth  func handleOIDCAuthorizationConsentModePreConfiguredWithoutID(ctx *middlewares.AutheliaCtx, issuer *url.URL, client oidc.Client,  	userSession session.UserSession, subject uuid.UUID, -	rw http.ResponseWriter, r *http.Request, requester fosite.AuthorizeRequester) (consent *model.OAuth2ConsentSession, handled bool) { +	rw http.ResponseWriter, r *http.Request, requester oauthelia2.AuthorizeRequester) (consent *model.OAuth2ConsentSession, handled bool) {  	var (  		config *model.OAuth2ConsentPreConfig  		err    error @@ -183,7 +183,7 @@ func handleOIDCAuthorizationConsentModePreConfiguredWithoutID(ctx *middlewares.A  	return consent, false  } -func handleOIDCAuthorizationConsentModePreConfiguredGetPreConfig(ctx *middlewares.AutheliaCtx, client oidc.Client, subject uuid.UUID, requester fosite.Requester) (config *model.OAuth2ConsentPreConfig, err error) { +func handleOIDCAuthorizationConsentModePreConfiguredGetPreConfig(ctx *middlewares.AutheliaCtx, client oidc.Client, subject uuid.UUID, requester oauthelia2.Requester) (config *model.OAuth2ConsentPreConfig, err error) {  	var (  		rows *storage.ConsentPreConfigRows  	) diff --git a/internal/handlers/handler_oidc_token.go b/internal/handlers/handler_oidc_token.go index cb8cc854f..468f8f51e 100644 --- a/internal/handlers/handler_oidc_token.go +++ b/internal/handlers/handler_oidc_token.go @@ -3,7 +3,7 @@ package handlers  import (  	"net/http" -	"github.com/ory/fosite" +	oauthelia2 "authelia.com/provider/oauth2"  	"github.com/authelia/authelia/v4/internal/middlewares"  	"github.com/authelia/authelia/v4/internal/oidc" @@ -14,8 +14,8 @@ import (  // https://openid.net/specs/openid-connect-core-1_0.html#TokenEndpoint  func OpenIDConnectTokenPOST(ctx *middlewares.AutheliaCtx, rw http.ResponseWriter, req *http.Request) {  	var ( -		requester fosite.AccessRequester -		responder fosite.AccessResponder +		requester oauthelia2.AccessRequester +		responder oauthelia2.AccessResponder  		err       error  	) diff --git a/internal/handlers/handler_oidc_userinfo.go b/internal/handlers/handler_oidc_userinfo.go index bd83bc6e2..84a86a58d 100644 --- a/internal/handlers/handler_oidc_userinfo.go +++ b/internal/handlers/handler_oidc_userinfo.go @@ -6,10 +6,10 @@ import (  	"net/http"  	"time" +	oauthelia2 "authelia.com/provider/oauth2" +	"authelia.com/provider/oauth2/handler/oauth2" +	"authelia.com/provider/oauth2/token/jwt"  	"github.com/google/uuid" -	"github.com/ory/fosite" -	"github.com/ory/fosite/handler/oauth2" -	"github.com/ory/fosite/token/jwt"  	"github.com/pkg/errors"  	"github.com/valyala/fasthttp" @@ -24,14 +24,14 @@ import (  func OpenIDConnectUserinfo(ctx *middlewares.AutheliaCtx, rw http.ResponseWriter, req *http.Request) {  	var (  		requestID uuid.UUID -		tokenType fosite.TokenType -		requester fosite.AccessRequester +		tokenType oauthelia2.TokenType +		requester oauthelia2.AccessRequester  		client    oidc.Client  		err       error  	)  	if requestID, err = uuid.NewRandom(); err != nil { -		ctx.Providers.OpenIDConnect.WriteError(rw, req, fosite.ErrServerError) +		ctx.Providers.OpenIDConnect.WriteError(rw, req, oauthelia2.ErrServerError)  		return  	} @@ -40,10 +40,10 @@ func OpenIDConnectUserinfo(ctx *middlewares.AutheliaCtx, rw http.ResponseWriter,  	ctx.Logger.Debugf("UserInfo Request with id '%s' is being processed", requestID) -	if tokenType, requester, err = ctx.Providers.OpenIDConnect.IntrospectToken(req.Context(), fosite.AccessTokenFromRequest(req), fosite.AccessToken, oidcSession); err != nil { +	if tokenType, requester, err = ctx.Providers.OpenIDConnect.IntrospectToken(req.Context(), oauthelia2.AccessTokenFromRequest(req), oauthelia2.AccessToken, oidcSession); err != nil {  		ctx.Logger.Errorf("UserInfo Request with id '%s' failed with error: %s", requestID, oidc.ErrorToDebugRFC6749Error(err)) -		if rfc := fosite.ErrorToRFC6749Error(err); rfc.StatusCode() == http.StatusUnauthorized { +		if rfc := oauthelia2.ErrorToRFC6749Error(err); rfc.StatusCode() == http.StatusUnauthorized {  			rw.Header().Set(fasthttp.HeaderWWWAuthenticate, fmt.Sprintf(`Bearer %s`, oidc.RFC6750Header("", "", rfc)))  		} @@ -54,7 +54,7 @@ func OpenIDConnectUserinfo(ctx *middlewares.AutheliaCtx, rw http.ResponseWriter,  	clientID := requester.GetClient().GetID() -	if tokenType != fosite.AccessToken { +	if tokenType != oauthelia2.AccessToken {  		ctx.Logger.Errorf("UserInfo Request with id '%s' on client with id '%s' failed with error: bearer authorization failed as the token is not an access token", requestID, client.GetID())  		errStr := "Only access tokens are allowed in the authorization header." @@ -82,7 +82,7 @@ func OpenIDConnectUserinfo(ctx *middlewares.AutheliaCtx, rw http.ResponseWriter,  	default:  		ctx.Logger.Errorf("UserInfo Request with id '%s' on client with id '%s' failed to handle session with type '%T'", requestID, client.GetID(), session) -		ctx.Providers.OpenIDConnect.WriteError(rw, req, fosite.ErrServerError.WithDebugf("Failed to handle session with type '%T'.", session)) +		ctx.Providers.OpenIDConnect.WriteError(rw, req, oauthelia2.ErrServerError.WithDebugf("Failed to handle session with type '%T'.", session))  		return  	} @@ -122,7 +122,7 @@ func OpenIDConnectUserinfo(ctx *middlewares.AutheliaCtx, rw http.ResponseWriter,  		var jwk *oidc.JWK  		if jwk = ctx.Providers.OpenIDConnect.KeyManager.Get(ctx, client.GetUserinfoSignedResponseKeyID(), alg); jwk == nil { -			ctx.Providers.OpenIDConnect.WriteError(rw, req, errors.WithStack(fosite.ErrServerError.WithHintf("Unsupported UserInfo signing algorithm '%s'.", alg))) +			ctx.Providers.OpenIDConnect.WriteError(rw, req, errors.WithStack(oauthelia2.ErrServerError.WithHintf("Unsupported UserInfo signing algorithm '%s'.", alg)))  			return  		} @@ -132,7 +132,7 @@ func OpenIDConnectUserinfo(ctx *middlewares.AutheliaCtx, rw http.ResponseWriter,  		var jti uuid.UUID  		if jti, err = uuid.NewRandom(); err != nil { -			ctx.Providers.OpenIDConnect.WriteError(rw, req, fosite.ErrServerError.WithHint("Could not generate JTI.")) +			ctx.Providers.OpenIDConnect.WriteError(rw, req, oauthelia2.ErrServerError.WithHint("Could not generate JTI."))  			return  		} diff --git a/internal/handlers/oidc.go b/internal/handlers/oidc.go index 9febd5fe5..6b0f99733 100644 --- a/internal/handlers/oidc.go +++ b/internal/handlers/oidc.go @@ -1,14 +1,14 @@  package handlers  import ( -	"github.com/ory/fosite" +	oauthelia2 "authelia.com/provider/oauth2"  	"github.com/authelia/authelia/v4/internal/model"  	"github.com/authelia/authelia/v4/internal/oidc"  	"github.com/authelia/authelia/v4/internal/session"  ) -func oidcGrantRequests(ar fosite.AuthorizeRequester, consent *model.OAuth2ConsentSession, userSession *session.UserSession) (extraClaims map[string]any) { +func oidcGrantRequests(ar oauthelia2.AuthorizeRequester, consent *model.OAuth2ConsentSession, userSession *session.UserSession) (extraClaims map[string]any) {  	extraClaims = map[string]any{}  	for _, scope := range consent.GrantedScopes { diff --git a/internal/handlers/types.go b/internal/handlers/types.go index 1a0400768..98e43cde2 100644 --- a/internal/handlers/types.go +++ b/internal/handlers/types.go @@ -5,8 +5,8 @@ import (  	"net/http"  	"net/url" +	oauthelia2 "authelia.com/provider/oauth2"  	"github.com/google/uuid" -	"github.com/ory/fosite"  	"github.com/authelia/authelia/v4/internal/authentication"  	"github.com/authelia/authelia/v4/internal/middlewares" @@ -190,4 +190,4 @@ type handlerAuthorizationConsent func(  	ctx *middlewares.AutheliaCtx, issuer *url.URL, client oidc.Client,  	userSession session.UserSession, subject uuid.UUID,  	rw http.ResponseWriter, r *http.Request, -	requester fosite.AuthorizeRequester) (consent *model.OAuth2ConsentSession, handled bool) +	requester oauthelia2.AuthorizeRequester) (consent *model.OAuth2ConsentSession, handled bool) diff --git a/internal/mocks/duo_api.go b/internal/mocks/duo_api.go index 655dc331b..11c1cd5a2 100644 --- a/internal/mocks/duo_api.go +++ b/internal/mocks/duo_api.go @@ -5,6 +5,7 @@  //  //	mockgen -package mocks -destination duo_api.go -mock_names API=MockAPI github.com/authelia/authelia/v4/internal/duo API  // +  // Package mocks is a generated GoMock package.  package mocks diff --git a/internal/mocks/gen.go b/internal/mocks/gen.go index 52f9cfdcc..a46071d52 100644 --- a/internal/mocks/gen.go +++ b/internal/mocks/gen.go @@ -11,15 +11,15 @@ package mocks  //go:generate mockgen -package mocks -destination random.go -mock_names Provider=MockRandom github.com/authelia/authelia/v4/internal/random Provider  // Fosite Mocks. -//go:generate mockgen -package mocks -destination fosite_client_credentials_grant_storage.go -mock_names Provider=MockClientCredentialsGrantStorage github.com/ory/fosite/handler/oauth2 ClientCredentialsGrantStorage -//go:generate mockgen -package mocks -destination fosite_token_revocation_storage.go -mock_names Provider=MockTokenRevocationStorage github.com/ory/fosite/handler/oauth2 TokenRevocationStorage -//go:generate mockgen -package mocks -destination fosite_access_token_strategy.go -mock_names Provider=MockAccessTokenStrategy github.com/ory/fosite/handler/oauth2 AccessTokenStrategy +//go:generate mockgen -package mocks -destination oauth2_client_credentials_grant_storage.go -mock_names Provider=MockClientCredentialsGrantStorage authelia.com/provider/oauth2/handler/oauth2 ClientCredentialsGrantStorage +//go:generate mockgen -package mocks -destination oauth2_token_revocation_storage.go -mock_names Provider=MockTokenRevocationStorage authelia.com/provider/oauth2/handler/oauth2 TokenRevocationStorage +//go:generate mockgen -package mocks -destination oauth2_access_token_strategy.go -mock_names Provider=MockAccessTokenStrategy authelia.com/provider/oauth2/handler/oauth2 AccessTokenStrategy -//go:generate mockgen -package mocks -destination fosite_pkce_request_storage.go -mock_names Provider=MockPKCERequestStorage github.com/ory/fosite/handler/pkce PKCERequestStorage +//go:generate mockgen -package mocks -destination oauth2_pkce_request_storage.go -mock_names Provider=MockPKCERequestStorage authelia.com/provider/oauth2/handler/pkce PKCERequestStorage -//go:generate mockgen -package mocks -destination fosite_access_requester.go -mock_names Provider=MockAccessRequester github.com/ory/fosite AccessRequester +//go:generate mockgen -package mocks -destination oauth2_access_requester.go -mock_names Provider=MockAccessRequester authelia.com/provider/oauth2 AccessRequester -//go:generate mockgen -package mocks -destination fosite_transactional.go -mock_names Provider=MockTransactional github.com/ory/fosite/storage Transactional +//go:generate mockgen -package mocks -destination oauth2_transactional.go -mock_names Provider=MockTransactional authelia.com/provider/oauth2/storage Transactional -//go:generate mockgen -package mocks -destination fosite_token_introspector.go -mock_names TokenIntrospector=MockTokenIntrospector github.com/ory/fosite TokenIntrospector -//go:generate mockgen -package mocks -destination fosite_storage.go -mock_names Storage=MockFositeStorage github.com/ory/fosite Storage +//go:generate mockgen -package mocks -destination oauth2_token_introspector.go -mock_names TokenIntrospector=MockTokenIntrospector authelia.com/provider/oauth2 TokenIntrospector +//go:generate mockgen -package mocks -destination oauth2_storage.go -mock_names Storage=MockOAuth2Storage authelia.com/provider/oauth2 Storage diff --git a/internal/mocks/notifier.go b/internal/mocks/notifier.go index 6f31b8e7a..3a5026511 100644 --- a/internal/mocks/notifier.go +++ b/internal/mocks/notifier.go @@ -5,6 +5,7 @@  //  //	mockgen -package mocks -destination notifier.go -mock_names Notifier=MockNotifier github.com/authelia/authelia/v4/internal/notification Notifier  // +  // Package mocks is a generated GoMock package.  package mocks diff --git a/internal/mocks/fosite_access_requester.go b/internal/mocks/oauth2_access_requester.go index cea0a3a64..e9b29ad49 100644 --- a/internal/mocks/fosite_access_requester.go +++ b/internal/mocks/oauth2_access_requester.go @@ -1,10 +1,11 @@  // Code generated by MockGen. DO NOT EDIT. -// Source: github.com/ory/fosite (interfaces: AccessRequester) +// Source: authelia.com/provider/oauth2 (interfaces: AccessRequester)  //  // Generated by this command:  // -//	mockgen -package mocks -destination fosite_access_requester.go -mock_names Provider=MockAccessRequester github.com/ory/fosite AccessRequester +//	mockgen -package mocks -destination oauth2_access_requester.go -mock_names Provider=MockAccessRequester authelia.com/provider/oauth2 AccessRequester  // +  // Package mocks is a generated GoMock package.  package mocks @@ -13,7 +14,7 @@ import (  	reflect "reflect"  	time "time" -	fosite "github.com/ory/fosite" +	oauth2 "authelia.com/provider/oauth2"  	gomock "go.uber.org/mock/gomock"  ) @@ -53,10 +54,10 @@ func (mr *MockAccessRequesterMockRecorder) AppendRequestedScope(arg0 any) *gomoc  }  // GetClient mocks base method. -func (m *MockAccessRequester) GetClient() fosite.Client { +func (m *MockAccessRequester) GetClient() oauth2.Client {  	m.ctrl.T.Helper()  	ret := m.ctrl.Call(m, "GetClient") -	ret0, _ := ret[0].(fosite.Client) +	ret0, _ := ret[0].(oauth2.Client)  	return ret0  } @@ -67,10 +68,10 @@ func (mr *MockAccessRequesterMockRecorder) GetClient() *gomock.Call {  }  // GetGrantTypes mocks base method. -func (m *MockAccessRequester) GetGrantTypes() fosite.Arguments { +func (m *MockAccessRequester) GetGrantTypes() oauth2.Arguments {  	m.ctrl.T.Helper()  	ret := m.ctrl.Call(m, "GetGrantTypes") -	ret0, _ := ret[0].(fosite.Arguments) +	ret0, _ := ret[0].(oauth2.Arguments)  	return ret0  } @@ -81,10 +82,10 @@ func (mr *MockAccessRequesterMockRecorder) GetGrantTypes() *gomock.Call {  }  // GetGrantedAudience mocks base method. -func (m *MockAccessRequester) GetGrantedAudience() fosite.Arguments { +func (m *MockAccessRequester) GetGrantedAudience() oauth2.Arguments {  	m.ctrl.T.Helper()  	ret := m.ctrl.Call(m, "GetGrantedAudience") -	ret0, _ := ret[0].(fosite.Arguments) +	ret0, _ := ret[0].(oauth2.Arguments)  	return ret0  } @@ -95,10 +96,10 @@ func (mr *MockAccessRequesterMockRecorder) GetGrantedAudience() *gomock.Call {  }  // GetGrantedScopes mocks base method. -func (m *MockAccessRequester) GetGrantedScopes() fosite.Arguments { +func (m *MockAccessRequester) GetGrantedScopes() oauth2.Arguments {  	m.ctrl.T.Helper()  	ret := m.ctrl.Call(m, "GetGrantedScopes") -	ret0, _ := ret[0].(fosite.Arguments) +	ret0, _ := ret[0].(oauth2.Arguments)  	return ret0  } @@ -151,10 +152,10 @@ func (mr *MockAccessRequesterMockRecorder) GetRequestedAt() *gomock.Call {  }  // GetRequestedAudience mocks base method. -func (m *MockAccessRequester) GetRequestedAudience() fosite.Arguments { +func (m *MockAccessRequester) GetRequestedAudience() oauth2.Arguments {  	m.ctrl.T.Helper()  	ret := m.ctrl.Call(m, "GetRequestedAudience") -	ret0, _ := ret[0].(fosite.Arguments) +	ret0, _ := ret[0].(oauth2.Arguments)  	return ret0  } @@ -165,10 +166,10 @@ func (mr *MockAccessRequesterMockRecorder) GetRequestedAudience() *gomock.Call {  }  // GetRequestedScopes mocks base method. -func (m *MockAccessRequester) GetRequestedScopes() fosite.Arguments { +func (m *MockAccessRequester) GetRequestedScopes() oauth2.Arguments {  	m.ctrl.T.Helper()  	ret := m.ctrl.Call(m, "GetRequestedScopes") -	ret0, _ := ret[0].(fosite.Arguments) +	ret0, _ := ret[0].(oauth2.Arguments)  	return ret0  } @@ -179,10 +180,10 @@ func (mr *MockAccessRequesterMockRecorder) GetRequestedScopes() *gomock.Call {  }  // GetSession mocks base method. -func (m *MockAccessRequester) GetSession() fosite.Session { +func (m *MockAccessRequester) GetSession() oauth2.Session {  	m.ctrl.T.Helper()  	ret := m.ctrl.Call(m, "GetSession") -	ret0, _ := ret[0].(fosite.Session) +	ret0, _ := ret[0].(oauth2.Session)  	return ret0  } @@ -217,7 +218,7 @@ func (mr *MockAccessRequesterMockRecorder) GrantScope(arg0 any) *gomock.Call {  }  // Merge mocks base method. -func (m *MockAccessRequester) Merge(arg0 fosite.Requester) { +func (m *MockAccessRequester) Merge(arg0 oauth2.Requester) {  	m.ctrl.T.Helper()  	m.ctrl.Call(m, "Merge", arg0)  } @@ -229,10 +230,10 @@ func (mr *MockAccessRequesterMockRecorder) Merge(arg0 any) *gomock.Call {  }  // Sanitize mocks base method. -func (m *MockAccessRequester) Sanitize(arg0 []string) fosite.Requester { +func (m *MockAccessRequester) Sanitize(arg0 []string) oauth2.Requester {  	m.ctrl.T.Helper()  	ret := m.ctrl.Call(m, "Sanitize", arg0) -	ret0, _ := ret[0].(fosite.Requester) +	ret0, _ := ret[0].(oauth2.Requester)  	return ret0  } @@ -255,7 +256,7 @@ func (mr *MockAccessRequesterMockRecorder) SetID(arg0 any) *gomock.Call {  }  // SetRequestedAudience mocks base method. -func (m *MockAccessRequester) SetRequestedAudience(arg0 fosite.Arguments) { +func (m *MockAccessRequester) SetRequestedAudience(arg0 oauth2.Arguments) {  	m.ctrl.T.Helper()  	m.ctrl.Call(m, "SetRequestedAudience", arg0)  } @@ -267,7 +268,7 @@ func (mr *MockAccessRequesterMockRecorder) SetRequestedAudience(arg0 any) *gomoc  }  // SetRequestedScopes mocks base method. -func (m *MockAccessRequester) SetRequestedScopes(arg0 fosite.Arguments) { +func (m *MockAccessRequester) SetRequestedScopes(arg0 oauth2.Arguments) {  	m.ctrl.T.Helper()  	m.ctrl.Call(m, "SetRequestedScopes", arg0)  } @@ -279,7 +280,7 @@ func (mr *MockAccessRequesterMockRecorder) SetRequestedScopes(arg0 any) *gomock.  }  // SetSession mocks base method. -func (m *MockAccessRequester) SetSession(arg0 fosite.Session) { +func (m *MockAccessRequester) SetSession(arg0 oauth2.Session) {  	m.ctrl.T.Helper()  	m.ctrl.Call(m, "SetSession", arg0)  } diff --git a/internal/mocks/fosite_access_token_strategy.go b/internal/mocks/oauth2_access_token_strategy.go index 319c5fd6c..a1ec65fb4 100644 --- a/internal/mocks/fosite_access_token_strategy.go +++ b/internal/mocks/oauth2_access_token_strategy.go @@ -1,10 +1,11 @@  // Code generated by MockGen. DO NOT EDIT. -// Source: github.com/ory/fosite/handler/oauth2 (interfaces: AccessTokenStrategy) +// Source: authelia.com/provider/oauth2/handler/oauth2 (interfaces: AccessTokenStrategy)  //  // Generated by this command:  // -//	mockgen -package mocks -destination fosite_access_token_strategy.go -mock_names Provider=MockAccessTokenStrategy github.com/ory/fosite/handler/oauth2 AccessTokenStrategy +//	mockgen -package mocks -destination oauth2_access_token_strategy.go -mock_names Provider=MockAccessTokenStrategy authelia.com/provider/oauth2/handler/oauth2 AccessTokenStrategy  // +  // Package mocks is a generated GoMock package.  package mocks @@ -12,7 +13,7 @@ import (  	context "context"  	reflect "reflect" -	fosite "github.com/ory/fosite" +	oauth2 "authelia.com/provider/oauth2"  	gomock "go.uber.org/mock/gomock"  ) @@ -54,7 +55,7 @@ func (mr *MockAccessTokenStrategyMockRecorder) AccessTokenSignature(arg0, arg1 a  }  // GenerateAccessToken mocks base method. -func (m *MockAccessTokenStrategy) GenerateAccessToken(arg0 context.Context, arg1 fosite.Requester) (string, string, error) { +func (m *MockAccessTokenStrategy) GenerateAccessToken(arg0 context.Context, arg1 oauth2.Requester) (string, string, error) {  	m.ctrl.T.Helper()  	ret := m.ctrl.Call(m, "GenerateAccessToken", arg0, arg1)  	ret0, _ := ret[0].(string) @@ -70,7 +71,7 @@ func (mr *MockAccessTokenStrategyMockRecorder) GenerateAccessToken(arg0, arg1 an  }  // ValidateAccessToken mocks base method. -func (m *MockAccessTokenStrategy) ValidateAccessToken(arg0 context.Context, arg1 fosite.Requester, arg2 string) error { +func (m *MockAccessTokenStrategy) ValidateAccessToken(arg0 context.Context, arg1 oauth2.Requester, arg2 string) error {  	m.ctrl.T.Helper()  	ret := m.ctrl.Call(m, "ValidateAccessToken", arg0, arg1, arg2)  	ret0, _ := ret[0].(error) diff --git a/internal/mocks/fosite_client_credentials_grant_storage.go b/internal/mocks/oauth2_client_credentials_grant_storage.go index a35d2bc52..44621eeb9 100644 --- a/internal/mocks/fosite_client_credentials_grant_storage.go +++ b/internal/mocks/oauth2_client_credentials_grant_storage.go @@ -1,10 +1,11 @@  // Code generated by MockGen. DO NOT EDIT. -// Source: github.com/ory/fosite/handler/oauth2 (interfaces: ClientCredentialsGrantStorage) +// Source: authelia.com/provider/oauth2/handler/oauth2 (interfaces: ClientCredentialsGrantStorage)  //  // Generated by this command:  // -//	mockgen -package mocks -destination fosite_client_credentials_grant_storage.go -mock_names Provider=MockClientCredentialsGrantStorage github.com/ory/fosite/handler/oauth2 ClientCredentialsGrantStorage +//	mockgen -package mocks -destination oauth2_client_credentials_grant_storage.go -mock_names Provider=MockClientCredentialsGrantStorage authelia.com/provider/oauth2/handler/oauth2 ClientCredentialsGrantStorage  // +  // Package mocks is a generated GoMock package.  package mocks @@ -12,7 +13,7 @@ import (  	context "context"  	reflect "reflect" -	fosite "github.com/ory/fosite" +	oauth2 "authelia.com/provider/oauth2"  	gomock "go.uber.org/mock/gomock"  ) @@ -40,7 +41,7 @@ func (m *MockClientCredentialsGrantStorage) EXPECT() *MockClientCredentialsGrant  }  // CreateAccessTokenSession mocks base method. -func (m *MockClientCredentialsGrantStorage) CreateAccessTokenSession(arg0 context.Context, arg1 string, arg2 fosite.Requester) error { +func (m *MockClientCredentialsGrantStorage) CreateAccessTokenSession(arg0 context.Context, arg1 string, arg2 oauth2.Requester) error {  	m.ctrl.T.Helper()  	ret := m.ctrl.Call(m, "CreateAccessTokenSession", arg0, arg1, arg2)  	ret0, _ := ret[0].(error) @@ -68,10 +69,10 @@ func (mr *MockClientCredentialsGrantStorageMockRecorder) DeleteAccessTokenSessio  }  // GetAccessTokenSession mocks base method. -func (m *MockClientCredentialsGrantStorage) GetAccessTokenSession(arg0 context.Context, arg1 string, arg2 fosite.Session) (fosite.Requester, error) { +func (m *MockClientCredentialsGrantStorage) GetAccessTokenSession(arg0 context.Context, arg1 string, arg2 oauth2.Session) (oauth2.Requester, error) {  	m.ctrl.T.Helper()  	ret := m.ctrl.Call(m, "GetAccessTokenSession", arg0, arg1, arg2) -	ret0, _ := ret[0].(fosite.Requester) +	ret0, _ := ret[0].(oauth2.Requester)  	ret1, _ := ret[1].(error)  	return ret0, ret1  } diff --git a/internal/mocks/fosite_pkce_request_storage.go b/internal/mocks/oauth2_pkce_request_storage.go index 1ada6e2d5..dcfc951f3 100644 --- a/internal/mocks/fosite_pkce_request_storage.go +++ b/internal/mocks/oauth2_pkce_request_storage.go @@ -1,10 +1,11 @@  // Code generated by MockGen. DO NOT EDIT. -// Source: github.com/ory/fosite/handler/pkce (interfaces: PKCERequestStorage) +// Source: authelia.com/provider/oauth2/handler/pkce (interfaces: PKCERequestStorage)  //  // Generated by this command:  // -//	mockgen -package mocks -destination fosite_pkce_request_storage.go -mock_names Provider=MockPKCERequestStorage github.com/ory/fosite/handler/pkce PKCERequestStorage +//	mockgen -package mocks -destination oauth2_pkce_request_storage.go -mock_names Provider=MockPKCERequestStorage authelia.com/provider/oauth2/handler/pkce PKCERequestStorage  // +  // Package mocks is a generated GoMock package.  package mocks @@ -12,7 +13,7 @@ import (  	context "context"  	reflect "reflect" -	fosite "github.com/ory/fosite" +	oauth2 "authelia.com/provider/oauth2"  	gomock "go.uber.org/mock/gomock"  ) @@ -40,7 +41,7 @@ func (m *MockPKCERequestStorage) EXPECT() *MockPKCERequestStorageMockRecorder {  }  // CreatePKCERequestSession mocks base method. -func (m *MockPKCERequestStorage) CreatePKCERequestSession(arg0 context.Context, arg1 string, arg2 fosite.Requester) error { +func (m *MockPKCERequestStorage) CreatePKCERequestSession(arg0 context.Context, arg1 string, arg2 oauth2.Requester) error {  	m.ctrl.T.Helper()  	ret := m.ctrl.Call(m, "CreatePKCERequestSession", arg0, arg1, arg2)  	ret0, _ := ret[0].(error) @@ -68,10 +69,10 @@ func (mr *MockPKCERequestStorageMockRecorder) DeletePKCERequestSession(arg0, arg  }  // GetPKCERequestSession mocks base method. -func (m *MockPKCERequestStorage) GetPKCERequestSession(arg0 context.Context, arg1 string, arg2 fosite.Session) (fosite.Requester, error) { +func (m *MockPKCERequestStorage) GetPKCERequestSession(arg0 context.Context, arg1 string, arg2 oauth2.Session) (oauth2.Requester, error) {  	m.ctrl.T.Helper()  	ret := m.ctrl.Call(m, "GetPKCERequestSession", arg0, arg1, arg2) -	ret0, _ := ret[0].(fosite.Requester) +	ret0, _ := ret[0].(oauth2.Requester)  	ret1, _ := ret[1].(error)  	return ret0, ret1  } diff --git a/internal/mocks/fosite_storage.go b/internal/mocks/oauth2_storage.go index e7b99271b..0e806db39 100644 --- a/internal/mocks/fosite_storage.go +++ b/internal/mocks/oauth2_storage.go @@ -1,10 +1,11 @@  // Code generated by MockGen. DO NOT EDIT. -// Source: github.com/ory/fosite (interfaces: Storage) +// Source: authelia.com/provider/oauth2 (interfaces: Storage)  //  // Generated by this command:  // -//	mockgen -package mocks -destination fosite_storage.go -mock_names Storage=MockFositeStorage github.com/ory/fosite Storage +//	mockgen -package mocks -destination oauth2_storage.go -mock_names Storage=MockOAuth2Storage authelia.com/provider/oauth2 Storage  // +  // Package mocks is a generated GoMock package.  package mocks @@ -13,35 +14,35 @@ import (  	reflect "reflect"  	time "time" -	fosite "github.com/ory/fosite" +	oauth2 "authelia.com/provider/oauth2"  	gomock "go.uber.org/mock/gomock"  ) -// MockFositeStorage is a mock of Storage interface. -type MockFositeStorage struct { +// MockOAuth2Storage is a mock of Storage interface. +type MockOAuth2Storage struct {  	ctrl     *gomock.Controller -	recorder *MockFositeStorageMockRecorder +	recorder *MockOAuth2StorageMockRecorder  } -// MockFositeStorageMockRecorder is the mock recorder for MockFositeStorage. -type MockFositeStorageMockRecorder struct { -	mock *MockFositeStorage +// MockOAuth2StorageMockRecorder is the mock recorder for MockOAuth2Storage. +type MockOAuth2StorageMockRecorder struct { +	mock *MockOAuth2Storage  } -// NewMockFositeStorage creates a new mock instance. -func NewMockFositeStorage(ctrl *gomock.Controller) *MockFositeStorage { -	mock := &MockFositeStorage{ctrl: ctrl} -	mock.recorder = &MockFositeStorageMockRecorder{mock} +// NewMockOAuth2Storage creates a new mock instance. +func NewMockOAuth2Storage(ctrl *gomock.Controller) *MockOAuth2Storage { +	mock := &MockOAuth2Storage{ctrl: ctrl} +	mock.recorder = &MockOAuth2StorageMockRecorder{mock}  	return mock  }  // EXPECT returns an object that allows the caller to indicate expected use. -func (m *MockFositeStorage) EXPECT() *MockFositeStorageMockRecorder { +func (m *MockOAuth2Storage) EXPECT() *MockOAuth2StorageMockRecorder {  	return m.recorder  }  // ClientAssertionJWTValid mocks base method. -func (m *MockFositeStorage) ClientAssertionJWTValid(arg0 context.Context, arg1 string) error { +func (m *MockOAuth2Storage) ClientAssertionJWTValid(arg0 context.Context, arg1 string) error {  	m.ctrl.T.Helper()  	ret := m.ctrl.Call(m, "ClientAssertionJWTValid", arg0, arg1)  	ret0, _ := ret[0].(error) @@ -49,28 +50,28 @@ func (m *MockFositeStorage) ClientAssertionJWTValid(arg0 context.Context, arg1 s  }  // ClientAssertionJWTValid indicates an expected call of ClientAssertionJWTValid. -func (mr *MockFositeStorageMockRecorder) ClientAssertionJWTValid(arg0, arg1 any) *gomock.Call { +func (mr *MockOAuth2StorageMockRecorder) ClientAssertionJWTValid(arg0, arg1 any) *gomock.Call {  	mr.mock.ctrl.T.Helper() -	return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ClientAssertionJWTValid", reflect.TypeOf((*MockFositeStorage)(nil).ClientAssertionJWTValid), arg0, arg1) +	return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ClientAssertionJWTValid", reflect.TypeOf((*MockOAuth2Storage)(nil).ClientAssertionJWTValid), arg0, arg1)  }  // GetClient mocks base method. -func (m *MockFositeStorage) GetClient(arg0 context.Context, arg1 string) (fosite.Client, error) { +func (m *MockOAuth2Storage) GetClient(arg0 context.Context, arg1 string) (oauth2.Client, error) {  	m.ctrl.T.Helper()  	ret := m.ctrl.Call(m, "GetClient", arg0, arg1) -	ret0, _ := ret[0].(fosite.Client) +	ret0, _ := ret[0].(oauth2.Client)  	ret1, _ := ret[1].(error)  	return ret0, ret1  }  // GetClient indicates an expected call of GetClient. -func (mr *MockFositeStorageMockRecorder) GetClient(arg0, arg1 any) *gomock.Call { +func (mr *MockOAuth2StorageMockRecorder) GetClient(arg0, arg1 any) *gomock.Call {  	mr.mock.ctrl.T.Helper() -	return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetClient", reflect.TypeOf((*MockFositeStorage)(nil).GetClient), arg0, arg1) +	return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetClient", reflect.TypeOf((*MockOAuth2Storage)(nil).GetClient), arg0, arg1)  }  // SetClientAssertionJWT mocks base method. -func (m *MockFositeStorage) SetClientAssertionJWT(arg0 context.Context, arg1 string, arg2 time.Time) error { +func (m *MockOAuth2Storage) SetClientAssertionJWT(arg0 context.Context, arg1 string, arg2 time.Time) error {  	m.ctrl.T.Helper()  	ret := m.ctrl.Call(m, "SetClientAssertionJWT", arg0, arg1, arg2)  	ret0, _ := ret[0].(error) @@ -78,7 +79,7 @@ func (m *MockFositeStorage) SetClientAssertionJWT(arg0 context.Context, arg1 str  }  // SetClientAssertionJWT indicates an expected call of SetClientAssertionJWT. -func (mr *MockFositeStorageMockRecorder) SetClientAssertionJWT(arg0, arg1, arg2 any) *gomock.Call { +func (mr *MockOAuth2StorageMockRecorder) SetClientAssertionJWT(arg0, arg1, arg2 any) *gomock.Call {  	mr.mock.ctrl.T.Helper() -	return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "SetClientAssertionJWT", reflect.TypeOf((*MockFositeStorage)(nil).SetClientAssertionJWT), arg0, arg1, arg2) +	return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "SetClientAssertionJWT", reflect.TypeOf((*MockOAuth2Storage)(nil).SetClientAssertionJWT), arg0, arg1, arg2)  } diff --git a/internal/mocks/fosite_token_introspector.go b/internal/mocks/oauth2_token_introspector.go index 52760ea76..06a991c8f 100644 --- a/internal/mocks/fosite_token_introspector.go +++ b/internal/mocks/oauth2_token_introspector.go @@ -1,10 +1,11 @@  // Code generated by MockGen. DO NOT EDIT. -// Source: github.com/ory/fosite (interfaces: TokenIntrospector) +// Source: authelia.com/provider/oauth2 (interfaces: TokenIntrospector)  //  // Generated by this command:  // -//	mockgen -package mocks -destination fosite_token_introspector.go -mock_names TokenIntrospector=MockTokenIntrospector github.com/ory/fosite TokenIntrospector +//	mockgen -package mocks -destination oauth2_token_introspector.go -mock_names TokenIntrospector=MockTokenIntrospector authelia.com/provider/oauth2 TokenIntrospector  // +  // Package mocks is a generated GoMock package.  package mocks @@ -12,7 +13,7 @@ import (  	context "context"  	reflect "reflect" -	fosite "github.com/ory/fosite" +	oauth2 "authelia.com/provider/oauth2"  	gomock "go.uber.org/mock/gomock"  ) @@ -40,10 +41,10 @@ func (m *MockTokenIntrospector) EXPECT() *MockTokenIntrospectorMockRecorder {  }  // IntrospectToken mocks base method. -func (m *MockTokenIntrospector) IntrospectToken(arg0 context.Context, arg1 string, arg2 fosite.TokenType, arg3 fosite.AccessRequester, arg4 []string) (fosite.TokenType, error) { +func (m *MockTokenIntrospector) IntrospectToken(arg0 context.Context, arg1 string, arg2 oauth2.TokenType, arg3 oauth2.AccessRequester, arg4 []string) (oauth2.TokenType, error) {  	m.ctrl.T.Helper()  	ret := m.ctrl.Call(m, "IntrospectToken", arg0, arg1, arg2, arg3, arg4) -	ret0, _ := ret[0].(fosite.TokenType) +	ret0, _ := ret[0].(oauth2.TokenType)  	ret1, _ := ret[1].(error)  	return ret0, ret1  } diff --git a/internal/mocks/fosite_token_revocation_storage.go b/internal/mocks/oauth2_token_revocation_storage.go index d9836c373..e9a590f9b 100644 --- a/internal/mocks/fosite_token_revocation_storage.go +++ b/internal/mocks/oauth2_token_revocation_storage.go @@ -1,10 +1,11 @@  // Code generated by MockGen. DO NOT EDIT. -// Source: github.com/ory/fosite/handler/oauth2 (interfaces: TokenRevocationStorage) +// Source: authelia.com/provider/oauth2/handler/oauth2 (interfaces: TokenRevocationStorage)  //  // Generated by this command:  // -//	mockgen -package mocks -destination fosite_token_revocation_storage.go -mock_names Provider=MockTokenRevocationStorage github.com/ory/fosite/handler/oauth2 TokenRevocationStorage +//	mockgen -package mocks -destination oauth2_token_revocation_storage.go -mock_names Provider=MockTokenRevocationStorage authelia.com/provider/oauth2/handler/oauth2 TokenRevocationStorage  // +  // Package mocks is a generated GoMock package.  package mocks @@ -12,7 +13,7 @@ import (  	context "context"  	reflect "reflect" -	fosite "github.com/ory/fosite" +	oauth2 "authelia.com/provider/oauth2"  	gomock "go.uber.org/mock/gomock"  ) @@ -40,7 +41,7 @@ func (m *MockTokenRevocationStorage) EXPECT() *MockTokenRevocationStorageMockRec  }  // CreateAccessTokenSession mocks base method. -func (m *MockTokenRevocationStorage) CreateAccessTokenSession(arg0 context.Context, arg1 string, arg2 fosite.Requester) error { +func (m *MockTokenRevocationStorage) CreateAccessTokenSession(arg0 context.Context, arg1 string, arg2 oauth2.Requester) error {  	m.ctrl.T.Helper()  	ret := m.ctrl.Call(m, "CreateAccessTokenSession", arg0, arg1, arg2)  	ret0, _ := ret[0].(error) @@ -54,7 +55,7 @@ func (mr *MockTokenRevocationStorageMockRecorder) CreateAccessTokenSession(arg0,  }  // CreateRefreshTokenSession mocks base method. -func (m *MockTokenRevocationStorage) CreateRefreshTokenSession(arg0 context.Context, arg1 string, arg2 fosite.Requester) error { +func (m *MockTokenRevocationStorage) CreateRefreshTokenSession(arg0 context.Context, arg1 string, arg2 oauth2.Requester) error {  	m.ctrl.T.Helper()  	ret := m.ctrl.Call(m, "CreateRefreshTokenSession", arg0, arg1, arg2)  	ret0, _ := ret[0].(error) @@ -96,10 +97,10 @@ func (mr *MockTokenRevocationStorageMockRecorder) DeleteRefreshTokenSession(arg0  }  // GetAccessTokenSession mocks base method. -func (m *MockTokenRevocationStorage) GetAccessTokenSession(arg0 context.Context, arg1 string, arg2 fosite.Session) (fosite.Requester, error) { +func (m *MockTokenRevocationStorage) GetAccessTokenSession(arg0 context.Context, arg1 string, arg2 oauth2.Session) (oauth2.Requester, error) {  	m.ctrl.T.Helper()  	ret := m.ctrl.Call(m, "GetAccessTokenSession", arg0, arg1, arg2) -	ret0, _ := ret[0].(fosite.Requester) +	ret0, _ := ret[0].(oauth2.Requester)  	ret1, _ := ret[1].(error)  	return ret0, ret1  } @@ -111,10 +112,10 @@ func (mr *MockTokenRevocationStorageMockRecorder) GetAccessTokenSession(arg0, ar  }  // GetRefreshTokenSession mocks base method. -func (m *MockTokenRevocationStorage) GetRefreshTokenSession(arg0 context.Context, arg1 string, arg2 fosite.Session) (fosite.Requester, error) { +func (m *MockTokenRevocationStorage) GetRefreshTokenSession(arg0 context.Context, arg1 string, arg2 oauth2.Session) (oauth2.Requester, error) {  	m.ctrl.T.Helper()  	ret := m.ctrl.Call(m, "GetRefreshTokenSession", arg0, arg1, arg2) -	ret0, _ := ret[0].(fosite.Requester) +	ret0, _ := ret[0].(oauth2.Requester)  	ret1, _ := ret[1].(error)  	return ret0, ret1  } diff --git a/internal/mocks/fosite_transactional.go b/internal/mocks/oauth2_transactional.go index 3ff7a11aa..8de337aff 100644 --- a/internal/mocks/fosite_transactional.go +++ b/internal/mocks/oauth2_transactional.go @@ -1,10 +1,11 @@  // Code generated by MockGen. DO NOT EDIT. -// Source: github.com/ory/fosite/storage (interfaces: Transactional) +// Source: authelia.com/provider/oauth2/storage (interfaces: Transactional)  //  // Generated by this command:  // -//	mockgen -package mocks -destination fosite_transactional.go -mock_names Provider=MockTransactional github.com/ory/fosite/storage Transactional +//	mockgen -package mocks -destination oauth2_transactional.go -mock_names Provider=MockTransactional authelia.com/provider/oauth2/storage Transactional  // +  // Package mocks is a generated GoMock package.  package mocks diff --git a/internal/mocks/random.go b/internal/mocks/random.go index f566c59a1..8621c6939 100644 --- a/internal/mocks/random.go +++ b/internal/mocks/random.go @@ -5,6 +5,7 @@  //  //	mockgen -package mocks -destination random.go -mock_names Provider=MockRandom github.com/authelia/authelia/v4/internal/random Provider  // +  // Package mocks is a generated GoMock package.  package mocks diff --git a/internal/mocks/totp.go b/internal/mocks/totp.go index a2dbc9d7c..b85d26abf 100644 --- a/internal/mocks/totp.go +++ b/internal/mocks/totp.go @@ -5,6 +5,7 @@  //  //	mockgen -package mocks -destination totp.go -mock_names Provider=MockTOTP github.com/authelia/authelia/v4/internal/totp Provider  // +  // Package mocks is a generated GoMock package.  package mocks diff --git a/internal/mocks/user_provider.go b/internal/mocks/user_provider.go index 54e3e478a..e4d5715e7 100644 --- a/internal/mocks/user_provider.go +++ b/internal/mocks/user_provider.go @@ -5,6 +5,7 @@  //  //	mockgen -package mocks -destination user_provider.go -mock_names UserProvider=MockUserProvider github.com/authelia/authelia/v4/internal/authentication UserProvider  // +  // Package mocks is a generated GoMock package.  package mocks diff --git a/internal/model/oidc.go b/internal/model/oidc.go index e55c310d9..f7f244658 100644 --- a/internal/model/oidc.go +++ b/internal/model/oidc.go @@ -10,19 +10,19 @@ import (  	"strings"  	"time" +	oauthelia2 "authelia.com/provider/oauth2"  	"github.com/google/uuid" -	"github.com/ory/fosite"  	"github.com/authelia/authelia/v4/internal/utils"  )  // NewOAuth2ConsentSession creates a new OAuth2ConsentSession. -func NewOAuth2ConsentSession(subject uuid.UUID, r fosite.Requester) (consent *OAuth2ConsentSession, err error) { +func NewOAuth2ConsentSession(subject uuid.UUID, r oauthelia2.Requester) (consent *OAuth2ConsentSession, err error) {  	return NewOAuth2ConsentSessionWithForm(subject, r, r.GetRequestForm())  } -// NewOAuth2ConsentSessionWithForm creates a new OAuth2ConsentSession with a custom form parameter.. -func NewOAuth2ConsentSessionWithForm(subject uuid.UUID, r fosite.Requester, form url.Values) (consent *OAuth2ConsentSession, err error) { +// NewOAuth2ConsentSessionWithForm creates a new OAuth2ConsentSession with a custom form parameter. +func NewOAuth2ConsentSessionWithForm(subject uuid.UUID, r oauthelia2.Requester, form url.Values) (consent *OAuth2ConsentSession, err error) {  	consent = &OAuth2ConsentSession{  		ClientID:          r.GetClient().GetID(),  		Subject:           NullUUID(subject), @@ -49,10 +49,10 @@ func NewOAuth2BlacklistedJTI(jti string, exp time.Time) (jtiBlacklist OAuth2Blac  	}  } -// NewOAuth2SessionFromRequest creates a new OAuth2Session from a signature and fosite.Requester. -func NewOAuth2SessionFromRequest(signature string, r fosite.Requester) (session *OAuth2Session, err error) { +// NewOAuth2SessionFromRequest creates a new OAuth2Session from a signature and oauthelia2.Requester. +func NewOAuth2SessionFromRequest(signature string, r oauthelia2.Requester) (session *OAuth2Session, err error) {  	if r == nil { -		return nil, fmt.Errorf("failed to create new *model.OAuth2Session: the fosite.Requester was nil") +		return nil, fmt.Errorf("failed to create new *model.OAuth2Session: the oauthelia2.Requester was nil")  	}  	var ( @@ -78,11 +78,11 @@ func NewOAuth2SessionFromRequest(signature string, r fosite.Requester) (session  	requested, granted := r.GetRequestedScopes(), r.GetGrantedScopes()  	if requested == nil { -		requested = fosite.Arguments{} +		requested = oauthelia2.Arguments{}  	}  	if granted == nil { -		granted = fosite.Arguments{} +		granted = oauthelia2.Arguments{}  	}  	return &OAuth2Session{ @@ -104,11 +104,11 @@ func NewOAuth2SessionFromRequest(signature string, r fosite.Requester) (session  }  // NewOAuth2PARContext creates a new Pushed Authorization Request Context as a OAuth2PARContext. -func NewOAuth2PARContext(contextID string, r fosite.AuthorizeRequester) (context *OAuth2PARContext, err error) { +func NewOAuth2PARContext(contextID string, r oauthelia2.AuthorizeRequester) (context *OAuth2PARContext, err error) {  	var (  		s       OpenIDSession  		ok      bool -		req     *fosite.AuthorizeRequest +		req     *oauthelia2.AuthorizeRequest  		session []byte  	) @@ -122,7 +122,7 @@ func NewOAuth2PARContext(contextID string, r fosite.AuthorizeRequester) (context  	var handled StringSlicePipeDelimited -	if req, ok = r.(*fosite.AuthorizeRequest); ok { +	if req, ok = r.(*oauthelia2.AuthorizeRequest); ok {  		handled = StringSlicePipeDelimited(req.HandledResponseTypes)  	} @@ -284,8 +284,8 @@ func (s *OAuth2Session) SetSubject(subject string) {  	s.Subject = sql.NullString{String: subject, Valid: len(subject) > 0}  } -// ToRequest converts an OAuth2Session into a fosite.Request given a fosite.Session and fosite.Storage. -func (s *OAuth2Session) ToRequest(ctx context.Context, session fosite.Session, store fosite.Storage) (request *fosite.Request, err error) { +// ToRequest converts an OAuth2Session into a oauthelia2.Request given a oauthelia2.Session and oauthelia2.Storage. +func (s *OAuth2Session) ToRequest(ctx context.Context, session oauthelia2.Session, store oauthelia2.Storage) (request *oauthelia2.Request, err error) {  	sessionData := s.Session  	if session != nil { @@ -304,14 +304,14 @@ func (s *OAuth2Session) ToRequest(ctx context.Context, session fosite.Session, s  		return nil, fmt.Errorf("error occurred while mapping OAuth 2.0 Session back to a Request while trying to parse the original form: %w", err)  	} -	return &fosite.Request{ +	return &oauthelia2.Request{  		ID:                s.RequestID,  		RequestedAt:       s.RequestedAt,  		Client:            client, -		RequestedScope:    fosite.Arguments(s.RequestedScopes), -		GrantedScope:      fosite.Arguments(s.GrantedScopes), -		RequestedAudience: fosite.Arguments(s.RequestedAudience), -		GrantedAudience:   fosite.Arguments(s.GrantedAudience), +		RequestedScope:    oauthelia2.Arguments(s.RequestedScopes), +		GrantedScope:      oauthelia2.Arguments(s.GrantedScopes), +		RequestedAudience: oauthelia2.Arguments(s.RequestedAudience), +		GrantedAudience:   oauthelia2.Arguments(s.GrantedAudience),  		Form:              values,  		Session:           session,  	}, nil @@ -334,7 +334,7 @@ type OAuth2PARContext struct {  	Session              []byte                   `db:"session_data"`  } -func (par *OAuth2PARContext) ToAuthorizeRequest(ctx context.Context, session fosite.Session, store fosite.Storage) (request *fosite.AuthorizeRequest, err error) { +func (par *OAuth2PARContext) ToAuthorizeRequest(ctx context.Context, session oauthelia2.Session, store oauthelia2.Storage) (request *oauthelia2.AuthorizeRequest, err error) {  	if session != nil {  		if err = json.Unmarshal(par.Session, session); err != nil {  			return nil, fmt.Errorf("error occurred while mapping PAR context back to an Authorize Request while trying to unmarshal the JSON session data: %w", err) @@ -342,7 +342,7 @@ func (par *OAuth2PARContext) ToAuthorizeRequest(ctx context.Context, session fos  	}  	var ( -		client fosite.Client +		client oauthelia2.Client  		form   url.Values  	) @@ -354,14 +354,14 @@ func (par *OAuth2PARContext) ToAuthorizeRequest(ctx context.Context, session fos  		return nil, fmt.Errorf("error occurred while mapping PAR context back to an Authorize Request while trying to parse the original form: %w", err)  	} -	request = fosite.NewAuthorizeRequest() +	request = oauthelia2.NewAuthorizeRequest() -	request.Request = fosite.Request{ +	request.Request = oauthelia2.Request{  		ID:                par.RequestID,  		RequestedAt:       par.RequestedAt,  		Client:            client, -		RequestedScope:    fosite.Arguments(par.Scopes), -		RequestedAudience: fosite.Arguments(par.Audience), +		RequestedScope:    oauthelia2.Arguments(par.Scopes), +		RequestedAudience: oauthelia2.Arguments(par.Audience),  		Form:              form,  		Session:           session,  	} @@ -375,19 +375,19 @@ func (par *OAuth2PARContext) ToAuthorizeRequest(ctx context.Context, session fos  	}  	if form.Has("response_type") { -		request.ResponseTypes = fosite.RemoveEmpty(strings.Split(form.Get("response_type"), " ")) +		request.ResponseTypes = oauthelia2.RemoveEmpty(strings.Split(form.Get("response_type"), " "))  	}  	if par.ResponseMode != "" { -		request.ResponseMode = fosite.ResponseModeType(par.ResponseMode) +		request.ResponseMode = oauthelia2.ResponseModeType(par.ResponseMode)  	}  	if par.DefaultResponseMode != "" { -		request.DefaultResponseMode = fosite.ResponseModeType(par.DefaultResponseMode) +		request.DefaultResponseMode = oauthelia2.ResponseModeType(par.DefaultResponseMode)  	}  	if len(par.HandledResponseTypes) != 0 { -		request.HandledResponseTypes = fosite.Arguments(par.HandledResponseTypes) +		request.HandledResponseTypes = oauthelia2.Arguments(par.HandledResponseTypes)  	}  	return request, nil @@ -395,7 +395,7 @@ func (par *OAuth2PARContext) ToAuthorizeRequest(ctx context.Context, session fos  // OpenIDSession represents the types available for an oidc.Session that are required in the models package.  type OpenIDSession interface { -	fosite.Session +	oauthelia2.Session  	GetChallengeID() uuid.NullUUID  } diff --git a/internal/model/oidc_test.go b/internal/model/oidc_test.go index 32d253509..fd7951f57 100644 --- a/internal/model/oidc_test.go +++ b/internal/model/oidc_test.go @@ -9,9 +9,9 @@ import (  	"testing"  	"time" +	oauthelia2 "authelia.com/provider/oauth2" +	"authelia.com/provider/oauth2/handler/openid"  	"github.com/google/uuid" -	"github.com/ory/fosite" -	"github.com/ory/fosite/handler/openid"  	"github.com/stretchr/testify/assert"  	"github.com/stretchr/testify/require"  	"go.uber.org/mock/gomock" @@ -35,21 +35,21 @@ func TestNewOAuth2SessionFromRequest(t *testing.T) {  	testCaases := []struct {  		name      string  		signature string -		have      fosite.Requester +		have      oauthelia2.Requester  		expected  *model.OAuth2Session  		err       string  	}{  		{  			"ShouldNewUpStandard",  			"abc", -			&fosite.Request{ +			&oauthelia2.Request{  				ID: "example", -				Client: &fosite.DefaultClient{ +				Client: &oauthelia2.DefaultClient{  					ID: "client_id",  				},  				Session:        session, -				RequestedScope: fosite.Arguments{oidc.ScopeOpenID}, -				GrantedScope:   fosite.Arguments{oidc.ScopeOpenID}, +				RequestedScope: oauthelia2.Arguments{oidc.ScopeOpenID}, +				GrantedScope:   oauthelia2.Arguments{oidc.ScopeOpenID},  			},  			&model.OAuth2Session{  				ChallengeID:     challenge, @@ -67,9 +67,9 @@ func TestNewOAuth2SessionFromRequest(t *testing.T) {  		{  			"ShouldNewUpWithoutScopes",  			"abc", -			&fosite.Request{ +			&oauthelia2.Request{  				ID: "example", -				Client: &fosite.DefaultClient{ +				Client: &oauthelia2.DefaultClient{  					ID: "client_id",  				},  				Session:        session, @@ -92,9 +92,9 @@ func TestNewOAuth2SessionFromRequest(t *testing.T) {  		{  			"ShouldRaiseErrorOnInvalidSessionType",  			"abc", -			&fosite.Request{ +			&oauthelia2.Request{  				ID: "example", -				Client: &fosite.DefaultClient{ +				Client: &oauthelia2.DefaultClient{  					ID: "client_id",  				},  				Session:        &openid.DefaultSession{}, @@ -109,7 +109,7 @@ func TestNewOAuth2SessionFromRequest(t *testing.T) {  			"abc",  			nil,  			nil, -			"failed to create new *model.OAuth2Session: the fosite.Requester was nil", +			"failed to create new *model.OAuth2Session: the oauthelia2.Requester was nil",  		},  	} @@ -169,33 +169,33 @@ func TestOAuth2PARContext_ToAuthorizeRequest(t *testing.T) {  	testCases := []struct {  		name     string -		setup    func(mock *mocks.MockFositeStorage) +		setup    func(mock *mocks.MockOAuth2Storage)  		have     *model.OAuth2PARContext -		expected *fosite.AuthorizeRequest +		expected *oauthelia2.AuthorizeRequest  		err      string  	}{  		{  			"ShouldErrorInvalidJSONData",  			nil,  			&model.OAuth2PARContext{}, -			&fosite.AuthorizeRequest{}, +			&oauthelia2.AuthorizeRequest{},  			"error occurred while mapping PAR context back to an Authorize Request while trying to unmarshal the JSON session data: unexpected end of JSON input",  		},  		{  			"ShouldErrorInvalidClient", -			func(mock *mocks.MockFositeStorage) { -				mock.EXPECT().GetClient(context.TODO(), parclientid).Return(nil, fosite.ErrNotFound) +			func(mock *mocks.MockOAuth2Storage) { +				mock.EXPECT().GetClient(context.TODO(), parclientid).Return(nil, oauthelia2.ErrNotFound)  			},  			&model.OAuth2PARContext{  				ClientID: parclientid,  				Session:  []byte("{}"),  			}, -			&fosite.AuthorizeRequest{}, +			&oauthelia2.AuthorizeRequest{},  			"error occurred while mapping PAR context back to an Authorize Request while trying to lookup the registered client: not_found",  		},  		{  			"ShouldErrorOnBadForm", -			func(mock *mocks.MockFositeStorage) { +			func(mock *mocks.MockOAuth2Storage) {  				mock.EXPECT().GetClient(context.TODO(), parclientid).Return(&oidc.RegisteredClient{ID: parclientid}, nil)  			},  			&model.OAuth2PARContext{ @@ -211,7 +211,7 @@ func TestOAuth2PARContext_ToAuthorizeRequest(t *testing.T) {  		},  		{  			"ShouldErrorOnBadFormRedirectURI", -			func(mock *mocks.MockFositeStorage) { +			func(mock *mocks.MockOAuth2Storage) {  				mock.EXPECT().GetClient(context.TODO(), parclientid).Return(&oidc.RegisteredClient{ID: parclientid}, nil)  			},  			&model.OAuth2PARContext{ @@ -227,7 +227,7 @@ func TestOAuth2PARContext_ToAuthorizeRequest(t *testing.T) {  		},  		{  			"ShouldRestoreAuthorizeRequest", -			func(mock *mocks.MockFositeStorage) { +			func(mock *mocks.MockOAuth2Storage) {  				mock.EXPECT().GetClient(context.TODO(), parclientid).Return(&oidc.RegisteredClient{ID: parclientid}, nil)  			},  			&model.OAuth2PARContext{ @@ -248,18 +248,18 @@ func TestOAuth2PARContext_ToAuthorizeRequest(t *testing.T) {  				DefaultResponseMode:  oidc.ResponseModeQuery,  				HandledResponseTypes: model.StringSlicePipeDelimited{oidc.ResponseTypeAuthorizationCodeFlow},  			}, -			&fosite.AuthorizeRequest{ +			&oauthelia2.AuthorizeRequest{  				RedirectURI:          MustParseRequestURI("https://example.com"),  				State:                "abc123", -				ResponseMode:         fosite.ResponseModeQuery, -				DefaultResponseMode:  fosite.ResponseModeQuery, -				ResponseTypes:        fosite.Arguments{oidc.ResponseTypeAuthorizationCodeFlow}, -				HandledResponseTypes: fosite.Arguments{oidc.ResponseTypeAuthorizationCodeFlow}, -				Request: fosite.Request{ +				ResponseMode:         oauthelia2.ResponseModeQuery, +				DefaultResponseMode:  oauthelia2.ResponseModeQuery, +				ResponseTypes:        oauthelia2.Arguments{oidc.ResponseTypeAuthorizationCodeFlow}, +				HandledResponseTypes: oauthelia2.Arguments{oidc.ResponseTypeAuthorizationCodeFlow}, +				Request: oauthelia2.Request{  					ID:                requestid,  					Client:            &oidc.RegisteredClient{ID: parclientid}, -					RequestedScope:    fosite.Arguments{oidc.ScopeOpenID, oidc.ScopeOffline}, -					RequestedAudience: fosite.Arguments{parclientid}, +					RequestedScope:    oauthelia2.Arguments{oidc.ScopeOpenID, oidc.ScopeOffline}, +					RequestedAudience: oauthelia2.Arguments{parclientid},  					RequestedAt:       time.Unix(10000000, 0),  					Session:           oidc.NewSession(),  					Form: url.Values{ @@ -279,7 +279,7 @@ func TestOAuth2PARContext_ToAuthorizeRequest(t *testing.T) {  			defer ctrl.Finish() -			mock := mocks.NewMockFositeStorage(ctrl) +			mock := mocks.NewMockOAuth2Storage(ctrl)  			if tc.setup != nil {  				tc.setup(mock) @@ -301,25 +301,25 @@ func TestOAuth2PARContext_ToAuthorizeRequest(t *testing.T) {  func TestNewOAuth2PARContext(t *testing.T) {  	testCases := []struct {  		name     string -		have     fosite.AuthorizeRequester +		have     oauthelia2.AuthorizeRequester  		id       string  		expected *model.OAuth2PARContext  		err      string  	}{  		{  			"ShouldCreatePARContext", -			&fosite.AuthorizeRequest{ -				HandledResponseTypes: fosite.Arguments{oidc.ResponseTypeHybridFlowIDToken}, -				ResponseMode:         fosite.ResponseModeQuery, -				DefaultResponseMode:  fosite.ResponseModeFragment, -				Request: fosite.Request{ +			&oauthelia2.AuthorizeRequest{ +				HandledResponseTypes: oauthelia2.Arguments{oidc.ResponseTypeHybridFlowIDToken}, +				ResponseMode:         oauthelia2.ResponseModeQuery, +				DefaultResponseMode:  oauthelia2.ResponseModeFragment, +				Request: oauthelia2.Request{  					ID:                "a-id",  					RequestedAt:       time.Time{},  					Client:            &oidc.RegisteredClient{ID: "a-client"}, -					RequestedScope:    fosite.Arguments{oidc.ScopeOpenID}, +					RequestedScope:    oauthelia2.Arguments{oidc.ScopeOpenID},  					Form:              url.Values{oidc.FormParameterRedirectURI: []string{"https://example.com"}},  					Session:           &oidc.Session{}, -					RequestedAudience: fosite.Arguments{"a-client"}, +					RequestedAudience: oauthelia2.Arguments{"a-client"},  				},  			},  			"123", @@ -340,18 +340,18 @@ func TestNewOAuth2PARContext(t *testing.T) {  		},  		{  			"ShouldFailCreateWrongSessionType", -			&fosite.AuthorizeRequest{ -				HandledResponseTypes: fosite.Arguments{oidc.ResponseTypeHybridFlowIDToken}, -				ResponseMode:         fosite.ResponseModeQuery, -				DefaultResponseMode:  fosite.ResponseModeFragment, -				Request: fosite.Request{ +			&oauthelia2.AuthorizeRequest{ +				HandledResponseTypes: oauthelia2.Arguments{oidc.ResponseTypeHybridFlowIDToken}, +				ResponseMode:         oauthelia2.ResponseModeQuery, +				DefaultResponseMode:  oauthelia2.ResponseModeFragment, +				Request: oauthelia2.Request{  					ID:                "a-id",  					RequestedAt:       time.Time{},  					Client:            &oidc.RegisteredClient{ID: "a-client"}, -					RequestedScope:    fosite.Arguments{oidc.ScopeOpenID}, +					RequestedScope:    oauthelia2.Arguments{oidc.ScopeOpenID},  					Form:              url.Values{oidc.FormParameterRedirectURI: []string{"https://example.com"}},  					Session:           &openid.DefaultSession{}, -					RequestedAudience: fosite.Arguments{"a-client"}, +					RequestedAudience: oauthelia2.Arguments{"a-client"},  				},  			},  			"123", @@ -383,33 +383,33 @@ func TestOAuth2Session_ToRequest(t *testing.T) {  	testCases := []struct {  		name     string -		setup    func(mock *mocks.MockFositeStorage) +		setup    func(mock *mocks.MockOAuth2Storage)  		have     *model.OAuth2Session -		expected *fosite.Request +		expected *oauthelia2.Request  		err      string  	}{  		{  			"ShouldErrorInvalidJSONData",  			nil,  			&model.OAuth2Session{}, -			&fosite.Request{}, +			&oauthelia2.Request{},  			"error occurred while mapping OAuth 2.0 Session back to a Request while trying to unmarshal the JSON session data: unexpected end of JSON input",  		},  		{  			"ShouldErrorInvalidClient", -			func(mock *mocks.MockFositeStorage) { -				mock.EXPECT().GetClient(context.TODO(), parclientid).Return(nil, fosite.ErrNotFound) +			func(mock *mocks.MockOAuth2Storage) { +				mock.EXPECT().GetClient(context.TODO(), parclientid).Return(nil, oauthelia2.ErrNotFound)  			},  			&model.OAuth2Session{  				ClientID: parclientid,  				Session:  []byte("{}"),  			}, -			&fosite.Request{}, +			&oauthelia2.Request{},  			"error occurred while mapping OAuth 2.0 Session back to a Request while trying to lookup the registered client: not_found",  		},  		{  			"ShouldErrorOnBadForm", -			func(mock *mocks.MockFositeStorage) { +			func(mock *mocks.MockOAuth2Storage) {  				mock.EXPECT().GetClient(context.TODO(), parclientid).Return(&oidc.RegisteredClient{ID: parclientid}, nil)  			},  			&model.OAuth2Session{ @@ -425,7 +425,7 @@ func TestOAuth2Session_ToRequest(t *testing.T) {  		},  		{  			"ShouldRestoreRequest", -			func(mock *mocks.MockFositeStorage) { +			func(mock *mocks.MockOAuth2Storage) {  				mock.EXPECT().GetClient(context.TODO(), parclientid).Return(&oidc.RegisteredClient{ID: parclientid}, nil)  			},  			&model.OAuth2Session{ @@ -443,11 +443,11 @@ func TestOAuth2Session_ToRequest(t *testing.T) {  					oidc.FormParameterResponseType: []string{oidc.ResponseTypeAuthorizationCodeFlow},  				}.Encode(),  			}, -			&fosite.Request{ +			&oauthelia2.Request{  				ID:                requestid,  				Client:            &oidc.RegisteredClient{ID: parclientid}, -				RequestedScope:    fosite.Arguments{oidc.ScopeOpenID, oidc.ScopeOffline}, -				RequestedAudience: fosite.Arguments{parclientid}, +				RequestedScope:    oauthelia2.Arguments{oidc.ScopeOpenID, oidc.ScopeOffline}, +				RequestedAudience: oauthelia2.Arguments{parclientid},  				RequestedAt:       time.Unix(10000000, 0),  				Session:           oidc.NewSession(),  				Form: url.Values{ @@ -466,7 +466,7 @@ func TestOAuth2Session_ToRequest(t *testing.T) {  			defer ctrl.Finish() -			mock := mocks.NewMockFositeStorage(ctrl) +			mock := mocks.NewMockOAuth2Storage(ctrl)  			if tc.setup != nil {  				tc.setup(mock) @@ -595,7 +595,7 @@ func TestMisc(t *testing.T) {  	sub := uuid.MustParse("b9423f3a-65da-4ea8-8f6b-1dafb141f3a8") -	session, err := model.NewOAuth2ConsentSession(sub, &fosite.Request{Client: &oidc.RegisteredClient{ID: "abc"}}) +	session, err := model.NewOAuth2ConsentSession(sub, &oauthelia2.Request{Client: &oidc.RegisteredClient{ID: "abc"}})  	assert.NoError(t, err)  	assert.NotNil(t, session) diff --git a/internal/oidc/client.go b/internal/oidc/client.go index 973111c78..9541f422f 100644 --- a/internal/oidc/client.go +++ b/internal/oidc/client.go @@ -4,9 +4,8 @@ import (  	"context"  	"time" -	"github.com/go-crypt/crypt/algorithm" -	"github.com/go-jose/go-jose/v3" -	"github.com/ory/fosite" +	oauthelia2 "authelia.com/provider/oauth2" +	"github.com/go-jose/go-jose/v4"  	"github.com/ory/x/errorsx"  	"github.com/authelia/authelia/v4/internal/authentication" @@ -20,22 +19,26 @@ func NewClient(config schema.IdentityProvidersOpenIDConnectClient, c *schema.Ide  	registered := &RegisteredClient{  		ID:                  config.ID,  		Name:                config.Name, -		Secret:              config.Secret,  		SectorIdentifierURI: config.SectorIdentifierURI,  		Public:              config.Public, -		RequirePKCE:                config.RequirePKCE || config.PKCEChallengeMethod != "", -		RequirePKCEChallengeMethod: config.PKCEChallengeMethod != "", -		PKCEChallengeMethod:        config.PKCEChallengeMethod, -  		Audience:      config.Audience,  		Scopes:        config.Scopes,  		RedirectURIs:  config.RedirectURIs,  		GrantTypes:    config.GrantTypes,  		ResponseTypes: config.ResponseTypes, -		ResponseModes: []fosite.ResponseModeType{}, +		ResponseModes: []oauthelia2.ResponseModeType{}, -		RequirePushedAuthorizationRequests: config.RequirePushedAuthorizationRequests, +		RequirePKCE:                config.RequirePKCE || config.PKCEChallengeMethod != "", +		RequirePKCEChallengeMethod: config.PKCEChallengeMethod != "", +		PKCEChallengeMethod:        config.PKCEChallengeMethod, + +		RequirePushedAuthorizationRequests:      config.RequirePushedAuthorizationRequests, +		ClientCredentialsFlowAllowImplicitScope: false, + +		AuthorizationPolicy:   NewClientAuthorizationPolicy(config.AuthorizationPolicy, c), +		ConsentPolicy:         NewClientConsentPolicy(config.ConsentMode, config.ConsentPreConfiguredDuration), +		RequestedAudienceMode: NewClientRequestedAudienceMode(config.RequestedAudienceMode),  		AuthorizationSignedResponseAlg:   config.AuthorizationSignedResponseAlg,  		AuthorizationSignedResponseKeyID: config.AuthorizationSignedResponseKeyID, @@ -47,18 +50,18 @@ func NewClient(config schema.IdentityProvidersOpenIDConnectClient, c *schema.Ide  		UserinfoSignedResponseKeyID:      config.UserinfoSignedResponseKeyID,  		IntrospectionSignedResponseAlg:   config.IntrospectionSignedResponseAlg,  		IntrospectionSignedResponseKeyID: config.IntrospectionSignedResponseKeyID, - -		AuthorizationPolicy:         NewClientAuthorizationPolicy(config.AuthorizationPolicy, c), -		ConsentPolicy:               NewClientConsentPolicy(config.ConsentMode, config.ConsentPreConfiguredDuration), -		RequestedAudienceMode:       NewClientRequestedAudienceMode(config.RequestedAudienceMode), -		TokenEndpointAuthMethod:     config.TokenEndpointAuthMethod, -		TokenEndpointAuthSigningAlg: config.TokenEndpointAuthSigningAlg, -		RequestObjectSigningAlg:     config.RequestObjectSigningAlg, +		RequestObjectSigningAlg:          config.RequestObjectSigningAlg, +		TokenEndpointAuthSigningAlg:      config.TokenEndpointAuthSigningAlg, +		TokenEndpointAuthMethod:          config.TokenEndpointAuthMethod,  		JSONWebKeysURI: config.JSONWebKeysURI,  		JSONWebKeys:    NewPublicJSONWebKeySetFromSchemaJWK(config.JSONWebKeys),  	} +	if config.Secret != nil && config.Secret.Digest != nil { +		registered.ClientSecret = &ClientSecretDigest{PasswordDigest: config.Secret} +	} +  	if len(config.Lifespan) != 0 {  		if lifespans, ok := c.Lifespans.Custom[config.Lifespan]; ok {  			registered.Lifespans = lifespans @@ -66,7 +69,7 @@ func NewClient(config schema.IdentityProvidersOpenIDConnectClient, c *schema.Ide  	}  	for _, mode := range config.ResponseModes { -		registered.ResponseModes = append(registered.ResponseModes, fosite.ResponseModeType(mode)) +		registered.ResponseModes = append(registered.ResponseModes, oauthelia2.ResponseModeType(mode))  	}  	return registered @@ -86,27 +89,29 @@ func (c *RegisteredClient) GetName() (name string) {  	return c.Name  } -// GetSecret returns the Secret. -func (c *RegisteredClient) GetSecret() algorithm.Digest { -	return c.Secret +// GetClientSecret returns the oauth2.ClientSecret. +func (c *RegisteredClient) GetClientSecret() (secret oauthelia2.ClientSecret) { +	return c.ClientSecret  } -// GetSectorIdentifier returns the SectorIdentifier for this client. -func (c *RegisteredClient) GetSectorIdentifier() string { -	if c.SectorIdentifierURI == nil { -		return "" +// GetRotatedClientSecrets returns the rotated oauth2.ClientSecret values. +func (c *RegisteredClient) GetRotatedClientSecrets() (secrets []oauthelia2.ClientSecret) { +	secrets = make([]oauthelia2.ClientSecret, len(c.RotatedClientSecrets)) + +	for i, secret := range c.RotatedClientSecrets { +		secrets[i] = secret  	} -	return c.SectorIdentifierURI.String() +	return secrets  } -// GetHashedSecret returns the Secret. -func (c *RegisteredClient) GetHashedSecret() (secret []byte) { -	if c.Secret == nil { -		return []byte(nil) +// GetSectorIdentifierURI returns the SectorIdentifier for this client. +func (c *RegisteredClient) GetSectorIdentifierURI() (sector string) { +	if c.SectorIdentifierURI == nil { +		return ""  	} -	return []byte(c.Secret.Encode()) +	return c.SectorIdentifierURI.String()  }  // GetRedirectURIs returns the RedirectURIs. @@ -115,37 +120,37 @@ func (c *RegisteredClient) GetRedirectURIs() (redirectURIs []string) {  }  // GetGrantTypes returns the GrantTypes. -func (c *RegisteredClient) GetGrantTypes() fosite.Arguments { +func (c *RegisteredClient) GetGrantTypes() (types oauthelia2.Arguments) {  	if len(c.GrantTypes) == 0 { -		return fosite.Arguments{"authorization_code"} +		return oauthelia2.Arguments{"authorization_code"}  	}  	return c.GrantTypes  }  // GetResponseTypes returns the ResponseTypes. -func (c *RegisteredClient) GetResponseTypes() fosite.Arguments { +func (c *RegisteredClient) GetResponseTypes() (types oauthelia2.Arguments) {  	if len(c.ResponseTypes) == 0 { -		return fosite.Arguments{"code"} +		return oauthelia2.Arguments{"code"}  	}  	return c.ResponseTypes  }  // GetScopes returns the Scopes. -func (c *RegisteredClient) GetScopes() fosite.Arguments { +func (c *RegisteredClient) GetScopes() (scopes oauthelia2.Arguments) {  	return c.Scopes  }  // GetAudience returns the Audience. -func (c *RegisteredClient) GetAudience() fosite.Arguments { +func (c *RegisteredClient) GetAudience() (audience oauthelia2.Arguments) {  	return c.Audience  }  // GetResponseModes returns the valid response modes for this client.  // -// Implements the fosite.ResponseModeClient. -func (c *RegisteredClient) GetResponseModes() []fosite.ResponseModeType { +// Implements the oauthelia2.ResponseModeClient. +func (c *RegisteredClient) GetResponseModes() (modes []oauthelia2.ResponseModeType) {  	return c.ResponseModes  } @@ -163,6 +168,14 @@ func (c *RegisteredClient) GetAuthorizationSignedResponseKeyID() (kid string) {  	return c.AuthorizationSignedResponseKeyID  } +func (c *RegisteredClient) GetAuthorizationEncryptedResponseAlg() (alg string) { +	return c.AuthorizationEncryptedResponseAlg +} + +func (c *RegisteredClient) GetAuthorizationEncryptedResponseEncryptionAlg() (alg string) { +	return "" +} +  // GetIDTokenSignedResponseAlg returns the IDTokenSignedResponseAlg.  func (c *RegisteredClient) GetIDTokenSignedResponseAlg() (alg string) {  	if c.IDTokenSignedResponseAlg == "" { @@ -173,7 +186,7 @@ func (c *RegisteredClient) GetIDTokenSignedResponseAlg() (alg string) {  }  // GetIDTokenSignedResponseKeyID returns the IDTokenSignedResponseKeyID. -func (c *RegisteredClient) GetIDTokenSignedResponseKeyID() (alg string) { +func (c *RegisteredClient) GetIDTokenSignedResponseKeyID() (kid string) {  	return c.IDTokenSignedResponseKeyID  } @@ -187,18 +200,12 @@ func (c *RegisteredClient) GetAccessTokenSignedResponseAlg() (alg string) {  }  // GetAccessTokenSignedResponseKeyID returns the AccessTokenSignedResponseKeyID. -func (c *RegisteredClient) GetAccessTokenSignedResponseKeyID() (alg string) { +func (c *RegisteredClient) GetAccessTokenSignedResponseKeyID() (kid string) {  	return c.AccessTokenSignedResponseKeyID  } -// GetJWTProfileOAuthAccessTokensEnabled returns true if this client is configured to return the -// RFC9068 JWT Profile for OAuth 2.0 Access Tokens. -func (c *RegisteredClient) GetJWTProfileOAuthAccessTokensEnabled() bool { -	return c.GetAccessTokenSignedResponseAlg() != SigningAlgNone || len(c.GetAccessTokenSignedResponseKeyID()) > 0 -} -  // GetUserinfoSignedResponseAlg returns the UserinfoSignedResponseAlg. -func (c *RegisteredClient) GetUserinfoSignedResponseAlg() string { +func (c *RegisteredClient) GetUserinfoSignedResponseAlg() (alg string) {  	if c.UserinfoSignedResponseAlg == "" {  		c.UserinfoSignedResponseAlg = SigningAlgNone  	} @@ -225,40 +232,63 @@ func (c *RegisteredClient) GetIntrospectionSignedResponseKeyID() (alg string) {  	return c.IntrospectionSignedResponseKeyID  } -// GetRequirePushedAuthorizationRequests returns RequirePushedAuthorizationRequests. -func (c *RegisteredClient) GetRequirePushedAuthorizationRequests() bool { +// GetTokenEndpointAuthSigningAlg returns the JWS [JWS] alg algorithm [JWA] that MUST be used for signing the JWT +// [JWT] used to authenticate the Client at the Token Endpoint for the private_key_jwt and client_secret_jwt +// authentication methods. +func (c *RegisteredClient) GetTokenEndpointAuthSigningAlg() (alg string) { +	if c.TokenEndpointAuthSigningAlg == "" { +		c.TokenEndpointAuthSigningAlg = SigningAlgRSAUsingSHA256 +	} + +	return c.TokenEndpointAuthSigningAlg +} + +// GetTokenEndpointAuthMethod returns the requested Client Authentication Method for the Token Endpoint. The options are +// client_secret_post, client_secret_basic, client_secret_jwt, private_key_jwt, and none. +func (c *RegisteredClient) GetTokenEndpointAuthMethod() (method string) { +	if c.TokenEndpointAuthMethod == "" { +		if c.Public { +			c.TokenEndpointAuthMethod = ClientAuthMethodNone +		} else { +			c.TokenEndpointAuthMethod = ClientAuthMethodClientSecretBasic +		} +	} + +	return c.TokenEndpointAuthMethod +} + +// GetEnableJWTProfileOAuthAccessTokens returns true if this client is configured to return the +// RFC9068 JWT Profile for OAuth 2.0 Access Tokens. +func (c *RegisteredClient) GetEnableJWTProfileOAuthAccessTokens() (enable bool) { +	return c.GetAccessTokenSignedResponseAlg() != SigningAlgNone || len(c.GetAccessTokenSignedResponseKeyID()) > 0 +} + +// GetRequirePushedAuthorizationRequests should return true if this client MUST use a Pushed Authorization Request. +func (c *RegisteredClient) GetRequirePushedAuthorizationRequests() (require bool) {  	return c.RequirePushedAuthorizationRequests  } -// GetPKCEEnforcement returns RequirePKCE. -func (c *RegisteredClient) GetPKCEEnforcement() bool { +// GetPushedAuthorizeContextLifespan should return a custom lifespan or a duration of 0 seconds to utilize the +// global lifespan. +func (c *RegisteredClient) GetPushedAuthorizeContextLifespan() (lifespan time.Duration) { +	return lifespan +} + +// GetEnforcePKCE returns RequirePKCE. +func (c *RegisteredClient) GetEnforcePKCE() (enforce bool) {  	return c.RequirePKCE  } -// GetPKCEChallengeMethodEnforcement returns RequirePKCEChallengeMethod. -func (c *RegisteredClient) GetPKCEChallengeMethodEnforcement() bool { +// GetEnforcePKCEChallengeMethod returns RequirePKCEChallengeMethod. +func (c *RegisteredClient) GetEnforcePKCEChallengeMethod() (enforce bool) {  	return c.RequirePKCEChallengeMethod  }  // GetPKCEChallengeMethod returns PKCEChallengeMethod. -func (c *RegisteredClient) GetPKCEChallengeMethod() string { +func (c *RegisteredClient) GetPKCEChallengeMethod() (method string) {  	return c.PKCEChallengeMethod  } -// ApplyRequestedAudiencePolicy applies the requested audience policy to a fosite.Requester. -func (c *RegisteredClient) ApplyRequestedAudiencePolicy(requester fosite.Requester) { -	switch c.RequestedAudienceMode { -	case ClientRequestedAudienceModeExplicit: -		return -	case ClientRequestedAudienceModeImplicit: -		if requester.GetRequestForm().Has(FormParameterAudience) || len(requester.GetRequestedAudience()) != 0 { -			return -		} - -		requester.SetRequestedAudience(c.Audience) -	} -} -  // GetConsentResponseBody returns the proper consent response body for this session.OIDCWorkflowSession.  func (c *RegisteredClient) GetConsentResponseBody(consent *model.OAuth2ConsentSession) ConsentGetResponseBody {  	body := ConsentGetResponseBody{ @@ -276,12 +306,12 @@ func (c *RegisteredClient) GetConsentResponseBody(consent *model.OAuth2ConsentSe  }  // GetConsentPolicy returns Consent. -func (c *RegisteredClient) GetConsentPolicy() ClientConsentPolicy { +func (c *RegisteredClient) GetConsentPolicy() (policy ClientConsentPolicy) {  	return c.ConsentPolicy  }  // IsAuthenticationLevelSufficient returns if the provided authentication.Level is sufficient for the client of the AutheliaClient. -func (c *RegisteredClient) IsAuthenticationLevelSufficient(level authentication.Level, subject authorization.Subject) bool { +func (c *RegisteredClient) IsAuthenticationLevelSufficient(level authentication.Level, subject authorization.Subject) (sufficient bool) {  	if level == authentication.NotAuthenticated {  		return false  	} @@ -290,63 +320,24 @@ func (c *RegisteredClient) IsAuthenticationLevelSufficient(level authentication.  }  // GetAuthorizationPolicyRequiredLevel returns the required authorization.Level given an authorization.Subject. -func (c *RegisteredClient) GetAuthorizationPolicyRequiredLevel(subject authorization.Subject) authorization.Level { +func (c *RegisteredClient) GetAuthorizationPolicyRequiredLevel(subject authorization.Subject) (level authorization.Level) {  	return c.AuthorizationPolicy.GetRequiredLevel(subject)  }  // GetAuthorizationPolicy returns the ClientAuthorizationPolicy from the Policy. -func (c *RegisteredClient) GetAuthorizationPolicy() ClientAuthorizationPolicy { +func (c *RegisteredClient) GetAuthorizationPolicy() (policy ClientAuthorizationPolicy) {  	return c.AuthorizationPolicy  }  // IsPublic returns the value of the Public property. -func (c *RegisteredClient) IsPublic() bool { +func (c *RegisteredClient) IsPublic() (public bool) {  	return c.Public  } -// ValidatePKCEPolicy is a helper function to validate PKCE policy constraints on a per-client basis. -func (c *RegisteredClient) ValidatePKCEPolicy(r fosite.Requester) (err error) { -	form := r.GetRequestForm() - -	if c.RequirePKCE { -		if form.Get(FormParameterCodeChallenge) == "" { -			return errorsx.WithStack(fosite.ErrInvalidRequest. -				WithHint("Clients must include a code_challenge when performing the authorize code flow, but it is missing."). -				WithDebug("The server is configured in a way that enforces PKCE for this client.")) -		} - -		if c.RequirePKCEChallengeMethod { -			if method := form.Get(FormParameterCodeChallengeMethod); method != c.PKCEChallengeMethod { -				return errorsx.WithStack(fosite.ErrInvalidRequest. -					WithHintf("Client must use code_challenge_method=%s, %s is not allowed.", c.PKCEChallengeMethod, method). -					WithDebugf("The server is configured in a way that enforces PKCE %s as challenge method for this client.", c.PKCEChallengeMethod)) -			} -		} -	} - -	return nil -} - -// ValidatePARPolicy is a helper function to validate additional policy constraints on a per-client basis. -func (c *RegisteredClient) ValidatePARPolicy(r fosite.Requester, prefix string) (err error) { -	if c.RequirePushedAuthorizationRequests { -		if !IsPushedAuthorizedRequest(r, prefix) { -			switch requestURI := r.GetRequestForm().Get(FormParameterRequestURI); requestURI { -			case "": -				return errorsx.WithStack(ErrPAREnforcedClientMissingPAR.WithDebug("The request_uri parameter was empty.")) -			default: -				return errorsx.WithStack(ErrPAREnforcedClientMissingPAR.WithDebugf("The request_uri parameter '%s' is malformed.", requestURI)) -			} -		} -	} - -	return nil -} -  // ValidateResponseModePolicy is an additional check to the response mode parameter to ensure if it's omitted that the -// default response mode for the fosite.AuthorizeRequester is permitted. -func (c *RegisteredClient) ValidateResponseModePolicy(r fosite.AuthorizeRequester) (err error) { -	if r.GetResponseMode() != fosite.ResponseModeDefault { +// default response mode for the oauthelia2.AuthorizeRequester is permitted. +func (c *RegisteredClient) ValidateResponseModePolicy(r oauthelia2.AuthorizeRequester) (err error) { +	if r.GetResponseMode() != oauthelia2.ResponseModeDefault {  		return nil  	} @@ -364,40 +355,73 @@ func (c *RegisteredClient) ValidateResponseModePolicy(r fosite.AuthorizeRequeste  		}  	} -	return errorsx.WithStack(fosite.ErrUnsupportedResponseMode.WithHintf(`The request omitted the response_mode making the default response_mode "%s" based on the other authorization request parameters but registered OAuth 2.0 client doesn't support this response_mode`, m)) +	return errorsx.WithStack(oauthelia2.ErrUnsupportedResponseMode.WithHintf(`The request omitted the response_mode making the default response_mode "%s" based on the other authorization request parameters but registered OAuth 2.0 client doesn't support this response_mode`, m))  }  // GetRefreshFlowIgnoreOriginalGrantedScopes returns the value which indicates if the client should ignore the  // originally granted scopes when the scope parameter is present. The specification requires that this is always false,  // however some misbehaving clients may need this option. -func (c *RegisteredClient) GetRefreshFlowIgnoreOriginalGrantedScopes(ctx context.Context) (ignoreOriginalGrantedScopes bool) { +func (c *RegisteredClient) GetRefreshFlowIgnoreOriginalGrantedScopes(ctx context.Context) (ignore bool) {  	return c.RefreshFlowIgnoreOriginalGrantedScopes  } -func (c *RegisteredClient) getGrantTypeLifespan(gt fosite.GrantType) (gtl schema.IdentityProvidersOpenIDConnectLifespanToken) { -	switch gt { -	case fosite.GrantTypeAuthorizationCode: -		return c.Lifespans.Grants.AuthorizeCode -	case fosite.GrantTypeImplicit: -		return c.Lifespans.Grants.Implicit -	case fosite.GrantTypeClientCredentials: -		return c.Lifespans.Grants.ClientCredentials -	case fosite.GrantTypeRefreshToken: -		return c.Lifespans.Grants.RefreshToken -	case fosite.GrantTypeJWTBearer: -		return c.Lifespans.Grants.JWTBearer -	default: -		return gtl +func (c *RegisteredClient) GetRevokeRefreshTokensExplicit(ctx context.Context) (explicit bool) { +	return false +} + +// GetRequestURIs is an array of request_uri values that are pre-registered by the RP for use at the OP. Servers MAY +// cache the contents of the files referenced by these URIs and not retrieve them at the time they are used in a request. +// OPs can require that request_uri values used be pre-registered with the require_request_uri_registration +// discovery parameter. +func (c *RegisteredClient) GetRequestURIs() (uris []string) { +	return c.RequestURIs +} + +// GetJSONWebKeys returns the JSON Web Key Set containing the public key used by the client to authenticate. +func (c *RegisteredClient) GetJSONWebKeys() (keys *jose.JSONWebKeySet) { +	return c.JSONWebKeys +} + +// SetJSONWebKeys sets the JSON Web Key Set containing the public key used by the client to authenticate. +func (c *RegisteredClient) SetJSONWebKeys(jwks *jose.JSONWebKeySet) { +	c.JSONWebKeys = jwks +} + +// GetJSONWebKeysURI returns the URL for lookup of JSON Web Key Set containing the +// public key used by the client to authenticate. +func (c *RegisteredClient) GetJSONWebKeysURI() (uri string) { +	if c.JSONWebKeysURI == nil { +		return ""  	} + +	return c.JSONWebKeysURI.String() +} + +// GetRequestObjectSigningAlg returns the JWS [JWS] alg algorithm [JWA] that MUST be used for signing Request +// Objects sent to the OP. All Request Objects from this Client MUST be rejected, if not signed with this algorithm. +func (c *RegisteredClient) GetRequestObjectSigningAlg() (alg string) { +	return c.RequestObjectSigningAlg +} + +func (c *RegisteredClient) GetAllowMultipleAuthenticationMethods(ctx context.Context) (allow bool) { +	return c.AllowMultipleAuthenticationMethods +} + +func (c *RegisteredClient) GetClientCredentialsFlowRequestedScopeImplicit() (allow bool) { +	return c.ClientCredentialsFlowAllowImplicitScope +} + +func (c *RegisteredClient) GetRequestedAudienceImplicit() (implicit bool) { +	return c.RequestedAudienceMode == ClientRequestedAudienceModeImplicit  }  // GetEffectiveLifespan returns the effective lifespan for a grant type and token type otherwise returns the fallback -// value. This implements the fosite.ClientWithCustomTokenLifespans interface. -func (c *RegisteredClient) GetEffectiveLifespan(gt fosite.GrantType, tt fosite.TokenType, fallback time.Duration) time.Duration { +// value. This implements the oauthelia2.ClientWithCustomTokenLifespans interface. +func (c *RegisteredClient) GetEffectiveLifespan(gt oauthelia2.GrantType, tt oauthelia2.TokenType, fallback time.Duration) time.Duration {  	gtl := c.getGrantTypeLifespan(gt)  	switch tt { -	case fosite.AccessToken: +	case oauthelia2.AccessToken:  		switch {  		case gtl.AccessToken > durationZero:  			return gtl.AccessToken @@ -406,7 +430,7 @@ func (c *RegisteredClient) GetEffectiveLifespan(gt fosite.GrantType, tt fosite.T  		default:  			return fallback  		} -	case fosite.AuthorizeCode: +	case oauthelia2.AuthorizeCode:  		switch {  		case gtl.AuthorizeCode > durationZero:  			return gtl.AuthorizeCode @@ -415,7 +439,7 @@ func (c *RegisteredClient) GetEffectiveLifespan(gt fosite.GrantType, tt fosite.T  		default:  			return fallback  		} -	case fosite.IDToken: +	case oauthelia2.IDToken:  		switch {  		case gtl.IDToken > durationZero:  			return gtl.IDToken @@ -424,7 +448,7 @@ func (c *RegisteredClient) GetEffectiveLifespan(gt fosite.GrantType, tt fosite.T  		default:  			return fallback  		} -	case fosite.RefreshToken: +	case oauthelia2.RefreshToken:  		switch {  		case gtl.RefreshToken > durationZero:  			return gtl.RefreshToken @@ -438,61 +462,19 @@ func (c *RegisteredClient) GetEffectiveLifespan(gt fosite.GrantType, tt fosite.T  	}  } -// GetRequestURIs is an array of request_uri values that are pre-registered by the RP for use at the OP. Servers MAY -// cache the contents of the files referenced by these URIs and not retrieve them at the time they are used in a request. -// OPs can require that request_uri values used be pre-registered with the require_request_uri_registration -// discovery parameter. -func (c *RegisteredClient) GetRequestURIs() []string { -	return c.RequestURIs -} - -// GetJSONWebKeys returns the JSON Web Key Set containing the public key used by the client to authenticate. -func (c *RegisteredClient) GetJSONWebKeys() *jose.JSONWebKeySet { -	return c.JSONWebKeys -} - -// SetJSONWebKeys sets the JSON Web Key Set containing the public key used by the client to authenticate. -func (c *RegisteredClient) SetJSONWebKeys(jwks *jose.JSONWebKeySet) { -	c.JSONWebKeys = jwks -} - -// GetJSONWebKeysURI returns the URL for lookup of JSON Web Key Set containing the -// public key used by the client to authenticate. -func (c *RegisteredClient) GetJSONWebKeysURI() string { -	if c.JSONWebKeysURI == nil { -		return "" -	} - -	return c.JSONWebKeysURI.String() -} - -// GetRequestObjectSigningAlgorithm returns the JWS [JWS] alg algorithm [JWA] that MUST be used for signing Request -// Objects sent to the OP. All Request Objects from this Client MUST be rejected, if not signed with this algorithm. -func (c *RegisteredClient) GetRequestObjectSigningAlgorithm() string { -	return c.RequestObjectSigningAlg -} - -// GetTokenEndpointAuthMethod returns the requested Client Authentication Method for the Token Endpoint. The options are -// client_secret_post, client_secret_basic, client_secret_jwt, private_key_jwt, and none. -func (c *RegisteredClient) GetTokenEndpointAuthMethod() string { -	if c.TokenEndpointAuthMethod == "" { -		if c.Public { -			c.TokenEndpointAuthMethod = ClientAuthMethodNone -		} else { -			c.TokenEndpointAuthMethod = ClientAuthMethodClientSecretBasic -		} -	} - -	return c.TokenEndpointAuthMethod -} - -// GetTokenEndpointAuthSigningAlgorithm returns the JWS [JWS] alg algorithm [JWA] that MUST be used for signing the JWT -// [JWT] used to authenticate the Client at the Token Endpoint for the private_key_jwt and client_secret_jwt -// authentication methods. -func (c *RegisteredClient) GetTokenEndpointAuthSigningAlgorithm() string { -	if c.TokenEndpointAuthSigningAlg == "" { -		c.TokenEndpointAuthSigningAlg = SigningAlgRSAUsingSHA256 +func (c *RegisteredClient) getGrantTypeLifespan(gt oauthelia2.GrantType) (gtl schema.IdentityProvidersOpenIDConnectLifespanToken) { +	switch gt { +	case oauthelia2.GrantTypeAuthorizationCode: +		return c.Lifespans.Grants.AuthorizeCode +	case oauthelia2.GrantTypeImplicit: +		return c.Lifespans.Grants.Implicit +	case oauthelia2.GrantTypeClientCredentials: +		return c.Lifespans.Grants.ClientCredentials +	case oauthelia2.GrantTypeRefreshToken: +		return c.Lifespans.Grants.RefreshToken +	case oauthelia2.GrantTypeJWTBearer: +		return c.Lifespans.Grants.JWTBearer +	default: +		return gtl  	} - -	return c.TokenEndpointAuthSigningAlg  } diff --git a/internal/oidc/client_credentials.go b/internal/oidc/client_credentials.go index 9a0cd900b..72d360f29 100644 --- a/internal/oidc/client_credentials.go +++ b/internal/oidc/client_credentials.go @@ -2,762 +2,26 @@ package oidc  import (  	"context" -	"crypto/ecdsa" -	"crypto/rsa" -	"crypto/sha256" -	"crypto/subtle" -	"encoding/base64" -	"errors" -	"fmt" -	"net/http" -	"net/url" -	"regexp" -	"strings" -	"time" -	"github.com/go-crypt/crypt" -	"github.com/go-crypt/crypt/algorithm" -	"github.com/go-crypt/crypt/algorithm/plaintext" -	"github.com/go-jose/go-jose/v3" -	"github.com/golang-jwt/jwt/v5" -	"github.com/ory/fosite" -	"github.com/ory/fosite/handler/oauth2" -	"github.com/ory/fosite/handler/pkce" -	"github.com/ory/x/errorsx" -	"github.com/valyala/fasthttp" -) - -// NewHasher returns a new Hasher. -func NewHasher() (hasher *Hasher, err error) { -	hasher = &Hasher{} - -	if hasher.decoder, err = crypt.NewDefaultDecoder(); err != nil { -		return nil, err -	} - -	if err = plaintext.RegisterDecoderPlainText(hasher.decoder); err != nil { -		return nil, err -	} - -	return hasher, nil -} - -// Hasher implements the fosite.Hasher interface and adaptively compares hashes. -type Hasher struct { -	decoder algorithm.DecoderRegister -} - -// Compare compares the hash with the data and returns an error if they don't match. -func (h Hasher) Compare(_ context.Context, hash, data []byte) (err error) { -	var digest algorithm.Digest - -	if digest, err = h.decoder.Decode(string(hash)); err != nil { -		return err -	} - -	if digest.MatchBytes(data) { -		return nil -	} - -	return errPasswordsDoNotMatch -} - -// Hash creates a new hash from data. -func (h Hasher) Hash(_ context.Context, data []byte) (hash []byte, err error) { -	return data, nil -} - -// DefaultClientAuthenticationStrategy is a copy of fosite's with the addition of the client_secret_jwt method and some -// minor superficial changes. -func (p *OpenIDConnectProvider) DefaultClientAuthenticationStrategy(ctx context.Context, r *http.Request, form url.Values) (c fosite.Client, err error) { -	switch assertionType := form.Get(FormParameterClientAssertionType); assertionType { -	case "": -		break -	case ClientAssertionJWTBearerType: -		return p.JWTBearerClientAuthenticationStrategy(ctx, r, form) -	default: -		return nil, errorsx.WithStack(fosite.ErrInvalidRequest.WithHintf("Unknown client_assertion_type '%s'.", assertionType)) -	} - -	clientID, clientSecret, method, err := clientCredentialsFromRequest(r.Header, form) -	if err != nil { -		return nil, err -	} - -	var client Client - -	if client, err = p.Store.GetFullClient(ctx, clientID); err != nil { -		if errors.Is(err, fosite.ErrInvalidClient) { -			return nil, err -		} - -		return nil, errorsx.WithStack(fosite.ErrInvalidClient.WithWrap(err).WithDebug(ErrorToDebugRFC6749Error(err).Error())) -	} - -	if fclient, ok := client.(*RegisteredClient); ok { -		cmethod := fclient.GetTokenEndpointAuthMethod() - -		switch { -		case method != cmethod: -			return nil, errorsx.WithStack(fosite.ErrInvalidClient.WithHintf(errHintFmtClientAuthMethodMismatch, cmethod, method, method)) -		case method != ClientAuthMethodNone && fclient.IsPublic(): -			return nil, errorsx.WithStack(fosite.ErrInvalidClient.WithHintf("The OAuth 2.0 Client is not a confidential client however the client authentication method '%s' was used which is not permitted as it's only permitted on confidential clients.", method)) -		case method == ClientAuthMethodNone && !fclient.IsPublic(): -			return nil, errorsx.WithStack(fosite.ErrInvalidClient.WithHintf("The OAuth 2.0 Client is a confidential client however the client authentication method '%s' was used which is not permitted as it's not permitted on confidential clients.", method)) -		} -	} - -	if client.IsPublic() { -		return client, nil -	} - -	if err = p.checkClientSecret(ctx, client, []byte(clientSecret)); err != nil { -		return nil, errorsx.WithStack(fosite.ErrInvalidClient.WithWrap(err).WithDebug(ErrorToDebugRFC6749Error(err).Error())) -	} - -	return client, nil -} - -//nolint:gocyclo -func (p *OpenIDConnectProvider) JWTBearerClientAuthenticationStrategy(ctx context.Context, r *http.Request, form url.Values) (client fosite.Client, err error) { -	if form.Has(FormParameterClientSecret) { -		return nil, errorsx.WithStack(fosite.ErrInvalidRequest.WithHintf("The client_secret request parameter must not be set when using client_assertion_type of '%s'.", ClientAssertionJWTBearerType)) -	} - -	if value := r.Header.Get(fasthttp.HeaderAuthorization); len(value) != 0 { -		return nil, errorsx.WithStack(fosite.ErrInvalidRequest.WithHintf("The Authorization request header must not be set when using client_assertion_type of '%s'.", ClientAssertionJWTBearerType)) -	} - -	var claims jwt.MapClaims - -	client, _, claims, err = p.parseJWTAssertion(ctx, form) - -	switch { -	case err == nil: -		break -	default: -		var e *fosite.RFC6749Error - -		if errors.As(err, &e) { -			return nil, e -		} - -		rfc := fosite.ErrInvalidClient. -			WithHint("Unable to verify the integrity of the 'client_assertion' value."). -			WithWrap(err) - -		switch { -		case errors.Is(err, jwt.ErrTokenMalformed): -			return nil, errorsx.WithStack(rfc.WithDebug("The token is malformed.")) -		case errors.Is(err, jwt.ErrTokenUsedBeforeIssued): -			return nil, errorsx.WithStack(rfc.WithDebug("The token was used before it was issued.")) -		case errors.Is(err, jwt.ErrTokenExpired): -			return nil, errorsx.WithStack(rfc.WithDebug("The token is expired.")) -		case errors.Is(err, jwt.ErrTokenNotValidYet): -			return nil, errorsx.WithStack(rfc.WithDebug("The token isn't valid yet.")) -		case errors.Is(err, jwt.ErrTokenSignatureInvalid): -			return nil, errorsx.WithStack(rfc.WithDebug("The signature is invalid.")) -		case errors.Is(err, jwt.ErrTokenInvalidClaims): -			return nil, errorsx.WithStack(rfc.WithDebug("The token claims are invalid.")) -		default: -			return nil, errorsx.WithStack(fosite.ErrInvalidClient.WithHint("Unable to verify the integrity of the 'client_assertion' value.").WithWrap(err).WithDebug(ErrorToDebugRFC6749Error(err).Error())) -		} -	} - -	var ( -		iss, sub, jti string -		ok            bool -		exp           *jwt.NumericDate -	) - -	if iss, err = claims.GetIssuer(); err != nil { -		return nil, errorsx.WithStack(fosite.ErrInvalidClient.WithHint("Claim 'iss' from 'client_assertion' is invalid.").WithWrap(err)) -	} - -	if sub, err = claims.GetSubject(); err != nil { -		return nil, errorsx.WithStack(fosite.ErrInvalidClient.WithHint("Claim 'sub' from 'client_assertion' is invalid.").WithWrap(err)) -	} - -	if jti, err = JTIFromMapClaims(claims); err != nil { -		return nil, errorsx.WithStack(fosite.ErrInvalidClient.WithHint("Claim 'jti' from 'client_assertion' is invalid.").WithWrap(err)) -	} - -	var tokenURL string +	oauthelia2 "authelia.com/provider/oauth2" -	if tokenURLs := p.Config.GetTokenURLs(ctx); len(tokenURLs) != 0 { -		tokenURL = tokenURLs[0] -	} - -	switch { -	case iss != client.GetID(): -		return nil, errorsx.WithStack(fosite.ErrInvalidClient.WithHint("Claim 'iss' from 'client_assertion' must match the 'client_id' of the OAuth 2.0 Client.").WithDebugf(errDebugFmtParameterMatchClaim, ClaimIssuer, iss, FormParameterClientID, client.GetID())) -	case sub != client.GetID(): -		return nil, errorsx.WithStack(fosite.ErrInvalidClient.WithHint("Claim 'sub' from 'client_assertion' must match the 'client_id' of the OAuth 2.0 Client.").WithDebugf(errDebugFmtParameterMatchClaim, ClaimSubject, sub, FormParameterClientID, client.GetID())) -	case tokenURL == "": -		return nil, errorsx.WithStack(fosite.ErrMisconfiguration.WithHint("The authorization server's token endpoint URL has not been set.")) -	case len(jti) == 0: -		return nil, errorsx.WithStack(fosite.ErrInvalidClient.WithHint("Claim 'jti' from 'client_assertion' must be set but it is not.")) -	case p.Store.ClientAssertionJWTValid(ctx, jti) != nil: -		return nil, errorsx.WithStack(fosite.ErrJTIKnown.WithHint("Claim 'jti' from 'client_assertion' MUST only be used once.")) -	} - -	if exp, err = claims.GetExpirationTime(); err != nil { -		var epoch int64 - -		if epoch, ok = claims[ClaimExpirationTime].(int64); ok { -			exp = jwt.NewNumericDate(time.Unix(epoch, 0)) -		} else { -			return nil, errorsx.WithStack(err) -		} -	} - -	if err = p.Store.SetClientAssertionJWT(ctx, jti, exp.Time); err != nil { -		return nil, err -	} - -	var ( -		found bool -	) - -	if auds, ok := claims[ClaimAudience].([]any); ok { -		for _, aud := range auds { -			if audience, ok := aud.(string); ok && audience == tokenURL { -				found = true -				break -			} -		} -	} - -	if !found { -		return nil, errorsx.WithStack(fosite.ErrInvalidClient.WithHintf("Claim 'aud' from 'client_assertion' must match the authorization server's token endpoint '%s'.", tokenURL)) -	} - -	return client, nil -} - -//nolint:gocyclo -func (p *OpenIDConnectProvider) parseJWTAssertion(ctx context.Context, form url.Values) (client fosite.Client, token *jwt.Token, claims jwt.MapClaims, err error) { -	assertion := form.Get(FormParameterClientAssertion) -	if len(assertion) == 0 { -		return nil, nil, nil, errorsx.WithStack(fosite.ErrInvalidRequest.WithHintf("The 'client_assertion' request parameter must be set when using 'client_assertion_type' of '%s'.", ClientAssertionJWTBearerType)) -	} - -	var ( -		clientID string -	) - -	clientID = form.Get(FormParameterClientID) - -	parserOpts := []jwt.ParserOption{ -		jwt.WithIssuedAt(), -		jwt.WithStrictDecoding(), -	} - -	if octx, ok := ctx.(Context); ok { -		parserOpts = append(parserOpts, octx.GetJWTWithTimeFuncOption()) -	} - -	token, err = jwt.ParseWithClaims(assertion, jwt.MapClaims{}, func(token *jwt.Token) (any, error) { -		var ( -			ok bool -		) - -		claims, ok = token.Claims.(jwt.MapClaims) -		if !ok { -			return nil, errorsx.WithStack(fosite.ErrInvalidClient.WithHint("The claims could not be parsed due to an unknown error.")) -		} - -		if clientID, err = parseJWTAssertionClientID(clientID, claims); err != nil { -			return nil, err -		} - -		if client, err = p.Store.GetFullClient(ctx, clientID); err != nil { -			if errors.Is(err, fosite.ErrInvalidClient) { -				return nil, err -			} - -			return nil, errorsx.WithStack(fosite.ErrInvalidClient.WithWrap(err).WithDebug(ErrorToDebugRFC6749Error(err).Error())) -		} - -		fclient, ok := client.(*RegisteredClient) -		if !ok { -			return nil, errorsx.WithStack(fosite.ErrInvalidRequest.WithHint("The client configuration does not support OpenID Connect specific authentication methods.")) -		} - -		switch fclient.GetTokenEndpointAuthMethod() { -		case ClientAuthMethodPrivateKeyJWT: -			switch token.Method { -			case jwt.SigningMethodRS256, jwt.SigningMethodRS384, jwt.SigningMethodRS512, -				jwt.SigningMethodPS256, jwt.SigningMethodPS384, jwt.SigningMethodPS512, -				jwt.SigningMethodES256, jwt.SigningMethodES384, jwt.SigningMethodES512: -				break -			default: -				return nil, errorsx.WithStack(fosite.ErrInvalidClient.WithHintf("This requested OAuth 2.0 client supports client authentication method '%s', however the '%s' JWA is not supported with this method.", ClientAuthMethodPrivateKeyJWT, token.Header[JWTHeaderKeyAlgorithm])) -			} -		case ClientAuthMethodClientSecretJWT: -			switch token.Method { -			case jwt.SigningMethodHS256, jwt.SigningMethodHS384, jwt.SigningMethodHS512: -				break -			default: -				return nil, errorsx.WithStack(fosite.ErrInvalidClient.WithHintf("This requested OAuth 2.0 client supports client authentication method '%s', however the '%s' JWA is not supported with this method.", ClientAuthMethodClientSecretJWT, token.Header[JWTHeaderKeyAlgorithm])) -			} -		case ClientAuthMethodNone: -			return nil, errorsx.WithStack(fosite.ErrInvalidClient.WithHint("This requested OAuth 2.0 client does not support client authentication, however 'client_assertion' was provided in the request.")) -		case ClientAuthMethodClientSecretPost: -			fallthrough -		case ClientAuthMethodClientSecretBasic: -			return nil, errorsx.WithStack(fosite.ErrInvalidClient.WithHintf("This requested OAuth 2.0 client only supports client authentication method '%s', however 'client_assertion' was provided in the request.", fclient.GetTokenEndpointAuthMethod())) -		default: -			return nil, errorsx.WithStack(fosite.ErrInvalidClient.WithHintf("This requested OAuth 2.0 client only supports client authentication method '%s', however that method is not supported by this server.", fclient.GetTokenEndpointAuthMethod())) -		} - -		if fclient.GetTokenEndpointAuthSigningAlgorithm() != fmt.Sprintf("%s", token.Header[JWTHeaderKeyAlgorithm]) { -			return nil, errorsx.WithStack(fosite.ErrInvalidClient.WithHintf("The 'client_assertion' uses signing algorithm '%s' but the requested OAuth 2.0 Client enforces signing algorithm '%s'.", token.Header[JWTHeaderKeyAlgorithm], fclient.GetTokenEndpointAuthSigningAlgorithm())) -		} - -		switch token.Method { -		case jwt.SigningMethodRS256, jwt.SigningMethodRS384, jwt.SigningMethodRS512: -			return p.findClientPublicJWK(ctx, fclient, token, true) -		case jwt.SigningMethodES256, jwt.SigningMethodES384, jwt.SigningMethodES512: -			return p.findClientPublicJWK(ctx, fclient, token, false) -		case jwt.SigningMethodPS256, jwt.SigningMethodPS384, jwt.SigningMethodPS512: -			return p.findClientPublicJWK(ctx, fclient, token, true) -		case jwt.SigningMethodHS256, jwt.SigningMethodHS384, jwt.SigningMethodHS512: -			var secret *plaintext.Digest - -			if secret, ok = fclient.Secret.PlainText(); ok { -				return secret.Key(), nil -			} - -			return nil, errorsx.WithStack(fosite.ErrInvalidClient.WithHint("This client does not support authentication method 'client_secret_jwt' as the client secret is not in plaintext.")) -		default: -			return nil, errorsx.WithStack(fosite.ErrInvalidClient.WithHintf("The 'client_assertion' request parameter uses unsupported signing algorithm '%s'.", token.Header[JWTHeaderKeyAlgorithm])) -		} -	}, parserOpts...) - -	if err != nil { -		return -	} - -	return client, token, token.Claims.(jwt.MapClaims), err -} - -func parseJWTAssertionClientID(clientID string, claims jwt.MapClaims) (string, error) { -	if len(clientID) > 0 { -		return clientID, nil -	} - -	var err error - -	if clientID, err = claims.GetSubject(); err != nil { -		return "", errorsx.WithStack(fosite.ErrInvalidClient.WithHint("Claim 'sub' from 'client_assertion' is invalid.").WithWrap(err)) -	} else if len(clientID) > 0 { -		return clientID, nil -	} - -	if clientID, err = claims.GetIssuer(); err != nil { -		return "", errorsx.WithStack(fosite.ErrInvalidClient.WithHint("Claim 'iss' from 'client_assertion' is invalid.").WithWrap(err)) -	} else if len(clientID) > 0 { -		return clientID, nil -	} - -	return "", fosite.ErrInvalidClient.WithHint("There was insufficient information in the request to identify the client this request is for.") -} - -func (p *OpenIDConnectProvider) checkClientSecret(ctx context.Context, client Client, clientSecret []byte) (err error) { -	if client.GetSecret().MatchBytes(clientSecret) { -		return nil -	} - -	err = errPasswordsDoNotMatch - -	cc, ok := client.(fosite.ClientWithSecretRotation) -	if !ok { -		return err -	} - -	for _, hash := range cc.GetRotatedHashes() { -		if err = p.Config.GetSecretsHasher(ctx).Compare(ctx, hash, clientSecret); err == nil { -			return nil -		} -	} - -	return err -} - -func (p *OpenIDConnectProvider) findClientPublicJWK(ctx context.Context, oidcClient fosite.OpenIDConnectClient, t *jwt.Token, expectsRSAKey bool) (any, error) { -	if set := oidcClient.GetJSONWebKeys(); set != nil { -		return findPublicKey(t, set, expectsRSAKey) -	} - -	if location := oidcClient.GetJSONWebKeysURI(); len(location) > 0 { -		keys, err := p.Config.GetJWKSFetcherStrategy(ctx).Resolve(ctx, location, false) -		if err != nil { -			return nil, err -		} - -		if key, err := findPublicKey(t, keys, expectsRSAKey); err == nil { -			return key, nil -		} - -		keys, err = p.Config.GetJWKSFetcherStrategy(ctx).Resolve(ctx, location, true) -		if err != nil { -			return nil, err -		} - -		return findPublicKey(t, keys, expectsRSAKey) -	} - -	return nil, errorsx.WithStack(fosite.ErrInvalidClient.WithHint("The OAuth 2.0 Client has no JSON Web Keys set registered, but they are needed to complete the request.")) -} - -func findPublicKey(t *jwt.Token, set *jose.JSONWebKeySet, expectsRSAKey bool) (any, error) { -	if len(set.Keys) == 0 { -		return nil, errorsx.WithStack(fosite.ErrInvalidRequest.WithHintf("The retrieved JSON Web Key Set does not contain any key.")) -	} - -	keys := set.Keys - -	kid, ok := t.Header[JWTHeaderKeyIdentifier].(string) -	if ok { -		keys = set.Key(kid) -	} - -	if len(keys) == 0 { -		return nil, errorsx.WithStack(fosite.ErrInvalidRequest.WithHintf("The JSON Web Token uses signing key with kid '%s', which could not be found.", kid)) -	} - -	for _, key := range keys { -		if key.Use != KeyUseSignature { -			continue -		} - -		if expectsRSAKey { -			if k, ok := key.Key.(*rsa.PublicKey); ok { -				return k, nil -			} -		} else { -			if k, ok := key.Key.(*ecdsa.PublicKey); ok { -				return k, nil -			} -		} -	} - -	if expectsRSAKey { -		return nil, errorsx.WithStack(fosite.ErrInvalidRequest.WithHintf("Unable to find RSA public key with a 'use' value of 'sig' for kid '%s' in JSON Web Key Set.", kid)) -	} else { -		return nil, errorsx.WithStack(fosite.ErrInvalidRequest.WithHintf("Unable to find ECDSA public key with a 'use' value of 'sig' for kid '%s' in JSON Web Key Set.", kid)) -	} -} - -func clientCredentialsFromRequest(header http.Header, form url.Values) (clientID, clientSecret, method string, err error) { -	var ok bool - -	switch clientID, clientSecret, ok, err = clientCredentialsFromBasicAuth(header); { -	case err != nil: -		return "", "", "", errorsx.WithStack(fosite.ErrInvalidRequest.WithHint("The client credentials in the HTTP authorization header could not be parsed. Either the scheme was missing, the scheme was invalid, or the value had malformed data.").WithWrap(err).WithDebug(ErrorToDebugRFC6749Error(err).Error())) -	case ok: -		return clientID, clientSecret, ClientAuthMethodClientSecretBasic, nil -	default: -		clientID, clientSecret = form.Get(FormParameterClientID), form.Get(FormParameterClientSecret) - -		switch { -		case clientID == "": -			return "", "", "", errorsx.WithStack(fosite.ErrInvalidRequest.WithHint("Client credentials missing or malformed in both HTTP Authorization header and HTTP POST body.")) -		case clientSecret == "": -			return clientID, "", ClientAuthMethodNone, nil -		default: -			return clientID, clientSecret, ClientAuthMethodClientSecretPost, nil -		} -	} -} - -func clientCredentialsFromBasicAuth(header http.Header) (clientID, clientSecret string, ok bool, err error) { -	auth := header.Get(fasthttp.HeaderAuthorization) - -	if auth == "" { -		return "", "", false, nil -	} - -	scheme, value, ok := strings.Cut(auth, " ") - -	if !ok { -		return "", "", false, errors.New("failed to parse http authorization header: invalid scheme: the scheme was missing") -	} - -	if !strings.EqualFold(scheme, httpAuthSchemeBasic) { -		return "", "", false, fmt.Errorf("failed to parse http authorization header: invalid scheme: expected the %s scheme but received %s", httpAuthSchemeBasic, scheme) -	} - -	c, err := base64.StdEncoding.DecodeString(value) -	if err != nil { -		return "", "", false, fmt.Errorf("failed to parse http authorization header: invalid value: malformed base64 data: %w", err) -	} - -	cs := string(c) - -	clientID, clientSecret, ok = strings.Cut(cs, ":") -	if !ok { -		return "", "", false, errors.New("failed to parse http authorization header: invalid value: the basic scheme separator was missing") -	} - -	if clientID, err = url.QueryUnescape(clientID); err != nil { -		return "", "", false, fmt.Errorf("failed to query unescape client id from http authorization header: %w", err) -	} - -	if clientSecret, err = url.QueryUnescape(clientSecret); err != nil { -		return "", "", false, fmt.Errorf("failed to query unescape client secret from http authorization header: %w", err) -	} - -	return clientID, clientSecret, true, nil -} - -// PKCEHandler is a fork of pkce.Handler with modifications to rectify bugs. It implements the -// fosite.TokenEndpointHandler. -type PKCEHandler struct { -	AuthorizeCodeStrategy oauth2.AuthorizeCodeStrategy -	Storage               pkce.PKCERequestStorage -	Config                interface { -		fosite.EnforcePKCEProvider -		fosite.EnforcePKCEForPublicClientsProvider -		fosite.EnablePKCEPlainChallengeMethodProvider -	} -} - -var ( -	rePKCEVerifier = regexp.MustCompile(`[^\w.\-~]`) +	"github.com/authelia/authelia/v4/internal/configuration/schema"  ) -// HandleAuthorizeEndpointRequest implements fosite.TokenEndpointHandler partially. -func (c *PKCEHandler) HandleAuthorizeEndpointRequest(ctx context.Context, requester fosite.AuthorizeRequester, responder fosite.AuthorizeResponder) error { -	// This let's us define multiple response types, for example open id connect's id_token. -	if !requester.GetResponseTypes().Has(FormParameterAuthorizationCode) { -		return nil -	} - -	challenge := requester.GetRequestForm().Get(FormParameterCodeChallenge) -	method := requester.GetRequestForm().Get(FormParameterCodeChallengeMethod) -	client := requester.GetClient() - -	if err := c.validate(ctx, challenge, method, client); err != nil { -		return err -	} - -	// We don't need a session if it's not enforced and the PKCE parameters are not provided by the client. -	if challenge == "" && method == "" { -		return nil -	} - -	code := responder.GetCode() -	if len(code) == 0 { -		return errorsx.WithStack(fosite.ErrServerError.WithDebug("The PKCE handler must be loaded after the authorize code handler.")) -	} - -	signature := c.AuthorizeCodeStrategy.AuthorizeCodeSignature(ctx, code) - -	if err := c.Storage.CreatePKCERequestSession(ctx, signature, requester.Sanitize([]string{FormParameterCodeChallenge, FormParameterCodeChallengeMethod})); err != nil { -		return errorsx.WithStack(fosite.ErrServerError.WithWrap(err).WithDebug(ErrorToDebugRFC6749Error(err).Error())) -	} - -	return nil -} - -func (c *PKCEHandler) validate(ctx context.Context, challenge, method string, client fosite.Client) (err error) { -	if len(challenge) == 0 { -		// If the server requires Proof Key for Code Exchange (PKCE) by OAuth -		// clients and the client does not send the "code_challenge" in -		// the request, the authorization endpoint MUST return the authorization -		// error response with the "error" value set to "invalid_request".  The -		// "error_description" or the response of "error_uri" SHOULD explain the -		// nature of error, e.g., code challenge required. -		return c.validateNoPKCE(ctx, client) -	} - -	// If the server supporting PKCE does not support the requested -	// transformation, the authorization endpoint MUST return the -	// authorization error response with "error" value set to -	// "invalid_request".  The "error_description" or the response of -	// "error_uri" SHOULD explain the nature of error, e.g., transform -	// algorithm not supported. -	switch method { -	case PKCEChallengeMethodSHA256: -		break -	case PKCEChallengeMethodPlain: -		fallthrough -	case "": -		if !c.Config.GetEnablePKCEPlainChallengeMethod(ctx) { -			return errorsx.WithStack(fosite.ErrInvalidRequest. -				WithHint("Clients must use the 'S256' PKCE 'code_challenge_method' but the 'plain' method was requested."). -				WithDebug("The server is configured in a way that enforces PKCE 'S256' as challenge method for clients.")) -		} -	default: -		return errorsx.WithStack(fosite.ErrInvalidRequest. -			WithHint("The code_challenge_method is not supported, use S256 instead.")) -	} - -	return nil -} - -func (c *PKCEHandler) validateNoPKCE(ctx context.Context, client fosite.Client) error { -	var enforce bool - -	enforce = c.Config.GetEnforcePKCE(ctx) - -	if enforce { -		return errorsx.WithStack(fosite.ErrInvalidRequest. -			WithHint("Clients must include a code_challenge when performing the authorize code flow, but it is missing."). -			WithDebug("The server is configured in a way that enforces PKCE for clients.")) -	} - -	enforce = c.Config.GetEnforcePKCEForPublicClients(ctx) -	public := client.IsPublic() - -	if enforce && public { -		return errorsx.WithStack(fosite.ErrInvalidRequest. -			WithHint("This client must include a code_challenge when performing the authorize code flow, but it is missing."). -			WithDebug("The server is configured in a way that enforces PKCE for this client.")) -	} - -	return nil +// ClientSecretDigest decorates the *schema.PasswordDigest with the relevant functions to implement oauth2.ClientSecret. +type ClientSecretDigest struct { +	*schema.PasswordDigest  } -// HandleTokenEndpointRequest implements fosite.TokenEndpointHandler partially. -// -//nolint:gocyclo -func (c *PKCEHandler) HandleTokenEndpointRequest(ctx context.Context, requester fosite.AccessRequester) (err error) { -	if !c.CanHandleTokenEndpointRequest(ctx, requester) { -		return errorsx.WithStack(fosite.ErrUnknownRequest) +// Compare decorates the *schema.PasswordDigest's implementation to satisfy oauth2.ClientSecret's Compare function. +func (d *ClientSecretDigest) Compare(ctx context.Context, rawSecret []byte) (err error) { +	if d.PasswordDigest == nil || d.PasswordDigest.Digest == nil { +		return oauthelia2.ErrClientSecretNotRegistered  	} -	// code_verifier -	// REQUIRED.  Code verifier -	// -	// The "code_challenge_method" is bound to the Authorization Code when -	// the Authorization Code is issued.  That is the method that the token -	// endpoint MUST use to verify the "code_verifier". -	verifier := requester.GetRequestForm().Get(FormParameterCodeVerifier) - -	code := requester.GetRequestForm().Get(FormParameterAuthorizationCode) -	signature := c.AuthorizeCodeStrategy.AuthorizeCodeSignature(ctx, code) -	pkceRequest, err := c.Storage.GetPKCERequestSession(ctx, signature, requester.GetSession()) - -	nv := len(verifier) - -	if errors.Is(err, fosite.ErrNotFound) { -		if nv == 0 { -			return c.validateNoPKCE(ctx, requester.GetClient()) -		} - -		return errorsx.WithStack(fosite.ErrInvalidGrant.WithHint("Unable to find initial PKCE data tied to this request.").WithWrap(err).WithDebug(ErrorToDebugRFC6749Error(err).Error())) -	} else if err != nil { -		return errorsx.WithStack(fosite.ErrServerError.WithWrap(err).WithDebug(ErrorToDebugRFC6749Error(err).Error())) -	} - -	if err = c.Storage.DeletePKCERequestSession(ctx, signature); err != nil { -		return errorsx.WithStack(fosite.ErrServerError.WithWrap(err).WithDebug(ErrorToDebugRFC6749Error(err).Error())) -	} - -	challenge := pkceRequest.GetRequestForm().Get(FormParameterCodeChallenge) -	method := pkceRequest.GetRequestForm().Get(FormParameterCodeChallengeMethod) -	client := pkceRequest.GetClient() - -	if err = c.validate(ctx, challenge, method, client); err != nil { -		return err -	} - -	nc := len(challenge) - -	if !c.Config.GetEnforcePKCE(ctx) && nc == 0 && nv == 0 { +	if d.MatchBytes(rawSecret) {  		return nil  	} -	// NOTE: The code verifier SHOULD have enough entropy to make it -	// 	impractical to guess the value.  It is RECOMMENDED that the output of -	// 	a suitable random number generator be used to create a 32-octet -	// 	sequence.  The octet sequence is then base64url-encoded to produce a -	// 	43-octet URL safe string to use as the code verifier. - -	// Miscellaneous validations. -	switch { -	case nv < 43: -		return errorsx.WithStack(fosite.ErrInvalidGrant. -			WithHint("The PKCE code verifier must be at least 43 characters.")) -	case nv > 128: -		return errorsx.WithStack(fosite.ErrInvalidGrant. -			WithHint("The PKCE code verifier can not be longer than 128 characters.")) -	case rePKCEVerifier.MatchString(verifier): -		return errorsx.WithStack(fosite.ErrInvalidGrant. -			WithHint("The PKCE code verifier must only contain [a-Z], [0-9], '-', '.', '_', '~'.")) -	case nc == 0: -		return errorsx.WithStack(fosite.ErrInvalidGrant. -			WithHint("The PKCE code verifier was provided but the code challenge was absent from the authorization request.")) -	} - -	// Upon receipt of the request at the token endpoint, the server -	// verifies it by calculating the code challenge from the received -	// "code_verifier" and comparing it with the previously associated -	// "code_challenge", after first transforming it according to the -	// "code_challenge_method" method specified by the client. -	// -	// 	If the "code_challenge_method" from Section 4.3 was "S256", the -	// received "code_verifier" is hashed by SHA-256, base64url-encoded, and -	// then compared to the "code_challenge", i.e.: -	// -	// BASE64URL-ENCODE(SHA256(ASCII(code_verifier))) == code_challenge -	// -	// If the "code_challenge_method" from Section 4.3 was "plain", they are -	// compared directly, i.e.: -	// -	// code_verifier == code_challenge. -	// -	// 	If the values are equal, the token endpoint MUST continue processing -	// as normal (as defined by OAuth 2.0 [RFC6749]).  If the values are not -	// equal, an error response indicating "invalid_grant" as described in -	// Section 5.2 of [RFC6749] MUST be returned. -	switch method { -	case PKCEChallengeMethodSHA256: -		hash := sha256.New() -		if _, err = hash.Write([]byte(verifier)); err != nil { -			return errorsx.WithStack(fosite.ErrServerError.WithWrap(err).WithDebug(ErrorToDebugRFC6749Error(err).Error())) -		} - -		sum := hash.Sum([]byte{}) - -		expected := make([]byte, base64.RawURLEncoding.EncodedLen(len(sum))) - -		base64.RawURLEncoding.Strict().Encode(expected, sum) - -		if subtle.ConstantTimeCompare(expected, []byte(challenge)) == 0 { -			return errorsx.WithStack(fosite.ErrInvalidGrant. -				WithHint("The PKCE code challenge did not match the code verifier.")) -		} -	case PKCEChallengeMethodPlain: -		fallthrough -	default: -		if subtle.ConstantTimeCompare([]byte(verifier), []byte(challenge)) == 0 { -			return errorsx.WithStack(fosite.ErrInvalidGrant. -				WithHint("The PKCE code challenge did not match the code verifier.")) -		} -	} - -	return nil -} - -// PopulateTokenEndpointResponse implements fosite.TokenEndpointHandler partially. -func (c *PKCEHandler) PopulateTokenEndpointResponse(ctx context.Context, requester fosite.AccessRequester, responder fosite.AccessResponder) (err error) { -	return nil +	return errClientSecretMismatch  } - -// CanSkipClientAuth implements fosite.TokenEndpointHandler partially. -func (c *PKCEHandler) CanSkipClientAuth(ctx context.Context, requester fosite.AccessRequester) bool { -	return false -} - -// CanHandleTokenEndpointRequest implements fosite.TokenEndpointHandler partially. -func (c *PKCEHandler) CanHandleTokenEndpointRequest(ctx context.Context, requester fosite.AccessRequester) bool { -	return requester.GetGrantTypes().ExactOne(GrantTypeAuthorizationCode) -} - -var ( -	_ fosite.TokenEndpointHandler = (*PKCEHandler)(nil) -) diff --git a/internal/oidc/client_credentials_blackbox_test.go b/internal/oidc/client_credentials_blackbox_test.go deleted file mode 100644 index 00dba8bef..000000000 --- a/internal/oidc/client_credentials_blackbox_test.go +++ /dev/null @@ -1,66 +0,0 @@ -package oidc - -import ( -	"net/http" -	"testing" - -	"github.com/stretchr/testify/assert" -	"github.com/valyala/fasthttp" -) - -func TestClientCredentialsFromBasicAuth(t *testing.T) { -	testCases := []struct { -		name       string -		have       http.Header -		id, secret string -		ok         bool -		err        string -	}{ -		{ -			"ShouldParseCredentials", -			http.Header{ -				fasthttp.HeaderAuthorization: []string{"Basic YWJjOjEyMw=="}, -			}, -			"abc", -			"123", -			true, -			"", -		}, -		{ -			"ShouldFailParseCredentialsClientIDNoEscape", -			http.Header{ -				fasthttp.HeaderAuthorization: []string{"Basic YSV6YmM6MTIz"}, -			}, -			"", -			"", -			false, -			"failed to query unescape client id from http authorization header: invalid URL escape \"%zb\"", -		}, -		{ -			"ShouldFailParseCredentialsClientSecretNoEscape", -			http.Header{ -				fasthttp.HeaderAuthorization: []string{"Basic YWJjOjEyMyV6YQ=="}, -			}, -			"", -			"", -			false, -			"failed to query unescape client secret from http authorization header: invalid URL escape \"%za\"", -		}, -	} - -	for _, tc := range testCases { -		t.Run(tc.name, func(t *testing.T) { -			id, secret, ok, err := clientCredentialsFromBasicAuth(tc.have) - -			if tc.err == "" { -				assert.NoError(t, err) -			} else { -				assert.EqualError(t, err, tc.err) -			} - -			assert.Equal(t, tc.id, id) -			assert.Equal(t, tc.secret, secret) -			assert.Equal(t, tc.ok, ok) -		}) -	} -} diff --git a/internal/oidc/client_credentials_test.go b/internal/oidc/client_credentials_test.go deleted file mode 100644 index 8ca16d5ac..000000000 --- a/internal/oidc/client_credentials_test.go +++ /dev/null @@ -1,2863 +0,0 @@ -package oidc_test - -import ( -	"context" -	"crypto/sha256" -	"database/sql" -	"encoding/base64" -	"errors" -	"fmt" -	"io" -	"net/http" -	"net/url" -	"strings" -	"testing" -	"time" - -	"github.com/go-jose/go-jose/v3" -	"github.com/golang-jwt/jwt/v5" -	"github.com/google/uuid" -	"github.com/ory/fosite" -	"github.com/ory/fosite/storage" -	"github.com/stretchr/testify/assert" -	"github.com/stretchr/testify/require" -	"github.com/stretchr/testify/suite" -	"github.com/valyala/fasthttp" -	"go.uber.org/mock/gomock" - -	"github.com/authelia/authelia/v4/internal/authorization" -	"github.com/authelia/authelia/v4/internal/clock" -	"github.com/authelia/authelia/v4/internal/configuration/schema" -	"github.com/authelia/authelia/v4/internal/mocks" -	"github.com/authelia/authelia/v4/internal/model" -	"github.com/authelia/authelia/v4/internal/oidc" -) - -func TestHasher_Compare(t *testing.T) { -	testCases := []struct { -		name     string -		have     string -		input    string -		expected string -	}{ -		{ -			"ShouldComparePlainTextEqual", -			"$plaintext$abc", -			"abc", -			"", -		}, -		{ -			"ShouldComparePlainTextEqualWithSeparator", -			"$plaintext$abc$123", -			"abc$123", -			"", -		}, -		{ -			"ShouldComparePlainTextNotEqual", -			"$plaintext$abc", -			"123", -			"The provided client secret did not match the registered client secret.", -		}, -		{ -			"ShouldCompareReturnHasherErrorBadHash", -			"bad$abc", -			"abc", -			"provided encoded hash has an invalid format: the digest doesn't begin with the delimiter '$' and is not one of the other understood formats", -		}, -	} - -	hasher, err := oidc.NewHasher() -	require.NoError(t, err) - -	for _, tc := range testCases { -		t.Run(tc.name, func(t *testing.T) { -			ctx := context.TODO() - -			if len(tc.expected) == 0 { -				assert.NoError(t, hasher.Compare(ctx, []byte(tc.have), []byte(tc.input))) -			} else { -				assert.EqualError(t, hasher.Compare(ctx, []byte(tc.have), []byte(tc.input)), tc.expected) -			} -		}) -	} -} - -func TestShouldHashPassword(t *testing.T) { -	hasher := oidc.Hasher{} - -	data := []byte(abc) - -	ctx := context.TODO() - -	hash, err := hasher.Hash(ctx, data) - -	assert.NoError(t, err) -	assert.Equal(t, data, hash) -} - -func TestClientAuthenticationStrategySuite(t *testing.T) { -	suite.Run(t, &ClientAuthenticationStrategySuite{}) -} - -type ClientAuthenticationStrategySuite struct { -	suite.Suite - -	issuerURL *url.URL - -	ctrl     *gomock.Controller -	store    *mocks.MockStorage -	provider *oidc.OpenIDConnectProvider -} - -func (s *ClientAuthenticationStrategySuite) GetIssuerURL() *url.URL { -	if s.issuerURL == nil { -		s.issuerURL = MustParseRequestURI("https://auth.example.com") -	} - -	return s.issuerURL -} - -func (s *ClientAuthenticationStrategySuite) GetTokenURL() *url.URL { -	return s.GetIssuerURL().JoinPath(oidc.EndpointPathToken) -} - -func (s *ClientAuthenticationStrategySuite) GetBaseRequest(body io.Reader) (r *http.Request) { -	var err error - -	r, err = http.NewRequest(http.MethodPost, s.GetTokenURL().String(), body) - -	s.Require().NoError(oidc.ErrorToDebugRFC6749Error(err)) -	s.Require().NotNil(r) - -	r.Header.Set(fasthttp.HeaderContentType, "application/x-www-form-urlencoded; charset=utf8") - -	return r -} - -func (s *ClientAuthenticationStrategySuite) GetRequest(values *url.Values) (r *http.Request) { -	var body io.Reader - -	if values != nil { -		body = strings.NewReader(values.Encode()) -	} - -	r = s.GetBaseRequest(body) - -	s.Require().NoError(r.ParseForm()) - -	return r -} - -func (s *ClientAuthenticationStrategySuite) GetAssertionValues(token string) *url.Values { -	values := &url.Values{} - -	values.Set(oidc.FormParameterClientAssertionType, oidc.ClientAssertionJWTBearerType) - -	if token != "" { -		values.Set(oidc.FormParameterClientAssertion, token) -	} - -	return values -} - -func (s *ClientAuthenticationStrategySuite) GetClientValues(id string) *url.Values { -	values := &url.Values{} - -	values.Set(oidc.FormParameterClientID, id) - -	return values -} - -func (s *ClientAuthenticationStrategySuite) GetClientValuesPost(id, secret string) *url.Values { -	values := s.GetClientValues(id) - -	values.Set(oidc.FormParameterClientSecret, secret) - -	return values -} - -func (s *ClientAuthenticationStrategySuite) GetClientSecretBasicRequest(id, secret string) (r *http.Request) { -	values := s.GetClientValues(id) - -	r = s.GetRequest(values) - -	r.SetBasicAuth(id, secret) - -	return r -} - -func (s *ClientAuthenticationStrategySuite) GetClientSecretPostRequest(id, secret string) (r *http.Request) { -	values := s.GetClientValuesPost(id, secret) - -	return s.GetRequest(values) -} - -func (s *ClientAuthenticationStrategySuite) GetAssertionRequest(token string) (r *http.Request) { -	values := s.GetAssertionValues(token) - -	return s.GetRequest(values) -} - -func (s *ClientAuthenticationStrategySuite) GetCtx() oidc.Context { -	return &TestContext{ -		Context:       context.TODO(), -		MockIssuerURL: s.GetIssuerURL(), -		Clock:         clock.New(), -	} -} - -func (s *ClientAuthenticationStrategySuite) SetupTest() { -	s.ctrl = gomock.NewController(s.T()) -	s.store = mocks.NewMockStorage(s.ctrl) - -	secret := tOpenIDConnectPlainTextClientSecret - -	s.provider = oidc.NewOpenIDConnectProvider(&schema.IdentityProvidersOpenIDConnect{ -		JSONWebKeys: []schema.JWK{ -			{Key: x509PrivateKeyRSA2048, CertificateChain: x509CertificateChainRSA2048, Use: oidc.KeyUseSignature, Algorithm: oidc.SigningAlgRSAUsingSHA256}, -		}, -		HMACSecret: "abc123", -		Clients: []schema.IdentityProvidersOpenIDConnectClient{ -			{ -				ID:                  "hs256", -				Secret:              secret, -				AuthorizationPolicy: authorization.OneFactor.String(), -				RedirectURIs: []string{ -					"https://client.example.com", -				}, -				TokenEndpointAuthMethod:     oidc.ClientAuthMethodClientSecretJWT, -				TokenEndpointAuthSigningAlg: oidc.SigningAlgHMACUsingSHA256, -			}, -			{ -				ID:                  "hs384", -				Secret:              secret, -				AuthorizationPolicy: authorization.OneFactor.String(), -				RedirectURIs: []string{ -					"https://client.example.com", -				}, -				TokenEndpointAuthMethod:     oidc.ClientAuthMethodClientSecretJWT, -				TokenEndpointAuthSigningAlg: oidc.SigningAlgHMACUsingSHA384, -			}, -			{ -				ID:                  "hs512", -				Secret:              secret, -				AuthorizationPolicy: authorization.OneFactor.String(), -				RedirectURIs: []string{ -					"https://client.example.com", -				}, -				TokenEndpointAuthMethod:     oidc.ClientAuthMethodClientSecretJWT, -				TokenEndpointAuthSigningAlg: oidc.SigningAlgHMACUsingSHA512, -			}, -			{ -				ID:                  rs256, -				AuthorizationPolicy: authorization.OneFactor.String(), -				RedirectURIs: []string{ -					"https://client.example.com", -				}, -				TokenEndpointAuthMethod:     oidc.ClientAuthMethodPrivateKeyJWT, -				TokenEndpointAuthSigningAlg: oidc.SigningAlgRSAUsingSHA256, -			}, -			{ -				ID:                  "rs384", -				AuthorizationPolicy: authorization.OneFactor.String(), -				RedirectURIs: []string{ -					"https://client.example.com", -				}, -				TokenEndpointAuthMethod:     oidc.ClientAuthMethodPrivateKeyJWT, -				TokenEndpointAuthSigningAlg: oidc.SigningAlgRSAUsingSHA384, -			}, -			{ -				ID:                  "rs512", -				AuthorizationPolicy: authorization.OneFactor.String(), -				RedirectURIs: []string{ -					"https://client.example.com", -				}, -				TokenEndpointAuthMethod:     oidc.ClientAuthMethodPrivateKeyJWT, -				TokenEndpointAuthSigningAlg: oidc.SigningAlgRSAUsingSHA512, -			}, -			{ -				ID:                  "ps256", -				AuthorizationPolicy: authorization.OneFactor.String(), -				RedirectURIs: []string{ -					"https://client.example.com", -				}, -				TokenEndpointAuthMethod:     oidc.ClientAuthMethodPrivateKeyJWT, -				TokenEndpointAuthSigningAlg: oidc.SigningAlgRSAPSSUsingSHA256, -			}, -			{ -				ID:                  "ps384", -				AuthorizationPolicy: authorization.OneFactor.String(), -				RedirectURIs: []string{ -					"https://client.example.com", -				}, -				TokenEndpointAuthMethod:     oidc.ClientAuthMethodPrivateKeyJWT, -				TokenEndpointAuthSigningAlg: oidc.SigningAlgRSAPSSUsingSHA384, -			}, -			{ -				ID:                  "ps512", -				AuthorizationPolicy: authorization.OneFactor.String(), -				RedirectURIs: []string{ -					"https://client.example.com", -				}, -				TokenEndpointAuthMethod:     oidc.ClientAuthMethodPrivateKeyJWT, -				TokenEndpointAuthSigningAlg: oidc.SigningAlgRSAPSSUsingSHA512, -			}, -			{ -				ID:                  "es256", -				AuthorizationPolicy: authorization.OneFactor.String(), -				RedirectURIs: []string{ -					"https://client.example.com", -				}, -				TokenEndpointAuthMethod:     oidc.ClientAuthMethodPrivateKeyJWT, -				TokenEndpointAuthSigningAlg: oidc.SigningAlgECDSAUsingP256AndSHA256, -			}, -			{ -				ID:                  "es384", -				AuthorizationPolicy: authorization.OneFactor.String(), -				RedirectURIs: []string{ -					"https://client.example.com", -				}, -				TokenEndpointAuthMethod:     oidc.ClientAuthMethodPrivateKeyJWT, -				TokenEndpointAuthSigningAlg: oidc.SigningAlgECDSAUsingP384AndSHA384, -			}, -			{ -				ID:                  es512, -				AuthorizationPolicy: authorization.OneFactor.String(), -				RedirectURIs: []string{ -					"https://client.example.com", -				}, -				TokenEndpointAuthMethod:     oidc.ClientAuthMethodPrivateKeyJWT, -				TokenEndpointAuthSigningAlg: oidc.SigningAlgECDSAUsingP521AndSHA512, -			}, - -			{ -				ID:                  "rs256k", -				AuthorizationPolicy: authorization.OneFactor.String(), -				RedirectURIs: []string{ -					"https://client.example.com", -				}, -				TokenEndpointAuthMethod:     oidc.ClientAuthMethodPrivateKeyJWT, -				TokenEndpointAuthSigningAlg: oidc.SigningAlgRSAUsingSHA256, -				JSONWebKeys: []schema.JWK{ -					{KeyID: rs256, Key: x509PrivateKeyRSA2048.PublicKey, Algorithm: oidc.SigningAlgRSAUsingSHA256, Use: oidc.KeyUseSignature}, -				}, -			}, -			{ -				ID:                  "rs384k", -				AuthorizationPolicy: authorization.OneFactor.String(), -				RedirectURIs: []string{ -					"https://client.example.com", -				}, -				TokenEndpointAuthMethod:     oidc.ClientAuthMethodPrivateKeyJWT, -				TokenEndpointAuthSigningAlg: oidc.SigningAlgRSAUsingSHA384, -				JSONWebKeys: []schema.JWK{ -					{KeyID: "rs384", Key: x509PrivateKeyRSA2048.PublicKey, Algorithm: oidc.SigningAlgRSAUsingSHA384, Use: oidc.KeyUseSignature}, -				}, -			}, -			{ -				ID:                  "rs512k", -				AuthorizationPolicy: authorization.OneFactor.String(), -				RedirectURIs: []string{ -					"https://client.example.com", -				}, -				TokenEndpointAuthMethod:     oidc.ClientAuthMethodPrivateKeyJWT, -				TokenEndpointAuthSigningAlg: oidc.SigningAlgRSAUsingSHA512, -				JSONWebKeys: []schema.JWK{ -					{KeyID: "rs512", Key: x509PrivateKeyRSA2048.PublicKey, Algorithm: oidc.SigningAlgRSAUsingSHA512, Use: oidc.KeyUseSignature}, -				}, -			}, -			{ -				ID:                  "ps256k", -				AuthorizationPolicy: authorization.OneFactor.String(), -				RedirectURIs: []string{ -					"https://client.example.com", -				}, -				TokenEndpointAuthMethod:     oidc.ClientAuthMethodPrivateKeyJWT, -				TokenEndpointAuthSigningAlg: oidc.SigningAlgRSAPSSUsingSHA256, -				JSONWebKeys: []schema.JWK{ -					{KeyID: "ps256", Key: x509PrivateKeyRSA2048.PublicKey, Algorithm: oidc.SigningAlgRSAPSSUsingSHA256, Use: oidc.KeyUseSignature}, -				}, -			}, -			{ -				ID:                  "ps384k", -				AuthorizationPolicy: authorization.OneFactor.String(), -				RedirectURIs: []string{ -					"https://client.example.com", -				}, -				TokenEndpointAuthMethod:     oidc.ClientAuthMethodPrivateKeyJWT, -				TokenEndpointAuthSigningAlg: oidc.SigningAlgRSAPSSUsingSHA384, -				JSONWebKeys: []schema.JWK{ -					{KeyID: "ps384", Key: x509PrivateKeyRSA2048.PublicKey, Algorithm: oidc.SigningAlgRSAPSSUsingSHA384, Use: oidc.KeyUseSignature}, -				}, -			}, -			{ -				ID:                  "ps512k", -				AuthorizationPolicy: authorization.OneFactor.String(), -				RedirectURIs: []string{ -					"https://client.example.com", -				}, -				TokenEndpointAuthMethod:     oidc.ClientAuthMethodPrivateKeyJWT, -				TokenEndpointAuthSigningAlg: oidc.SigningAlgRSAPSSUsingSHA512, -				JSONWebKeys: []schema.JWK{ -					{KeyID: "ps512", Key: x509PrivateKeyRSA2048.PublicKey, Algorithm: oidc.SigningAlgRSAPSSUsingSHA512, Use: oidc.KeyUseSignature}, -				}, -			}, -			{ -				ID:                  "es256k", -				AuthorizationPolicy: authorization.OneFactor.String(), -				RedirectURIs: []string{ -					"https://client.example.com", -				}, -				TokenEndpointAuthMethod:     oidc.ClientAuthMethodPrivateKeyJWT, -				TokenEndpointAuthSigningAlg: oidc.SigningAlgECDSAUsingP256AndSHA256, -				JSONWebKeys: []schema.JWK{ -					{KeyID: "es256", Key: x509PrivateKeyECDSAP256.PublicKey, Algorithm: oidc.SigningAlgECDSAUsingP256AndSHA256, Use: oidc.KeyUseSignature}, -				}, -			}, -			{ -				ID:                  "es384k", -				AuthorizationPolicy: authorization.OneFactor.String(), -				RedirectURIs: []string{ -					"https://client.example.com", -				}, -				TokenEndpointAuthMethod:     oidc.ClientAuthMethodPrivateKeyJWT, -				TokenEndpointAuthSigningAlg: oidc.SigningAlgECDSAUsingP384AndSHA384, -				JSONWebKeys: []schema.JWK{ -					{KeyID: "es384", Key: x509PrivateKeyECDSAP384.PublicKey, Algorithm: oidc.SigningAlgECDSAUsingP384AndSHA384, Use: oidc.KeyUseSignature}, -				}, -			}, -			{ -				ID:                  "es512k", -				AuthorizationPolicy: authorization.OneFactor.String(), -				RedirectURIs: []string{ -					"https://client.example.com", -				}, -				TokenEndpointAuthMethod:     oidc.ClientAuthMethodPrivateKeyJWT, -				TokenEndpointAuthSigningAlg: oidc.SigningAlgECDSAUsingP521AndSHA512, -				JSONWebKeys: []schema.JWK{ -					{KeyID: es512, Key: x509PrivateKeyECDSAP521.PublicKey, Algorithm: oidc.SigningAlgECDSAUsingP521AndSHA512, Use: oidc.KeyUseSignature}, -				}, -			}, -			{ -				ID:                  "mismatched-alg", -				AuthorizationPolicy: authorization.OneFactor.String(), -				RedirectURIs: []string{ -					"https://client.example.com", -				}, -				TokenEndpointAuthMethod:     oidc.ClientAuthMethodPrivateKeyJWT, -				TokenEndpointAuthSigningAlg: oidc.SigningAlgRSAUsingSHA256, -				JSONWebKeys: []schema.JWK{ -					{KeyID: es512, Key: x509PrivateKeyECDSAP521.PublicKey, Algorithm: oidc.SigningAlgECDSAUsingP521AndSHA512, Use: oidc.KeyUseSignature}, -				}, -			}, -			{ -				ID:                  "no-key", -				AuthorizationPolicy: authorization.OneFactor.String(), -				RedirectURIs: []string{ -					"https://client.example.com", -				}, -				TokenEndpointAuthMethod:     oidc.ClientAuthMethodPrivateKeyJWT, -				TokenEndpointAuthSigningAlg: oidc.SigningAlgRSAUsingSHA256, -				JSONWebKeys:                 []schema.JWK{}, -			}, -			{ -				ID:                  "es512u", -				AuthorizationPolicy: authorization.OneFactor.String(), -				RedirectURIs: []string{ -					"https://client.example.com", -				}, -				TokenEndpointAuthMethod:     oidc.ClientAuthMethodPrivateKeyJWT, -				TokenEndpointAuthSigningAlg: oidc.SigningAlgECDSAUsingP521AndSHA512, -				JSONWebKeys: []schema.JWK{ -					{KeyID: es512, Key: x509PrivateKeyECDSAP521.PublicKey, Algorithm: oidc.SigningAlgECDSAUsingP521AndSHA512, Use: "enc"}, -				}, -			}, -			{ -				ID:                  "hs5122", -				Secret:              secret, -				AuthorizationPolicy: authorization.OneFactor.String(), -				RedirectURIs: []string{ -					"https://client.example.com", -				}, -				TokenEndpointAuthMethod:     oidc.ClientAuthMethodClientSecretJWT, -				TokenEndpointAuthSigningAlg: oidc.SigningAlgHMACUsingSHA512, -			}, -			{ -				ID:                  "hashed", -				Secret:              MustDecodeSecret("$pbkdf2-sha512$310000$c8p78n7pUMln0jzvd4aK4Q$JNRBzwAo0ek5qKn50cFzzvE9RXV88h1wJn5KGiHrD0YKtZaR/nCb2CJPOsKaPK0hjf.9yHxzQGZziziccp6Yng"), -				AuthorizationPolicy: authorization.OneFactor.String(), -				RedirectURIs: []string{ -					"https://client.example.com", -				}, -				TokenEndpointAuthMethod:     oidc.ClientAuthMethodClientSecretJWT, -				TokenEndpointAuthSigningAlg: oidc.SigningAlgHMACUsingSHA512, -			}, -			{ -				ID:                  oidc.ClientAuthMethodClientSecretBasic, -				Secret:              secret, -				AuthorizationPolicy: authorization.OneFactor.String(), -				RedirectURIs: []string{ -					"https://client.example.com", -				}, -				TokenEndpointAuthMethod:     oidc.ClientAuthMethodClientSecretBasic, -				TokenEndpointAuthSigningAlg: oidc.SigningAlgNone, -			}, -			{ -				ID:                  oidc.ClientAuthMethodNone, -				Secret:              secret, -				AuthorizationPolicy: authorization.OneFactor.String(), -				RedirectURIs: []string{ -					"https://client.example.com", -				}, -				TokenEndpointAuthMethod:     oidc.ClientAuthMethodNone, -				TokenEndpointAuthSigningAlg: oidc.SigningAlgNone, -			}, -			{ -				ID:                  oidc.ClientAuthMethodClientSecretPost, -				Secret:              secret, -				AuthorizationPolicy: authorization.OneFactor.String(), -				RedirectURIs: []string{ -					"https://client.example.com", -				}, -				TokenEndpointAuthMethod:     oidc.ClientAuthMethodClientSecretPost, -				TokenEndpointAuthSigningAlg: oidc.SigningAlgNone, -			}, -			{ -				ID:                  "bad_method", -				Secret:              secret, -				AuthorizationPolicy: authorization.OneFactor.String(), -				RedirectURIs: []string{ -					"https://client.example.com", -				}, -				TokenEndpointAuthMethod:     "bad_method", -				TokenEndpointAuthSigningAlg: oidc.SigningAlgNone, -			}, -			{ -				ID:                  "base", -				Secret:              secret, -				AuthorizationPolicy: authorization.OneFactor.String(), -				RedirectURIs: []string{ -					"https://client.example.com", -				}, -			}, -			{ -				ID:                      "public", -				Public:                  true, -				AuthorizationPolicy:     authorization.OneFactor.String(), -				TokenEndpointAuthMethod: oidc.ClientAuthMethodNone, -				RedirectURIs: []string{ -					"https://client.example.com", -				}, -			}, -			{ -				ID:                  "public-nomethod", -				Public:              true, -				AuthorizationPolicy: authorization.OneFactor.String(), -				RedirectURIs: []string{ -					"https://client.example.com", -				}, -			}, -			{ -				ID:                      "public-basic", -				Public:                  true, -				AuthorizationPolicy:     authorization.OneFactor.String(), -				TokenEndpointAuthMethod: oidc.ClientAuthMethodClientSecretBasic, -				RedirectURIs: []string{ -					"https://client.example.com", -				}, -			}, -			{ -				ID:                      "public-post", -				Public:                  true, -				AuthorizationPolicy:     authorization.OneFactor.String(), -				TokenEndpointAuthMethod: oidc.ClientAuthMethodClientSecretPost, -				RedirectURIs: []string{ -					"https://client.example.com", -				}, -			}, -			{ -				ID:                      "confidential-none", -				AuthorizationPolicy:     authorization.OneFactor.String(), -				TokenEndpointAuthMethod: oidc.ClientAuthMethodNone, -				RedirectURIs: []string{ -					"https://client.example.com", -				}, -			}, -		}, -	}, s.store, nil) - -	c, _ := s.provider.Store.GetFullClient(context.TODO(), "no-key") -	client := c.(*oidc.RegisteredClient) - -	client.SetJSONWebKeys(&jose.JSONWebKeySet{}) -} - -func (s *ClientAuthenticationStrategySuite) TestShouldValidateAssertionHS256() { -	assertion := NewAssertion("hs256", s.GetTokenURL(), time.Now().Add(time.Second*-3), time.Unix(time.Now().Add(time.Minute).Unix(), 0)) - -	assertionJWT := jwt.NewWithClaims(jwt.SigningMethodHS256, assertion) - -	token, err := assertionJWT.SignedString([]byte("client-secret")) - -	s.Require().NoError(oidc.ErrorToDebugRFC6749Error(err)) -	s.Require().NotEqual("", token) - -	r := s.GetAssertionRequest(token) - -	sig := fmt.Sprintf("%x", sha256.Sum256([]byte(assertion.ID))) - -	ctx := s.GetCtx() - -	gomock.InOrder( -		s.store. -			EXPECT().LoadOAuth2BlacklistedJTI(ctx, sig). -			Return(nil, sql.ErrNoRows), - -		s.store. -			EXPECT().SaveOAuth2BlacklistedJTI(ctx, model.OAuth2BlacklistedJTI{Signature: sig, ExpiresAt: assertion.ExpiresAt.Time}). -			Return(nil), -	) - -	client, err := s.provider.DefaultClientAuthenticationStrategy(ctx, r, r.PostForm) - -	s.NoError(oidc.ErrorToDebugRFC6749Error(err)) -	s.Require().NotNil(client) -	s.Equal("hs256", client.GetID()) -} - -func (s *ClientAuthenticationStrategySuite) TestShouldValidateAssertionHS384() { -	assertion := NewAssertion("hs384", s.GetTokenURL(), time.Now().Add(time.Second*-3), time.Unix(time.Now().Add(time.Minute).Unix(), 0)) - -	assertionJWT := jwt.NewWithClaims(jwt.SigningMethodHS384, assertion) - -	token, err := assertionJWT.SignedString([]byte("client-secret")) - -	s.Require().NoError(oidc.ErrorToDebugRFC6749Error(err)) -	s.Require().NotEqual("", token) - -	r := s.GetAssertionRequest(token) - -	sig := fmt.Sprintf("%x", sha256.Sum256([]byte(assertion.ID))) - -	ctx := s.GetCtx() - -	gomock.InOrder( -		s.store. -			EXPECT().LoadOAuth2BlacklistedJTI(ctx, sig). -			Return(nil, sql.ErrNoRows), - -		s.store. -			EXPECT().SaveOAuth2BlacklistedJTI(ctx, model.OAuth2BlacklistedJTI{Signature: sig, ExpiresAt: assertion.ExpiresAt.Time}). -			Return(nil), -	) - -	client, err := s.provider.DefaultClientAuthenticationStrategy(ctx, r, r.PostForm) - -	s.NoError(oidc.ErrorToDebugRFC6749Error(err)) -	s.Require().NotNil(client) -	s.Equal("hs384", client.GetID()) -} - -func (s *ClientAuthenticationStrategySuite) TestShouldValidateAssertionHS512() { -	assertion := NewAssertion("hs512", s.GetTokenURL(), time.Now().Add(time.Second*-3), time.Unix(time.Now().Add(time.Minute).Unix(), 0)) - -	assertionJWT := jwt.NewWithClaims(jwt.SigningMethodHS512, assertion) - -	token, err := assertionJWT.SignedString([]byte("client-secret")) - -	s.Require().NoError(oidc.ErrorToDebugRFC6749Error(err)) -	s.Require().NotEqual("", token) - -	r := s.GetAssertionRequest(token) - -	sig := fmt.Sprintf("%x", sha256.Sum256([]byte(assertion.ID))) - -	ctx := s.GetCtx() - -	gomock.InOrder( -		s.store. -			EXPECT().LoadOAuth2BlacklistedJTI(ctx, sig). -			Return(nil, sql.ErrNoRows), - -		s.store. -			EXPECT().SaveOAuth2BlacklistedJTI(ctx, model.OAuth2BlacklistedJTI{Signature: sig, ExpiresAt: assertion.ExpiresAt.Time}). -			Return(nil), -	) - -	client, err := s.provider.DefaultClientAuthenticationStrategy(ctx, r, r.PostForm) - -	s.NoError(oidc.ErrorToDebugRFC6749Error(err)) -	s.Require().NotNil(client) -	s.Equal("hs512", client.GetID()) -} - -func (s *ClientAuthenticationStrategySuite) TestShouldRaiseErrorOnMismatchedAssertionAuthMethodPrivateKeyJWT() { -	assertion := NewAssertion(rs256, s.GetTokenURL(), time.Now().Add(time.Second*-3), time.Unix(time.Now().Add(time.Minute).Unix(), 0)) - -	assertionJWT := jwt.NewWithClaims(jwt.SigningMethodHS512, assertion) - -	token, err := assertionJWT.SignedString([]byte("client-secret")) - -	s.Require().NoError(oidc.ErrorToDebugRFC6749Error(err)) -	s.Require().NotEqual("", token) - -	r := s.GetAssertionRequest(token) - -	client, err := s.provider.DefaultClientAuthenticationStrategy(s.GetCtx(), r, r.PostForm) - -	s.EqualError(oidc.ErrorToDebugRFC6749Error(err), "Client authentication failed (e.g., unknown client, no client authentication included, or unsupported authentication method). This requested OAuth 2.0 client supports client authentication method 'private_key_jwt', however the 'HS512' JWA is not supported with this method.") -	s.Nil(client) -} - -func (s *ClientAuthenticationStrategySuite) TestShouldRaiseErrorOnInvalidIssuerValue() { -	assertion := NewAssertionMapClaims("hs512", 123, s.GetTokenURL(), &jwt.NumericDate{Time: time.Now().Add(time.Second * -3)}, &jwt.NumericDate{Time: time.Unix(time.Now().Add(time.Minute).Unix(), 0)}) - -	assertionJWT := jwt.NewWithClaims(jwt.SigningMethodHS512, assertion) - -	token, err := assertionJWT.SignedString([]byte("client-secret")) - -	s.Require().NoError(oidc.ErrorToDebugRFC6749Error(err)) -	s.Require().NotEqual("", token) - -	r := s.GetAssertionRequest(token) - -	client, err := s.provider.DefaultClientAuthenticationStrategy(s.GetCtx(), r, r.PostForm) - -	s.EqualError(oidc.ErrorToDebugRFC6749Error(err), "Client authentication failed (e.g., unknown client, no client authentication included, or unsupported authentication method). Claim 'iss' from 'client_assertion' is invalid.") -	s.Nil(client) -} - -func (s *ClientAuthenticationStrategySuite) TestShouldRaiseErrorOnInvalidSubjectValue() { -	assertion := NewAssertionMapClaims(123, "hs512", s.GetTokenURL(), &jwt.NumericDate{Time: time.Now().Add(time.Second * -3)}, &jwt.NumericDate{Time: time.Unix(time.Now().Add(time.Minute).Unix(), 0)}) - -	assertionJWT := jwt.NewWithClaims(jwt.SigningMethodHS512, assertion) - -	token, err := assertionJWT.SignedString([]byte("client-secret")) - -	s.Require().NoError(oidc.ErrorToDebugRFC6749Error(err)) -	s.Require().NotEqual("", token) - -	r := s.GetAssertionRequest(token) - -	client, err := s.provider.DefaultClientAuthenticationStrategy(s.GetCtx(), r, r.PostForm) - -	s.EqualError(oidc.ErrorToDebugRFC6749Error(err), "Client authentication failed (e.g., unknown client, no client authentication included, or unsupported authentication method). Claim 'sub' from 'client_assertion' is invalid.") -	s.Nil(client) -} - -func (s *ClientAuthenticationStrategySuite) TestShouldRaiseErrorOnInvalidJTIValue() { -	assertion := NewAssertionMapClaims("hs512", "hs512", s.GetTokenURL(), &jwt.NumericDate{Time: time.Now().Add(time.Second * -3)}, &jwt.NumericDate{Time: time.Unix(time.Now().Add(time.Minute).Unix(), 0)}) - -	assertion[oidc.ClaimJWTID] = 123 - -	assertionJWT := jwt.NewWithClaims(jwt.SigningMethodHS512, assertion) - -	token, err := assertionJWT.SignedString([]byte("client-secret")) - -	s.Require().NoError(oidc.ErrorToDebugRFC6749Error(err)) -	s.Require().NotEqual("", token) - -	r := s.GetAssertionRequest(token) - -	client, err := s.provider.DefaultClientAuthenticationStrategy(s.GetCtx(), r, r.PostForm) - -	s.EqualError(oidc.ErrorToDebugRFC6749Error(err), "Client authentication failed (e.g., unknown client, no client authentication included, or unsupported authentication method). Claim 'jti' from 'client_assertion' is invalid.") -	s.Nil(client) -} - -func (s *ClientAuthenticationStrategySuite) TestShouldRaiseErrorOnMismatchedAssertionAuthMethodClientSecretJWT() { -	assertion := NewAssertion("hs256", s.GetTokenURL(), time.Now().Add(time.Second*-3), time.Unix(time.Now().Add(time.Minute).Unix(), 0)) - -	assertionJWT := jwt.NewWithClaims(jwt.SigningMethodPS256, assertion) - -	token, err := assertionJWT.SignedString(x509PrivateKeyRSA2048) - -	s.Require().NoError(oidc.ErrorToDebugRFC6749Error(err)) -	s.Require().NotEqual("", token) - -	r := s.GetAssertionRequest(token) - -	client, err := s.provider.DefaultClientAuthenticationStrategy(s.GetCtx(), r, r.PostForm) - -	s.EqualError(oidc.ErrorToDebugRFC6749Error(err), "Client authentication failed (e.g., unknown client, no client authentication included, or unsupported authentication method). This requested OAuth 2.0 client supports client authentication method 'client_secret_jwt', however the 'PS256' JWA is not supported with this method.") -	s.Nil(client) -} - -func (s *ClientAuthenticationStrategySuite) TestShouldRaiseErrorOnMismatchedAlg() { -	assertion := NewAssertion(rs256, s.GetTokenURL(), time.Now().Add(time.Second*-3), time.Unix(time.Now().Add(time.Minute).Unix(), 0)) - -	assertionJWT := jwt.NewWithClaims(jwt.SigningMethodPS256, assertion) - -	token, err := assertionJWT.SignedString(x509PrivateKeyRSA2048) - -	s.Require().NoError(oidc.ErrorToDebugRFC6749Error(err)) -	s.Require().NotEqual("", token) - -	r := s.GetAssertionRequest(token) - -	client, err := s.provider.DefaultClientAuthenticationStrategy(s.GetCtx(), r, r.PostForm) - -	s.EqualError(oidc.ErrorToDebugRFC6749Error(err), "Client authentication failed (e.g., unknown client, no client authentication included, or unsupported authentication method). The 'client_assertion' uses signing algorithm 'PS256' but the requested OAuth 2.0 Client enforces signing algorithm 'RS256'.") -	s.Nil(client) -} - -func (s *ClientAuthenticationStrategySuite) TestShouldRaiseErrorOnClientAssertionWithClientSecret() { -	assertion := NewAssertion(rs256, s.GetTokenURL(), time.Now().Add(time.Second*-3), time.Unix(time.Now().Add(time.Minute).Unix(), 0)) - -	assertionJWT := jwt.NewWithClaims(jwt.SigningMethodRS256, assertion) - -	token, err := assertionJWT.SignedString(x509PrivateKeyRSA2048) - -	s.Require().NoError(oidc.ErrorToDebugRFC6749Error(err)) -	s.Require().NotEqual("", token) - -	r := s.GetAssertionRequest(token) -	r.PostForm.Set(oidc.FormParameterClientSecret, "abc123") - -	client, err := s.provider.DefaultClientAuthenticationStrategy(s.GetCtx(), r, r.PostForm) - -	s.EqualError(oidc.ErrorToDebugRFC6749Error(err), "The request is missing a required parameter, includes an invalid parameter value, includes a parameter more than once, or is otherwise malformed. The client_secret request parameter must not be set when using client_assertion_type of 'urn:ietf:params:oauth:client-assertion-type:jwt-bearer'.") -	s.Nil(client) -} - -func (s *ClientAuthenticationStrategySuite) TestShouldRaiseErrorOnClientAssertionWithAuthorizationHeader() { -	assertion := NewAssertion(rs256, s.GetTokenURL(), time.Now().Add(time.Second*-3), time.Unix(time.Now().Add(time.Minute).Unix(), 0)) - -	assertionJWT := jwt.NewWithClaims(jwt.SigningMethodRS256, assertion) - -	token, err := assertionJWT.SignedString(x509PrivateKeyRSA2048) - -	s.Require().NoError(oidc.ErrorToDebugRFC6749Error(err)) -	s.Require().NotEqual("", token) - -	r := s.GetAssertionRequest(token) -	r.Header.Set(fasthttp.HeaderAuthorization, "abc123") - -	client, err := s.provider.DefaultClientAuthenticationStrategy(s.GetCtx(), r, r.PostForm) - -	s.EqualError(oidc.ErrorToDebugRFC6749Error(err), "The request is missing a required parameter, includes an invalid parameter value, includes a parameter more than once, or is otherwise malformed. The Authorization request header must not be set when using client_assertion_type of 'urn:ietf:params:oauth:client-assertion-type:jwt-bearer'.") -	s.Nil(client) -} - -func (s *ClientAuthenticationStrategySuite) TestShouldRaiseErrorOnMismatchedAlgSameMethod() { -	assertion := NewAssertion("hs256", s.GetTokenURL(), time.Now().Add(time.Second*-3), time.Unix(time.Now().Add(time.Minute).Unix(), 0)) - -	assertionJWT := jwt.NewWithClaims(jwt.SigningMethodHS512, assertion) - -	token, err := assertionJWT.SignedString([]byte("client-secret")) - -	s.Require().NoError(oidc.ErrorToDebugRFC6749Error(err)) -	s.Require().NotEqual("", token) - -	r := s.GetAssertionRequest(token) - -	client, err := s.provider.DefaultClientAuthenticationStrategy(s.GetCtx(), r, r.PostForm) - -	s.EqualError(oidc.ErrorToDebugRFC6749Error(err), "Client authentication failed (e.g., unknown client, no client authentication included, or unsupported authentication method). The 'client_assertion' uses signing algorithm 'HS512' but the requested OAuth 2.0 Client enforces signing algorithm 'HS256'.") -	s.Nil(client) -} - -func (s *ClientAuthenticationStrategySuite) TestShouldRaiseErrorOnUnregisteredKeysRS256() { -	assertion := NewAssertion(rs256, s.GetTokenURL(), time.Now().Add(time.Second*-3), time.Unix(time.Now().Add(time.Minute).Unix(), 0)) - -	assertionJWT := jwt.NewWithClaims(jwt.SigningMethodRS256, assertion) - -	token, err := assertionJWT.SignedString(x509PrivateKeyRSA2048) - -	s.Require().NoError(oidc.ErrorToDebugRFC6749Error(err)) -	s.Require().NotEqual("", token) - -	r := s.GetAssertionRequest(token) - -	client, err := s.provider.DefaultClientAuthenticationStrategy(s.GetCtx(), r, r.PostForm) - -	s.EqualError(oidc.ErrorToDebugRFC6749Error(err), "Client authentication failed (e.g., unknown client, no client authentication included, or unsupported authentication method). The OAuth 2.0 Client has no JSON Web Keys set registered, but they are needed to complete the request.") -	s.Nil(client) -} - -func (s *ClientAuthenticationStrategySuite) TestShouldRaiseErrorOnBadAlgRS256() { -	assertion := NewAssertion("rs256k", s.GetTokenURL(), time.Now().Add(time.Second*-3), time.Unix(time.Now().Add(time.Minute).Unix(), 0)) - -	assertionJWT := jwt.NewWithClaims(jwt.SigningMethodPS256, assertion) -	assertionJWT.Header[oidc.JWTHeaderKeyIdentifier] = rs256 - -	token, err := assertionJWT.SignedString(x509PrivateKeyRSA2048) -	s.Require().NoError(oidc.ErrorToDebugRFC6749Error(err)) -	s.Require().NotEqual("", token) - -	r := s.GetAssertionRequest(token) - -	client, err := s.provider.DefaultClientAuthenticationStrategy(s.GetCtx(), r, r.PostForm) - -	s.EqualError(oidc.ErrorToDebugRFC6749Error(err), "Client authentication failed (e.g., unknown client, no client authentication included, or unsupported authentication method). The 'client_assertion' uses signing algorithm 'PS256' but the requested OAuth 2.0 Client enforces signing algorithm 'RS256'.") -	s.Nil(client) -} - -func (s *ClientAuthenticationStrategySuite) TestShouldRaiseErrorOnBadKidRS256() { -	assertion := NewAssertion("rs256k", s.GetTokenURL(), time.Now().Add(time.Second*-3), time.Unix(time.Now().Add(time.Minute).Unix(), 0)) - -	assertionJWT := jwt.NewWithClaims(jwt.SigningMethodRS256, assertion) -	assertionJWT.Header[oidc.JWTHeaderKeyIdentifier] = "nokey" - -	token, err := assertionJWT.SignedString(x509PrivateKeyRSA2048) -	s.Require().NoError(oidc.ErrorToDebugRFC6749Error(err)) -	s.Require().NotEqual("", token) - -	r := s.GetAssertionRequest(token) - -	client, err := s.provider.DefaultClientAuthenticationStrategy(s.GetCtx(), r, r.PostForm) - -	s.EqualError(oidc.ErrorToDebugRFC6749Error(err), "The request is missing a required parameter, includes an invalid parameter value, includes a parameter more than once, or is otherwise malformed. The JSON Web Token uses signing key with kid 'nokey', which could not be found.") -	s.Nil(client) -} - -func (s *ClientAuthenticationStrategySuite) TestShouldRaiseErrorOnBadTypRS256() { -	assertion := NewAssertion("rs256k", s.GetTokenURL(), time.Now().Add(time.Second*-3), time.Unix(time.Now().Add(time.Minute).Unix(), 0)) - -	assertionJWT := jwt.NewWithClaims(jwt.SigningMethodES256, assertion) -	assertionJWT.Header[oidc.JWTHeaderKeyIdentifier] = rs256 - -	token, err := assertionJWT.SignedString(x509PrivateKeyECDSAP256) -	s.Require().NoError(oidc.ErrorToDebugRFC6749Error(err)) -	s.Require().NotEqual("", token) - -	r := s.GetAssertionRequest(token) - -	client, err := s.provider.DefaultClientAuthenticationStrategy(s.GetCtx(), r, r.PostForm) - -	s.EqualError(oidc.ErrorToDebugRFC6749Error(err), "Client authentication failed (e.g., unknown client, no client authentication included, or unsupported authentication method). The 'client_assertion' uses signing algorithm 'ES256' but the requested OAuth 2.0 Client enforces signing algorithm 'RS256'.") -	s.Nil(client) -} - -func (s *ClientAuthenticationStrategySuite) TestShouldAuthKeysRS256() { -	assertion := NewAssertion("rs256k", s.GetTokenURL(), time.Now().Add(time.Second*-3), time.Unix(time.Now().Add(time.Minute).Unix(), 0)) - -	assertionJWT := jwt.NewWithClaims(jwt.SigningMethodRS256, assertion) -	assertionJWT.Header[oidc.JWTHeaderKeyIdentifier] = rs256 - -	token, err := assertionJWT.SignedString(x509PrivateKeyRSA2048) -	s.Require().NoError(oidc.ErrorToDebugRFC6749Error(err)) -	s.Require().NotEqual("", token) - -	r := s.GetAssertionRequest(token) - -	sig := fmt.Sprintf("%x", sha256.Sum256([]byte(assertion.ID))) - -	ctx := s.GetCtx() - -	gomock.InOrder( -		s.store. -			EXPECT().LoadOAuth2BlacklistedJTI(ctx, sig). -			Return(nil, sql.ErrNoRows), - -		s.store. -			EXPECT().SaveOAuth2BlacklistedJTI(ctx, model.OAuth2BlacklistedJTI{Signature: sig, ExpiresAt: assertion.ExpiresAt.Time}). -			Return(nil), -	) - -	client, err := s.provider.DefaultClientAuthenticationStrategy(s.GetCtx(), r, r.PostForm) - -	s.NoError(oidc.ErrorToDebugRFC6749Error(err)) -	s.Require().NotNil(client) - -	s.Equal("rs256k", client.GetID()) -} - -func (s *ClientAuthenticationStrategySuite) TestShouldRaiseErrorOnUnregisteredKeysRS384() { -	assertion := NewAssertion("rs384", s.GetTokenURL(), time.Now().Add(time.Second*-3), time.Unix(time.Now().Add(time.Minute).Unix(), 0)) - -	assertionJWT := jwt.NewWithClaims(jwt.SigningMethodRS384, assertion) - -	token, err := assertionJWT.SignedString(x509PrivateKeyRSA2048) - -	s.Require().NoError(oidc.ErrorToDebugRFC6749Error(err)) -	s.Require().NotEqual("", token) - -	r := s.GetAssertionRequest(token) - -	client, err := s.provider.DefaultClientAuthenticationStrategy(s.GetCtx(), r, r.PostForm) - -	s.EqualError(oidc.ErrorToDebugRFC6749Error(err), "Client authentication failed (e.g., unknown client, no client authentication included, or unsupported authentication method). The OAuth 2.0 Client has no JSON Web Keys set registered, but they are needed to complete the request.") -	s.Nil(client) -} - -func (s *ClientAuthenticationStrategySuite) TestShouldAuthKeysRS384() { -	assertion := NewAssertion("rs384k", s.GetTokenURL(), time.Now().Add(time.Second*-3), time.Unix(time.Now().Add(time.Minute).Unix(), 0)) - -	assertionJWT := jwt.NewWithClaims(jwt.SigningMethodRS384, assertion) -	assertionJWT.Header[oidc.JWTHeaderKeyIdentifier] = "rs384" - -	token, err := assertionJWT.SignedString(x509PrivateKeyRSA2048) -	s.Require().NoError(oidc.ErrorToDebugRFC6749Error(err)) -	s.Require().NotEqual("", token) - -	r := s.GetAssertionRequest(token) - -	sig := fmt.Sprintf("%x", sha256.Sum256([]byte(assertion.ID))) - -	ctx := s.GetCtx() - -	gomock.InOrder( -		s.store. -			EXPECT().LoadOAuth2BlacklistedJTI(ctx, sig). -			Return(nil, sql.ErrNoRows), - -		s.store. -			EXPECT().SaveOAuth2BlacklistedJTI(ctx, model.OAuth2BlacklistedJTI{Signature: sig, ExpiresAt: assertion.ExpiresAt.Time}). -			Return(nil), -	) - -	client, err := s.provider.DefaultClientAuthenticationStrategy(s.GetCtx(), r, r.PostForm) - -	s.NoError(oidc.ErrorToDebugRFC6749Error(err)) -	s.Require().NotNil(client) - -	s.Equal("rs384k", client.GetID()) -} - -func (s *ClientAuthenticationStrategySuite) TestShouldRaiseErrorOnUnregisteredKeysRS512() { -	assertion := NewAssertion("rs512", s.GetTokenURL(), time.Now().Add(time.Second*-3), time.Unix(time.Now().Add(time.Minute).Unix(), 0)) - -	assertionJWT := jwt.NewWithClaims(jwt.SigningMethodRS512, assertion) - -	token, err := assertionJWT.SignedString(x509PrivateKeyRSA2048) - -	s.Require().NoError(oidc.ErrorToDebugRFC6749Error(err)) -	s.Require().NotEqual("", token) - -	r := s.GetAssertionRequest(token) - -	client, err := s.provider.DefaultClientAuthenticationStrategy(s.GetCtx(), r, r.PostForm) - -	s.EqualError(oidc.ErrorToDebugRFC6749Error(err), "Client authentication failed (e.g., unknown client, no client authentication included, or unsupported authentication method). The OAuth 2.0 Client has no JSON Web Keys set registered, but they are needed to complete the request.") -	s.Nil(client) -} - -func (s *ClientAuthenticationStrategySuite) TestShouldAuthKeysRS512() { -	assertion := NewAssertion("rs512k", s.GetTokenURL(), time.Now().Add(time.Second*-3), time.Unix(time.Now().Add(time.Minute).Unix(), 0)) - -	assertionJWT := jwt.NewWithClaims(jwt.SigningMethodRS512, assertion) -	assertionJWT.Header[oidc.JWTHeaderKeyIdentifier] = "rs512" - -	token, err := assertionJWT.SignedString(x509PrivateKeyRSA2048) -	s.Require().NoError(oidc.ErrorToDebugRFC6749Error(err)) -	s.Require().NotEqual("", token) - -	r := s.GetAssertionRequest(token) - -	sig := fmt.Sprintf("%x", sha256.Sum256([]byte(assertion.ID))) - -	ctx := s.GetCtx() - -	gomock.InOrder( -		s.store. -			EXPECT().LoadOAuth2BlacklistedJTI(ctx, sig). -			Return(nil, sql.ErrNoRows), - -		s.store. -			EXPECT().SaveOAuth2BlacklistedJTI(ctx, model.OAuth2BlacklistedJTI{Signature: sig, ExpiresAt: assertion.ExpiresAt.Time}). -			Return(nil), -	) - -	client, err := s.provider.DefaultClientAuthenticationStrategy(s.GetCtx(), r, r.PostForm) - -	s.NoError(oidc.ErrorToDebugRFC6749Error(err)) -	s.Require().NotNil(client) - -	s.Equal("rs512k", client.GetID()) -} - -func (s *ClientAuthenticationStrategySuite) TestShouldRaiseErrorOnUnregisteredKeysPS256() { -	assertion := NewAssertion("ps256", s.GetTokenURL(), time.Now().Add(time.Second*-3), time.Unix(time.Now().Add(time.Minute).Unix(), 0)) - -	assertionJWT := jwt.NewWithClaims(jwt.SigningMethodPS256, assertion) - -	token, err := assertionJWT.SignedString(x509PrivateKeyRSA2048) - -	s.Require().NoError(oidc.ErrorToDebugRFC6749Error(err)) -	s.Require().NotEqual("", token) - -	r := s.GetAssertionRequest(token) - -	client, err := s.provider.DefaultClientAuthenticationStrategy(s.GetCtx(), r, r.PostForm) - -	s.EqualError(oidc.ErrorToDebugRFC6749Error(err), "Client authentication failed (e.g., unknown client, no client authentication included, or unsupported authentication method). The OAuth 2.0 Client has no JSON Web Keys set registered, but they are needed to complete the request.") -	s.Nil(client) -} - -func (s *ClientAuthenticationStrategySuite) TestShouldAuthKeysPS256() { -	assertion := NewAssertion("ps256k", s.GetTokenURL(), time.Now().Add(time.Second*-3), time.Unix(time.Now().Add(time.Minute).Unix(), 0)) - -	assertionJWT := jwt.NewWithClaims(jwt.SigningMethodPS256, assertion) -	assertionJWT.Header[oidc.JWTHeaderKeyIdentifier] = "ps256" - -	token, err := assertionJWT.SignedString(x509PrivateKeyRSA2048) -	s.Require().NoError(oidc.ErrorToDebugRFC6749Error(err)) -	s.Require().NotEqual("", token) - -	r := s.GetAssertionRequest(token) - -	sig := fmt.Sprintf("%x", sha256.Sum256([]byte(assertion.ID))) - -	ctx := s.GetCtx() - -	gomock.InOrder( -		s.store. -			EXPECT().LoadOAuth2BlacklistedJTI(ctx, sig). -			Return(nil, sql.ErrNoRows), - -		s.store. -			EXPECT().SaveOAuth2BlacklistedJTI(ctx, model.OAuth2BlacklistedJTI{Signature: sig, ExpiresAt: assertion.ExpiresAt.Time}). -			Return(nil), -	) - -	client, err := s.provider.DefaultClientAuthenticationStrategy(s.GetCtx(), r, r.PostForm) - -	s.NoError(oidc.ErrorToDebugRFC6749Error(err)) -	s.Require().NotNil(client) - -	s.Equal("ps256k", client.GetID()) -} - -func (s *ClientAuthenticationStrategySuite) TestShouldRaiseErrorOnUnregisteredKeysPS384() { -	assertion := NewAssertion("ps384", s.GetTokenURL(), time.Now().Add(time.Second*-3), time.Unix(time.Now().Add(time.Minute).Unix(), 0)) - -	assertionJWT := jwt.NewWithClaims(jwt.SigningMethodPS384, assertion) - -	token, err := assertionJWT.SignedString(x509PrivateKeyRSA2048) - -	s.Require().NoError(oidc.ErrorToDebugRFC6749Error(err)) -	s.Require().NotEqual("", token) - -	r := s.GetAssertionRequest(token) - -	client, err := s.provider.DefaultClientAuthenticationStrategy(s.GetCtx(), r, r.PostForm) - -	s.EqualError(oidc.ErrorToDebugRFC6749Error(err), "Client authentication failed (e.g., unknown client, no client authentication included, or unsupported authentication method). The OAuth 2.0 Client has no JSON Web Keys set registered, but they are needed to complete the request.") -	s.Nil(client) -} - -func (s *ClientAuthenticationStrategySuite) TestShouldAuthKeysPS384() { -	assertion := NewAssertion("ps384k", s.GetTokenURL(), time.Now().Add(time.Second*-3), time.Unix(time.Now().Add(time.Minute).Unix(), 0)) - -	assertionJWT := jwt.NewWithClaims(jwt.SigningMethodPS384, assertion) -	assertionJWT.Header[oidc.JWTHeaderKeyIdentifier] = "ps384" - -	token, err := assertionJWT.SignedString(x509PrivateKeyRSA2048) -	s.Require().NoError(oidc.ErrorToDebugRFC6749Error(err)) -	s.Require().NotEqual("", token) - -	r := s.GetAssertionRequest(token) - -	sig := fmt.Sprintf("%x", sha256.Sum256([]byte(assertion.ID))) - -	ctx := s.GetCtx() - -	gomock.InOrder( -		s.store. -			EXPECT().LoadOAuth2BlacklistedJTI(ctx, sig). -			Return(nil, sql.ErrNoRows), - -		s.store. -			EXPECT().SaveOAuth2BlacklistedJTI(ctx, model.OAuth2BlacklistedJTI{Signature: sig, ExpiresAt: assertion.ExpiresAt.Time}). -			Return(nil), -	) - -	client, err := s.provider.DefaultClientAuthenticationStrategy(s.GetCtx(), r, r.PostForm) - -	s.NoError(oidc.ErrorToDebugRFC6749Error(err)) -	s.Require().NotNil(client) - -	s.Equal("ps384k", client.GetID()) -} - -func (s *ClientAuthenticationStrategySuite) TestShouldRaiseErrorOnUnregisteredKeysPS512() { -	assertion := NewAssertion("ps512", s.GetTokenURL(), time.Now().Add(time.Second*-3), time.Unix(time.Now().Add(time.Minute).Unix(), 0)) - -	assertionJWT := jwt.NewWithClaims(jwt.SigningMethodPS512, assertion) - -	token, err := assertionJWT.SignedString(x509PrivateKeyRSA2048) - -	s.Require().NoError(oidc.ErrorToDebugRFC6749Error(err)) -	s.Require().NotEqual("", token) - -	r := s.GetAssertionRequest(token) - -	client, err := s.provider.DefaultClientAuthenticationStrategy(s.GetCtx(), r, r.PostForm) - -	s.EqualError(oidc.ErrorToDebugRFC6749Error(err), "Client authentication failed (e.g., unknown client, no client authentication included, or unsupported authentication method). The OAuth 2.0 Client has no JSON Web Keys set registered, but they are needed to complete the request.") -	s.Nil(client) -} - -func (s *ClientAuthenticationStrategySuite) TestShouldAuthKeysPS512() { -	assertion := NewAssertion("ps512k", s.GetTokenURL(), time.Now().Add(time.Second*-3), time.Unix(time.Now().Add(time.Minute).Unix(), 0)) - -	assertionJWT := jwt.NewWithClaims(jwt.SigningMethodPS512, assertion) -	assertionJWT.Header[oidc.JWTHeaderKeyIdentifier] = "ps512" - -	token, err := assertionJWT.SignedString(x509PrivateKeyRSA2048) -	s.Require().NoError(oidc.ErrorToDebugRFC6749Error(err)) -	s.Require().NotEqual("", token) - -	r := s.GetAssertionRequest(token) - -	sig := fmt.Sprintf("%x", sha256.Sum256([]byte(assertion.ID))) - -	ctx := s.GetCtx() - -	gomock.InOrder( -		s.store. -			EXPECT().LoadOAuth2BlacklistedJTI(ctx, sig). -			Return(nil, sql.ErrNoRows), - -		s.store. -			EXPECT().SaveOAuth2BlacklistedJTI(ctx, model.OAuth2BlacklistedJTI{Signature: sig, ExpiresAt: assertion.ExpiresAt.Time}). -			Return(nil), -	) - -	client, err := s.provider.DefaultClientAuthenticationStrategy(s.GetCtx(), r, r.PostForm) - -	s.NoError(oidc.ErrorToDebugRFC6749Error(err)) -	s.Require().NotNil(client) - -	s.Equal("ps512k", client.GetID()) -} - -func (s *ClientAuthenticationStrategySuite) TestShouldRaiseErrorOnUnregisteredKeysES256() { -	assertion := NewAssertion("es256", s.GetTokenURL(), time.Now().Add(time.Second*-3), time.Unix(time.Now().Add(time.Minute).Unix(), 0)) - -	assertionJWT := jwt.NewWithClaims(jwt.SigningMethodES256, assertion) - -	token, err := assertionJWT.SignedString(x509PrivateKeyECDSAP256) - -	s.Require().NoError(oidc.ErrorToDebugRFC6749Error(err)) -	s.Require().NotEqual("", token) - -	r := s.GetAssertionRequest(token) - -	client, err := s.provider.DefaultClientAuthenticationStrategy(s.GetCtx(), r, r.PostForm) - -	s.EqualError(oidc.ErrorToDebugRFC6749Error(err), "Client authentication failed (e.g., unknown client, no client authentication included, or unsupported authentication method). The OAuth 2.0 Client has no JSON Web Keys set registered, but they are needed to complete the request.") -	s.Nil(client) -} - -func (s *ClientAuthenticationStrategySuite) TestShouldAuthKeysES256() { -	assertion := NewAssertion("es256k", s.GetTokenURL(), time.Now().Add(time.Second*-3), time.Unix(time.Now().Add(time.Minute).Unix(), 0)) - -	assertionJWT := jwt.NewWithClaims(jwt.SigningMethodES256, assertion) -	assertionJWT.Header[oidc.JWTHeaderKeyIdentifier] = "es256" - -	token, err := assertionJWT.SignedString(x509PrivateKeyECDSAP256) - -	s.Require().NoError(oidc.ErrorToDebugRFC6749Error(err)) -	s.Require().NotEqual("", token) - -	r := s.GetAssertionRequest(token) - -	sig := fmt.Sprintf("%x", sha256.Sum256([]byte(assertion.ID))) - -	ctx := s.GetCtx() - -	gomock.InOrder( -		s.store. -			EXPECT().LoadOAuth2BlacklistedJTI(ctx, sig). -			Return(nil, sql.ErrNoRows), - -		s.store. -			EXPECT().SaveOAuth2BlacklistedJTI(ctx, model.OAuth2BlacklistedJTI{Signature: sig, ExpiresAt: assertion.ExpiresAt.Time}). -			Return(nil), -	) - -	client, err := s.provider.DefaultClientAuthenticationStrategy(s.GetCtx(), r, r.PostForm) - -	s.NoError(oidc.ErrorToDebugRFC6749Error(err)) -	s.Require().NotNil(client) - -	s.Equal("es256k", client.GetID()) -} - -func (s *ClientAuthenticationStrategySuite) TestShouldRaiseErrorOnUnregisteredKeysES384() { -	assertion := NewAssertion("es384", s.GetTokenURL(), time.Now().Add(time.Second*-3), time.Unix(time.Now().Add(time.Minute).Unix(), 0)) - -	assertionJWT := jwt.NewWithClaims(jwt.SigningMethodES384, assertion) - -	token, err := assertionJWT.SignedString(x509PrivateKeyECDSAP384) - -	s.Require().NoError(oidc.ErrorToDebugRFC6749Error(err)) -	s.Require().NotEqual("", token) - -	r := s.GetAssertionRequest(token) - -	client, err := s.provider.DefaultClientAuthenticationStrategy(s.GetCtx(), r, r.PostForm) - -	s.EqualError(oidc.ErrorToDebugRFC6749Error(err), "Client authentication failed (e.g., unknown client, no client authentication included, or unsupported authentication method). The OAuth 2.0 Client has no JSON Web Keys set registered, but they are needed to complete the request.") -	s.Nil(client) -} - -func (s *ClientAuthenticationStrategySuite) TestShouldAuthKeysES384() { -	assertion := NewAssertion("es384k", s.GetTokenURL(), time.Now().Add(time.Second*-3), time.Unix(time.Now().Add(time.Minute).Unix(), 0)) - -	assertionJWT := jwt.NewWithClaims(jwt.SigningMethodES384, assertion) -	assertionJWT.Header[oidc.JWTHeaderKeyIdentifier] = "es384" - -	token, err := assertionJWT.SignedString(x509PrivateKeyECDSAP384) -	s.Require().NoError(oidc.ErrorToDebugRFC6749Error(err)) -	s.Require().NotEqual("", token) - -	r := s.GetAssertionRequest(token) - -	sig := fmt.Sprintf("%x", sha256.Sum256([]byte(assertion.ID))) - -	ctx := s.GetCtx() - -	gomock.InOrder( -		s.store. -			EXPECT().LoadOAuth2BlacklistedJTI(ctx, sig). -			Return(nil, sql.ErrNoRows), - -		s.store. -			EXPECT().SaveOAuth2BlacklistedJTI(ctx, model.OAuth2BlacklistedJTI{Signature: sig, ExpiresAt: assertion.ExpiresAt.Time}). -			Return(nil), -	) - -	client, err := s.provider.DefaultClientAuthenticationStrategy(s.GetCtx(), r, r.PostForm) - -	s.NoError(oidc.ErrorToDebugRFC6749Error(err)) -	s.Require().NotNil(client) - -	s.Equal("es384k", client.GetID()) -} - -func (s *ClientAuthenticationStrategySuite) TestShouldRaiseErrorOnUnregisteredKeysES512() { -	assertion := NewAssertion(es512, s.GetTokenURL(), time.Now().Add(time.Second*-3), time.Unix(time.Now().Add(time.Minute).Unix(), 0)) - -	assertionJWT := jwt.NewWithClaims(jwt.SigningMethodES512, assertion) - -	token, err := assertionJWT.SignedString(x509PrivateKeyECDSAP521) - -	s.Require().NoError(oidc.ErrorToDebugRFC6749Error(err)) -	s.Require().NotEqual("", token) - -	r := s.GetAssertionRequest(token) - -	client, err := s.provider.DefaultClientAuthenticationStrategy(s.GetCtx(), r, r.PostForm) - -	s.EqualError(oidc.ErrorToDebugRFC6749Error(err), "Client authentication failed (e.g., unknown client, no client authentication included, or unsupported authentication method). The OAuth 2.0 Client has no JSON Web Keys set registered, but they are needed to complete the request.") -	s.Nil(client) -} - -func (s *ClientAuthenticationStrategySuite) TestShouldAuthKeysES512() { -	assertion := NewAssertion("es512k", s.GetTokenURL(), time.Now().Add(time.Second*-3), time.Unix(time.Now().Add(time.Minute).Unix(), 0)) - -	assertionJWT := jwt.NewWithClaims(jwt.SigningMethodES512, assertion) -	assertionJWT.Header[oidc.JWTHeaderKeyIdentifier] = es512 - -	token, err := assertionJWT.SignedString(x509PrivateKeyECDSAP521) -	s.Require().NoError(oidc.ErrorToDebugRFC6749Error(err)) -	s.Require().NotEqual("", token) - -	r := s.GetAssertionRequest(token) - -	sig := fmt.Sprintf("%x", sha256.Sum256([]byte(assertion.ID))) - -	ctx := s.GetCtx() - -	gomock.InOrder( -		s.store. -			EXPECT().LoadOAuth2BlacklistedJTI(ctx, sig). -			Return(nil, sql.ErrNoRows), - -		s.store. -			EXPECT().SaveOAuth2BlacklistedJTI(ctx, model.OAuth2BlacklistedJTI{Signature: sig, ExpiresAt: assertion.ExpiresAt.Time}). -			Return(nil), -	) - -	client, err := s.provider.DefaultClientAuthenticationStrategy(s.GetCtx(), r, r.PostForm) - -	s.NoError(oidc.ErrorToDebugRFC6749Error(err)) -	s.Require().NotNil(client) - -	s.Equal("es512k", client.GetID()) -} - -func (s *ClientAuthenticationStrategySuite) TestShouldFailKeysES512Enc() { -	assertion := NewAssertion("es512u", s.GetTokenURL(), time.Now().Add(time.Second*-3), time.Unix(time.Now().Add(time.Minute).Unix(), 0)) - -	assertionJWT := jwt.NewWithClaims(jwt.SigningMethodES512, assertion) -	assertionJWT.Header[oidc.JWTHeaderKeyIdentifier] = es512 - -	token, err := assertionJWT.SignedString(x509PrivateKeyECDSAP521) -	s.Require().NoError(oidc.ErrorToDebugRFC6749Error(err)) -	s.Require().NotEqual("", token) - -	r := s.GetAssertionRequest(token) - -	client, err := s.provider.DefaultClientAuthenticationStrategy(s.GetCtx(), r, r.PostForm) - -	s.EqualError(oidc.ErrorToDebugRFC6749Error(err), "The request is missing a required parameter, includes an invalid parameter value, includes a parameter more than once, or is otherwise malformed. Unable to find ECDSA public key with a 'use' value of 'sig' for kid 'es512' in JSON Web Key Set.") -	s.Require().Nil(client) -} - -func (s *ClientAuthenticationStrategySuite) TestShouldFailKeysRS256MismatchedKIDAndAlg() { -	assertion := NewAssertion("mismatched-alg", s.GetTokenURL(), time.Now().Add(time.Second*-3), time.Unix(time.Now().Add(time.Minute).Unix(), 0)) - -	assertionJWT := jwt.NewWithClaims(jwt.SigningMethodRS256, assertion) -	assertionJWT.Header[oidc.JWTHeaderKeyIdentifier] = es512 - -	token, err := assertionJWT.SignedString(x509PrivateKeyRSA2048) -	s.Require().NoError(oidc.ErrorToDebugRFC6749Error(err)) -	s.Require().NotEqual("", token) - -	r := s.GetAssertionRequest(token) - -	client, err := s.provider.DefaultClientAuthenticationStrategy(s.GetCtx(), r, r.PostForm) - -	s.EqualError(oidc.ErrorToDebugRFC6749Error(err), "The request is missing a required parameter, includes an invalid parameter value, includes a parameter more than once, or is otherwise malformed. Unable to find RSA public key with a 'use' value of 'sig' for kid 'es512' in JSON Web Key Set.") -	s.Require().Nil(client) -} - -func (s *ClientAuthenticationStrategySuite) TestShouldFailWithoutKeys() { -	assertion := NewAssertion("no-key", s.GetTokenURL(), time.Now().Add(time.Second*-3), time.Unix(time.Now().Add(time.Minute).Unix(), 0)) - -	assertionJWT := jwt.NewWithClaims(jwt.SigningMethodRS256, assertion) -	assertionJWT.Header[oidc.JWTHeaderKeyIdentifier] = "rs256" - -	token, err := assertionJWT.SignedString(x509PrivateKeyRSA2048) -	s.Require().NoError(oidc.ErrorToDebugRFC6749Error(err)) -	s.Require().NotEqual("", token) - -	r := s.GetAssertionRequest(token) - -	client, err := s.provider.DefaultClientAuthenticationStrategy(s.GetCtx(), r, r.PostForm) - -	s.EqualError(oidc.ErrorToDebugRFC6749Error(err), "The request is missing a required parameter, includes an invalid parameter value, includes a parameter more than once, or is otherwise malformed. The retrieved JSON Web Key Set does not contain any key.") -	s.Require().Nil(client) -} - -func (s *ClientAuthenticationStrategySuite) TestShouldRaiseErrorOnJTIKnown() { -	assertion := NewAssertion("hs512", s.GetTokenURL(), time.Now().Add(time.Second*-3), time.Unix(time.Now().Add(time.Minute).Unix(), 0)) - -	assertionJWT := jwt.NewWithClaims(jwt.SigningMethodHS512, assertion) - -	token, err := assertionJWT.SignedString([]byte("client-secret")) - -	s.Require().NoError(oidc.ErrorToDebugRFC6749Error(err)) -	s.Require().NotEqual("", token) - -	r := s.GetAssertionRequest(token) - -	sig := fmt.Sprintf("%x", sha256.Sum256([]byte(assertion.ID))) - -	ctx := s.GetCtx() - -	gomock.InOrder( -		s.store. -			EXPECT().LoadOAuth2BlacklistedJTI(ctx, sig). -			Return(nil, sql.ErrNoRows), - -		s.store. -			EXPECT().SaveOAuth2BlacklistedJTI(ctx, model.OAuth2BlacklistedJTI{Signature: sig, ExpiresAt: assertion.ExpiresAt.Time}). -			Return(fosite.ErrJTIKnown), -	) - -	client, err := s.provider.DefaultClientAuthenticationStrategy(ctx, r, r.PostForm) - -	s.EqualError(oidc.ErrorToDebugRFC6749Error(err), "The jti was already used.") -	s.Nil(client) -} - -func (s *ClientAuthenticationStrategySuite) TestShouldValidateJWTWithArbitraryClaims() { -	assertion := NewAssertion("hs512", s.GetTokenURL(), time.Now().Add(time.Second*-3), time.Unix(time.Now().Add(time.Minute).Unix(), 0)) - -	a := assertion.ToMapClaims() -	a["aaa"] = abc - -	assertionJWT := jwt.NewWithClaims(jwt.SigningMethodHS512, a) - -	token, err := assertionJWT.SignedString([]byte("client-secret")) - -	s.Require().NoError(oidc.ErrorToDebugRFC6749Error(err)) -	s.Require().NotEqual("", token) - -	r := s.GetAssertionRequest(token) - -	sig := fmt.Sprintf("%x", sha256.Sum256([]byte(assertion.ID))) - -	ctx := s.GetCtx() - -	gomock.InOrder( -		s.store. -			EXPECT().LoadOAuth2BlacklistedJTI(ctx, sig). -			Return(nil, sql.ErrNoRows), - -		s.store. -			EXPECT().SaveOAuth2BlacklistedJTI(ctx, model.OAuth2BlacklistedJTI{Signature: sig, ExpiresAt: assertion.ExpiresAt.Time}). -			Return(nil), -	) - -	client, err := s.provider.DefaultClientAuthenticationStrategy(ctx, r, r.PostForm) - -	s.NoError(oidc.ErrorToDebugRFC6749Error(err)) -	s.Require().NotNil(client) -	s.Equal("hs512", client.GetID()) -} - -func (s *ClientAuthenticationStrategySuite) TestShouldFailWithMissingSubClaim() { -	assertion := NewAssertion("hs512", s.GetTokenURL(), time.Now().Add(time.Second*-3), time.Unix(time.Now().Add(time.Minute).Unix(), 0)) - -	a := assertion.ToMapClaims() -	delete(a, oidc.ClaimSubject) - -	assertionJWT := jwt.NewWithClaims(jwt.SigningMethodHS512, a) - -	token, err := assertionJWT.SignedString([]byte("client-secret")) - -	s.Require().NoError(oidc.ErrorToDebugRFC6749Error(err)) -	s.Require().NotEqual("", token) - -	r := s.GetAssertionRequest(token) - -	client, err := s.provider.DefaultClientAuthenticationStrategy(s.GetCtx(), r, r.PostForm) - -	s.EqualError(oidc.ErrorToDebugRFC6749Error(err), "Client authentication failed (e.g., unknown client, no client authentication included, or unsupported authentication method). Claim 'sub' from 'client_assertion' must match the 'client_id' of the OAuth 2.0 Client. The claim 'sub' with value '' did not match the 'client_id' with value 'hs512'.") -	s.Nil(client) -} - -func (s *ClientAuthenticationStrategySuite) TestShouldFailWithInvalidExpClaim() { -	assertion := NewAssertion("hs512", s.GetTokenURL(), time.Now().Add(time.Second*-3), time.Unix(time.Now().Add(time.Minute).Unix(), 0)) - -	a := assertion.ToMapClaims() -	a[oidc.ClaimExpirationTime] = "not a number" - -	assertionJWT := jwt.NewWithClaims(jwt.SigningMethodHS512, a) - -	token, err := assertionJWT.SignedString([]byte("client-secret")) - -	s.Require().NoError(oidc.ErrorToDebugRFC6749Error(err)) -	s.Require().NotEqual("", token) - -	r := s.GetAssertionRequest(token) - -	client, err := s.provider.DefaultClientAuthenticationStrategy(s.GetCtx(), r, r.PostForm) - -	s.EqualError(oidc.ErrorToDebugRFC6749Error(err), "Client authentication failed (e.g., unknown client, no client authentication included, or unsupported authentication method). Unable to verify the integrity of the 'client_assertion' value. The token claims are invalid.") -	s.Nil(client) -} - -func (s *ClientAuthenticationStrategySuite) TestShouldFailWithMissingIssClaim() { -	assertion := NewAssertion("hs512", s.GetTokenURL(), time.Now().Add(time.Second*-3), time.Unix(time.Now().Add(time.Minute).Unix(), 0)) - -	a := assertion.ToMapClaims() -	delete(a, oidc.ClaimIssuer) - -	assertionJWT := jwt.NewWithClaims(jwt.SigningMethodHS512, a) - -	token, err := assertionJWT.SignedString([]byte("client-secret")) - -	s.Require().NoError(oidc.ErrorToDebugRFC6749Error(err)) -	s.Require().NotEqual("", token) - -	r := s.GetAssertionRequest(token) - -	client, err := s.provider.DefaultClientAuthenticationStrategy(s.GetCtx(), r, r.PostForm) - -	s.EqualError(oidc.ErrorToDebugRFC6749Error(err), "Client authentication failed (e.g., unknown client, no client authentication included, or unsupported authentication method). Claim 'iss' from 'client_assertion' must match the 'client_id' of the OAuth 2.0 Client. The claim 'iss' with value '' did not match the 'client_id' with value 'hs512'.") -	s.Nil(client) -} - -func (s *ClientAuthenticationStrategySuite) TestShouldFailWithInvalidAudClaim() { -	assertion := NewAssertion("hs512", s.GetTokenURL(), time.Now().Add(time.Second*-3), time.Unix(time.Now().Add(time.Minute).Unix(), 0)) - -	assertion.Audience = []string{"notvalid"} - -	assertionJWT := jwt.NewWithClaims(jwt.SigningMethodHS512, assertion) - -	token, err := assertionJWT.SignedString([]byte("client-secret")) - -	s.Require().NoError(oidc.ErrorToDebugRFC6749Error(err)) -	s.Require().NotEqual("", token) - -	r := s.GetAssertionRequest(token) - -	sig := fmt.Sprintf("%x", sha256.Sum256([]byte(assertion.ID))) - -	ctx := s.GetCtx() - -	gomock.InOrder( -		s.store. -			EXPECT().LoadOAuth2BlacklistedJTI(ctx, sig). -			Return(nil, sql.ErrNoRows), - -		s.store. -			EXPECT().SaveOAuth2BlacklistedJTI(ctx, model.OAuth2BlacklistedJTI{Signature: sig, ExpiresAt: assertion.ExpiresAt.Time}). -			Return(nil), -	) - -	client, err := s.provider.DefaultClientAuthenticationStrategy(s.GetCtx(), r, r.PostForm) - -	s.EqualError(oidc.ErrorToDebugRFC6749Error(err), "Client authentication failed (e.g., unknown client, no client authentication included, or unsupported authentication method). Claim 'aud' from 'client_assertion' must match the authorization server's token endpoint 'https://auth.example.com/api/oidc/token'.") -	s.Nil(client) -} - -func (s *ClientAuthenticationStrategySuite) TestShouldFailWithInvalidAssertionType() { -	assertion := NewAssertion("hs512", s.GetTokenURL(), time.Now().Add(time.Second*-3), time.Unix(time.Now().Add(time.Minute).Unix(), 0)) - -	assertionJWT := jwt.NewWithClaims(jwt.SigningMethodHS512, assertion) - -	token, err := assertionJWT.SignedString([]byte("client-secret")) - -	s.Require().NoError(oidc.ErrorToDebugRFC6749Error(err)) -	s.Require().NotEqual("", token) - -	v := s.GetAssertionValues(token) - -	v.Set(oidc.FormParameterClientAssertionType, "not_valid") - -	r := s.GetRequest(v) - -	client, err := s.provider.DefaultClientAuthenticationStrategy(s.GetCtx(), r, r.PostForm) - -	s.EqualError(oidc.ErrorToDebugRFC6749Error(err), "The request is missing a required parameter, includes an invalid parameter value, includes a parameter more than once, or is otherwise malformed. Unknown client_assertion_type 'not_valid'.") -	s.Nil(client) -} - -func (s *ClientAuthenticationStrategySuite) TestShouldFailWithMissingJTIClaim() { -	assertion := NewAssertion("hs512", s.GetTokenURL(), time.Now().Add(time.Second*-3), time.Unix(time.Now().Add(time.Minute).Unix(), 0)) - -	a := assertion.ToMapClaims() -	delete(a, oidc.ClaimJWTID) - -	assertionJWT := jwt.NewWithClaims(jwt.SigningMethodHS512, a) - -	token, err := assertionJWT.SignedString([]byte("client-secret")) - -	s.Require().NoError(oidc.ErrorToDebugRFC6749Error(err)) -	s.Require().NotEqual("", token) - -	r := s.GetAssertionRequest(token) - -	client, err := s.provider.DefaultClientAuthenticationStrategy(s.GetCtx(), r, r.PostForm) - -	s.EqualError(oidc.ErrorToDebugRFC6749Error(err), "Client authentication failed (e.g., unknown client, no client authentication included, or unsupported authentication method). Claim 'jti' from 'client_assertion' must be set but it is not.") -	s.Nil(client) -} - -func (s *ClientAuthenticationStrategySuite) TestShouldFailWithMismatchedIssClaim() { -	assertion := NewAssertion("hs512", s.GetTokenURL(), time.Now().Add(time.Second*-3), time.Unix(time.Now().Add(time.Minute).Unix(), 0)) - -	assertion.Issuer = "hs256" - -	assertionJWT := jwt.NewWithClaims(jwt.SigningMethodHS512, assertion) - -	token, err := assertionJWT.SignedString([]byte("client-secret")) - -	s.Require().NoError(oidc.ErrorToDebugRFC6749Error(err)) -	s.Require().NotEqual("", token) - -	r := s.GetAssertionRequest(token) - -	client, err := s.provider.DefaultClientAuthenticationStrategy(s.GetCtx(), r, r.PostForm) - -	s.EqualError(oidc.ErrorToDebugRFC6749Error(err), "Client authentication failed (e.g., unknown client, no client authentication included, or unsupported authentication method). Claim 'iss' from 'client_assertion' must match the 'client_id' of the OAuth 2.0 Client. The claim 'iss' with value 'hs256' did not match the 'client_id' with value 'hs512'.") -	s.Nil(client) -} - -func (s *ClientAuthenticationStrategySuite) TestShouldValidateClientSecretPost() { -	r := s.GetClientSecretPostRequest(oidc.ClientAuthMethodClientSecretPost, "client-secret") - -	client, err := s.provider.DefaultClientAuthenticationStrategy(s.GetCtx(), r, r.PostForm) - -	s.NoError(oidc.ErrorToDebugRFC6749Error(err)) -	s.Require().NotNil(client) -	s.Equal(oidc.ClientAuthMethodClientSecretPost, client.GetID()) -} - -func (s *ClientAuthenticationStrategySuite) TestShouldErrorClientSecretPostOnClientSecretBasicClient() { -	r := s.GetClientSecretPostRequest(oidc.ClientAuthMethodClientSecretBasic, "client-secret") - -	client, err := s.provider.DefaultClientAuthenticationStrategy(s.GetCtx(), r, r.PostForm) - -	s.EqualError(err, "invalid_client") -	s.EqualError(oidc.ErrorToDebugRFC6749Error(err), "Client authentication failed (e.g., unknown client, no client authentication included, or unsupported authentication method). The OAuth 2.0 Client supports client authentication method 'client_secret_basic', but method 'client_secret_post' was requested. You must configure the OAuth 2.0 client's 'token_endpoint_auth_method' value to accept 'client_secret_post'.") -	s.Nil(client) -} - -func (s *ClientAuthenticationStrategySuite) TestShouldErrorClientSecretPostWrongSecret() { -	r := s.GetClientSecretPostRequest(oidc.ClientAuthMethodClientSecretPost, "client-secret-bad") - -	client, err := s.provider.DefaultClientAuthenticationStrategy(s.GetCtx(), r, r.PostForm) - -	s.EqualError(err, "invalid_client") -	s.EqualError(oidc.ErrorToDebugRFC6749Error(err), "Client authentication failed (e.g., unknown client, no client authentication included, or unsupported authentication method). The provided client secret did not match the registered client secret.") -	s.Nil(client) -} - -func (s *ClientAuthenticationStrategySuite) TestShouldValidateClientSecretBasic() { -	r := s.GetClientSecretBasicRequest(oidc.ClientAuthMethodClientSecretBasic, "client-secret") - -	client, err := s.provider.DefaultClientAuthenticationStrategy(s.GetCtx(), r, r.PostForm) - -	s.NoError(oidc.ErrorToDebugRFC6749Error(err)) -	s.Require().NotNil(client) -	s.Equal(oidc.ClientAuthMethodClientSecretBasic, client.GetID()) -} - -func (s *ClientAuthenticationStrategySuite) TestShouldRaiseErrorOnClientSecretPostWithoutClientID() { -	r := s.GetRequest(&url.Values{oidc.FormParameterClientSecret: []string{"client-secret"}}) - -	client, err := s.provider.DefaultClientAuthenticationStrategy(s.GetCtx(), r, r.PostForm) - -	s.EqualError(err, "invalid_request") -	s.EqualError(oidc.ErrorToDebugRFC6749Error(err), "The request is missing a required parameter, includes an invalid parameter value, includes a parameter more than once, or is otherwise malformed. Client credentials missing or malformed in both HTTP Authorization header and HTTP POST body.") -	s.Nil(client) -} - -func (s *ClientAuthenticationStrategySuite) TestShouldHandleBasicAuth() { -	r := s.GetRequest(&url.Values{oidc.FormParameterRequestURI: []string{"not applicable"}}) - -	r.Header.Set(fasthttp.HeaderAuthorization, fmt.Sprintf("Basic %s", base64.StdEncoding.EncodeToString([]byte("client_secret_basic:client-secret")))) -	client, err := s.provider.DefaultClientAuthenticationStrategy(s.GetCtx(), r, r.PostForm) - -	s.NoError(oidc.ErrorToDebugRFC6749Error(err)) -	s.Require().NotNil(client) -	s.Equal("client_secret_basic", client.GetID()) -} - -func (s *ClientAuthenticationStrategySuite) TestShouldErrorWithBasicAuthBadBas64Data() { -	r := s.GetRequest(&url.Values{oidc.FormParameterRequestURI: []string{"not applicable"}}) - -	r.Header.Set(fasthttp.HeaderAuthorization, fmt.Sprintf("Basic %s", "!@#&!@*&#(!*@#(*!@#")) -	client, err := s.provider.DefaultClientAuthenticationStrategy(s.GetCtx(), r, r.PostForm) - -	s.EqualError(err, "invalid_request") -	s.Nil(client) -} - -func (s *ClientAuthenticationStrategySuite) TestShouldErrorWithBasicAuthBadBasicData() { -	r := s.GetRequest(&url.Values{oidc.FormParameterRequestURI: []string{"not applicable"}}) - -	r.Header.Set(fasthttp.HeaderAuthorization, fmt.Sprintf("Basic %s", base64.StdEncoding.EncodeToString([]byte("client_secret_basic")))) -	client, err := s.provider.DefaultClientAuthenticationStrategy(s.GetCtx(), r, r.PostForm) - -	s.EqualError(err, "invalid_request") -	s.Nil(client) -} - -func (s *ClientAuthenticationStrategySuite) TestShouldErrorWithBasicAuthNoScheme() { -	r := s.GetRequest(&url.Values{oidc.FormParameterRequestURI: []string{"not applicable"}}) - -	r.Header.Set(fasthttp.HeaderAuthorization, fmt.Sprintf("Basic%s", base64.StdEncoding.EncodeToString([]byte("client_secret_basic:client-secret")))) -	client, err := s.provider.DefaultClientAuthenticationStrategy(s.GetCtx(), r, r.PostForm) - -	s.EqualError(err, "invalid_request") -	s.Nil(client) -} - -func (s *ClientAuthenticationStrategySuite) TestShouldErrorWithBasicAuthInvalidScheme() { -	r := s.GetRequest(&url.Values{oidc.FormParameterRequestURI: []string{"not applicable"}}) - -	r.Header.Set(fasthttp.HeaderAuthorization, fmt.Sprintf("Bassic %s", base64.StdEncoding.EncodeToString([]byte("client_secret_basic:client-secret")))) -	client, err := s.provider.DefaultClientAuthenticationStrategy(s.GetCtx(), r, r.PostForm) - -	s.EqualError(err, "invalid_request") -	s.Nil(client) -} - -func (s *ClientAuthenticationStrategySuite) TestShouldErrorClientSecretBasicOnClientSecretPostClient() { -	r := s.GetClientSecretBasicRequest(oidc.ClientAuthMethodClientSecretPost, "client-secret") - -	client, err := s.provider.DefaultClientAuthenticationStrategy(s.GetCtx(), r, r.PostForm) - -	s.EqualError(err, "invalid_client") -	s.EqualError(oidc.ErrorToDebugRFC6749Error(err), "Client authentication failed (e.g., unknown client, no client authentication included, or unsupported authentication method). The OAuth 2.0 Client supports client authentication method 'client_secret_post', but method 'client_secret_basic' was requested. You must configure the OAuth 2.0 client's 'token_endpoint_auth_method' value to accept 'client_secret_basic'.") -	s.Nil(client) -} - -func (s *ClientAuthenticationStrategySuite) TestShouldErrorClientSecretBasicWrongSecret() { -	r := s.GetClientSecretBasicRequest(oidc.ClientAuthMethodClientSecretBasic, "client-secret-bad") - -	client, err := s.provider.DefaultClientAuthenticationStrategy(s.GetCtx(), r, r.PostForm) - -	s.EqualError(err, "invalid_client") -	s.EqualError(oidc.ErrorToDebugRFC6749Error(err), "Client authentication failed (e.g., unknown client, no client authentication included, or unsupported authentication method). The provided client secret did not match the registered client secret.") -	s.Nil(client) -} - -func (s *ClientAuthenticationStrategySuite) TestShouldErrorClientSecretBasicOnPublic() { -	r := s.GetClientSecretBasicRequest("public", "client-secret") - -	client, err := s.provider.DefaultClientAuthenticationStrategy(s.GetCtx(), r, r.PostForm) - -	s.EqualError(err, "invalid_client") -	s.EqualError(oidc.ErrorToDebugRFC6749Error(err), "Client authentication failed (e.g., unknown client, no client authentication included, or unsupported authentication method). The OAuth 2.0 Client supports client authentication method 'none', but method 'client_secret_basic' was requested. You must configure the OAuth 2.0 client's 'token_endpoint_auth_method' value to accept 'client_secret_basic'.") -	s.Nil(client) -} - -func (s *ClientAuthenticationStrategySuite) TestShouldErrorClientSecretBasicOnPublicWithBasic() { -	r := s.GetClientSecretBasicRequest("public-basic", "client-secret") - -	client, err := s.provider.DefaultClientAuthenticationStrategy(s.GetCtx(), r, r.PostForm) - -	s.EqualError(err, "invalid_client") -	s.EqualError(oidc.ErrorToDebugRFC6749Error(err), "Client authentication failed (e.g., unknown client, no client authentication included, or unsupported authentication method). The OAuth 2.0 Client is not a confidential client however the client authentication method 'client_secret_basic' was used which is not permitted as it's only permitted on confidential clients.") -	s.Nil(client) -} - -func (s *ClientAuthenticationStrategySuite) TestShouldErrorClientSecretPostOnPublicWithPost() { -	r := s.GetClientSecretPostRequest("public-post", "client-secret") - -	client, err := s.provider.DefaultClientAuthenticationStrategy(s.GetCtx(), r, r.PostForm) - -	s.EqualError(err, "invalid_client") -	s.EqualError(oidc.ErrorToDebugRFC6749Error(err), "Client authentication failed (e.g., unknown client, no client authentication included, or unsupported authentication method). The OAuth 2.0 Client is not a confidential client however the client authentication method 'client_secret_post' was used which is not permitted as it's only permitted on confidential clients.") -	s.Nil(client) -} - -func (s *ClientAuthenticationStrategySuite) TestShouldErrorNoneOnConfidentialWithNone() { -	r := s.GetClientSecretPostRequest("confidential-none", "") - -	client, err := s.provider.DefaultClientAuthenticationStrategy(s.GetCtx(), r, r.PostForm) - -	s.EqualError(err, "invalid_client") -	s.EqualError(oidc.ErrorToDebugRFC6749Error(err), "Client authentication failed (e.g., unknown client, no client authentication included, or unsupported authentication method). The OAuth 2.0 Client is a confidential client however the client authentication method 'none' was used which is not permitted as it's not permitted on confidential clients.") -	s.Nil(client) -} - -func (s *ClientAuthenticationStrategySuite) TestShouldErrorClientSecretBasicOnInvalidClient() { -	r := s.GetClientSecretBasicRequest("not-a-client", "client-secret") - -	client, err := s.provider.DefaultClientAuthenticationStrategy(s.GetCtx(), r, r.PostForm) - -	s.EqualError(err, "invalid_client") -	s.EqualError(oidc.ErrorToDebugRFC6749Error(err), "Client authentication failed (e.g., unknown client, no client authentication included, or unsupported authentication method). Client with id 'not-a-client' does not appear to be a registered client.") -	s.Nil(client) -} - -func (s *ClientAuthenticationStrategySuite) TestShouldValidatePublic() { -	v := s.GetClientValues("public") - -	r := s.GetRequest(v) - -	client, err := s.provider.DefaultClientAuthenticationStrategy(s.GetCtx(), r, r.PostForm) - -	s.NoError(oidc.ErrorToDebugRFC6749Error(err)) -	s.Require().NotNil(client) -	s.Equal("public", client.GetID()) -} - -func (s *ClientAuthenticationStrategySuite) TestShouldFailWithMismatchedFormClientID() { -	assertion := NewAssertion("hs512", s.GetTokenURL(), time.Now().Add(time.Second*-3), time.Unix(time.Now().Add(time.Minute).Unix(), 0)) - -	assertion.Issuer = "hs5122" - -	assertionJWT := jwt.NewWithClaims(jwt.SigningMethodHS512, assertion) - -	token, err := assertionJWT.SignedString([]byte("client-secret")) - -	s.Require().NoError(oidc.ErrorToDebugRFC6749Error(err)) -	s.Require().NotEqual("", token) - -	values := s.GetAssertionValues(token) - -	values.Set(oidc.FormParameterClientID, "hs5122") - -	r := s.GetRequest(values) - -	client, err := s.provider.DefaultClientAuthenticationStrategy(s.GetCtx(), r, r.PostForm) - -	s.EqualError(oidc.ErrorToDebugRFC6749Error(err), "Client authentication failed (e.g., unknown client, no client authentication included, or unsupported authentication method). Claim 'sub' from 'client_assertion' must match the 'client_id' of the OAuth 2.0 Client. The claim 'sub' with value 'hs512' did not match the 'client_id' with value 'hs5122'.") -	s.Nil(client) -} - -func (s *ClientAuthenticationStrategySuite) TestShouldFailWithMismatchedFormClientIDWithIss() { -	assertion := NewAssertion("hs512", s.GetTokenURL(), time.Now().Add(time.Second*-3), time.Unix(time.Now().Add(time.Minute).Unix(), 0)) - -	assertionJWT := jwt.NewWithClaims(jwt.SigningMethodHS512, assertion) - -	token, err := assertionJWT.SignedString([]byte("client-secret")) - -	s.Require().NoError(oidc.ErrorToDebugRFC6749Error(err)) -	s.Require().NotEqual("", token) - -	values := s.GetAssertionValues(token) - -	values.Set(oidc.FormParameterClientID, "hs5122") - -	r := s.GetRequest(values) - -	client, err := s.provider.DefaultClientAuthenticationStrategy(s.GetCtx(), r, r.PostForm) - -	s.EqualError(oidc.ErrorToDebugRFC6749Error(err), "Client authentication failed (e.g., unknown client, no client authentication included, or unsupported authentication method). Claim 'iss' from 'client_assertion' must match the 'client_id' of the OAuth 2.0 Client. The claim 'iss' with value 'hs512' did not match the 'client_id' with value 'hs5122'.") -	s.Nil(client) -} - -func (s *ClientAuthenticationStrategySuite) TestShouldFailWithMissingClient() { -	assertion := NewAssertion("noclient", s.GetTokenURL(), time.Now().Add(time.Second*-3), time.Unix(time.Now().Add(time.Minute).Unix(), 0)) - -	assertionJWT := jwt.NewWithClaims(jwt.SigningMethodHS512, assertion) - -	token, err := assertionJWT.SignedString([]byte("client-secret")) - -	s.Require().NoError(oidc.ErrorToDebugRFC6749Error(err)) -	s.Require().NotEqual("", token) - -	r := s.GetAssertionRequest(token) - -	client, err := s.provider.DefaultClientAuthenticationStrategy(s.GetCtx(), r, r.PostForm) - -	s.EqualError(oidc.ErrorToDebugRFC6749Error(err), "Client authentication failed (e.g., unknown client, no client authentication included, or unsupported authentication method). Client with id 'noclient' does not appear to be a registered client.") -	s.Nil(client) -} - -func (s *ClientAuthenticationStrategySuite) TestShouldFailBadSecret() { -	assertion := NewAssertion("hs512", s.GetTokenURL(), time.Now().Add(time.Second*-3), time.Unix(time.Now().Add(time.Minute).Unix(), 0)) - -	assertionJWT := jwt.NewWithClaims(jwt.SigningMethodHS512, assertion) - -	token, err := assertionJWT.SignedString([]byte("client-secret-wrong")) - -	s.Require().NoError(oidc.ErrorToDebugRFC6749Error(err)) -	s.Require().NotEqual("", token) - -	r := s.GetAssertionRequest(token) - -	ctx := s.GetCtx() - -	client, err := s.provider.DefaultClientAuthenticationStrategy(ctx, r, r.PostForm) - -	s.EqualError(err, "invalid_client") -	s.EqualError(oidc.ErrorToDebugRFC6749Error(err), "Client authentication failed (e.g., unknown client, no client authentication included, or unsupported authentication method). Unable to verify the integrity of the 'client_assertion' value. The signature is invalid.") -	s.Nil(client) -} - -func (s *ClientAuthenticationStrategySuite) TestShouldFailMethodNone() { -	assertion := NewAssertion(oidc.ClientAuthMethodNone, s.GetTokenURL(), time.Now().Add(time.Second*-3), time.Unix(time.Now().Add(time.Minute).Unix(), 0)) - -	assertionJWT := jwt.NewWithClaims(jwt.SigningMethodHS512, assertion) - -	token, err := assertionJWT.SignedString([]byte("client-secret")) - -	s.Require().NoError(oidc.ErrorToDebugRFC6749Error(err)) -	s.Require().NotEqual("", token) - -	r := s.GetAssertionRequest(token) - -	ctx := s.GetCtx() - -	client, err := s.provider.DefaultClientAuthenticationStrategy(ctx, r, r.PostForm) - -	s.EqualError(err, "invalid_client") -	s.EqualError(oidc.ErrorToDebugRFC6749Error(err), "Client authentication failed (e.g., unknown client, no client authentication included, or unsupported authentication method). This requested OAuth 2.0 client does not support client authentication, however 'client_assertion' was provided in the request.") -	s.Nil(client) -} - -func (s *ClientAuthenticationStrategySuite) TestShouldFailAssertionMethodClientSecretPost() { -	assertion := NewAssertion(oidc.ClientAuthMethodClientSecretPost, s.GetTokenURL(), time.Now().Add(time.Second*-3), time.Unix(time.Now().Add(time.Minute).Unix(), 0)) - -	assertionJWT := jwt.NewWithClaims(jwt.SigningMethodHS512, assertion) - -	token, err := assertionJWT.SignedString([]byte("client-secret")) - -	s.Require().NoError(oidc.ErrorToDebugRFC6749Error(err)) -	s.Require().NotEqual("", token) - -	r := s.GetAssertionRequest(token) - -	ctx := s.GetCtx() - -	client, err := s.provider.DefaultClientAuthenticationStrategy(ctx, r, r.PostForm) - -	s.EqualError(err, "invalid_client") -	s.EqualError(oidc.ErrorToDebugRFC6749Error(err), "Client authentication failed (e.g., unknown client, no client authentication included, or unsupported authentication method). This requested OAuth 2.0 client only supports client authentication method 'client_secret_post', however 'client_assertion' was provided in the request.") -	s.Nil(client) -} - -func (s *ClientAuthenticationStrategySuite) TestShouldFailAssertionMethodBad() { -	assertion := NewAssertion("bad_method", s.GetTokenURL(), time.Now().Add(time.Second*-3), time.Unix(time.Now().Add(time.Minute).Unix(), 0)) - -	assertionJWT := jwt.NewWithClaims(jwt.SigningMethodHS512, assertion) - -	token, err := assertionJWT.SignedString([]byte("client-secret")) - -	s.Require().NoError(oidc.ErrorToDebugRFC6749Error(err)) -	s.Require().NotEqual("", token) - -	r := s.GetAssertionRequest(token) - -	ctx := s.GetCtx() - -	client, err := s.provider.DefaultClientAuthenticationStrategy(ctx, r, r.PostForm) - -	s.EqualError(err, "invalid_client") -	s.EqualError(oidc.ErrorToDebugRFC6749Error(err), "Client authentication failed (e.g., unknown client, no client authentication included, or unsupported authentication method). This requested OAuth 2.0 client only supports client authentication method 'bad_method', however that method is not supported by this server.") -	s.Nil(client) -} - -func (s *ClientAuthenticationStrategySuite) TestShouldFailAssertionMethodClientSecretBasic() { -	assertion := NewAssertion(oidc.ClientAuthMethodClientSecretBasic, s.GetTokenURL(), time.Now().Add(time.Second*-3), time.Unix(time.Now().Add(time.Minute).Unix(), 0)) - -	assertionJWT := jwt.NewWithClaims(jwt.SigningMethodHS512, assertion) - -	token, err := assertionJWT.SignedString([]byte("client-secret")) - -	s.Require().NoError(oidc.ErrorToDebugRFC6749Error(err)) -	s.Require().NotEqual("", token) - -	r := s.GetAssertionRequest(token) - -	ctx := s.GetCtx() - -	client, err := s.provider.DefaultClientAuthenticationStrategy(ctx, r, r.PostForm) - -	s.EqualError(err, "invalid_client") -	s.EqualError(oidc.ErrorToDebugRFC6749Error(err), "Client authentication failed (e.g., unknown client, no client authentication included, or unsupported authentication method). This requested OAuth 2.0 client only supports client authentication method 'client_secret_basic', however 'client_assertion' was provided in the request.") -	s.Nil(client) -} - -func (s *ClientAuthenticationStrategySuite) TestShouldFailHashedSecret() { -	assertion := NewAssertion("hashed", s.GetTokenURL(), time.Now().Add(time.Second*-3), time.Unix(time.Now().Add(time.Minute).Unix(), 0)) - -	assertionJWT := jwt.NewWithClaims(jwt.SigningMethodHS512, assertion) - -	token, err := assertionJWT.SignedString([]byte("client-secret")) - -	s.Require().NoError(oidc.ErrorToDebugRFC6749Error(err)) -	s.Require().NotEqual("", token) - -	r := s.GetAssertionRequest(token) - -	ctx := s.GetCtx() - -	client, err := s.provider.DefaultClientAuthenticationStrategy(ctx, r, r.PostForm) - -	s.EqualError(err, "invalid_client") -	s.EqualError(oidc.ErrorToDebugRFC6749Error(err), "Client authentication failed (e.g., unknown client, no client authentication included, or unsupported authentication method). This client does not support authentication method 'client_secret_jwt' as the client secret is not in plaintext.") -	s.Nil(client) -} - -func (s *ClientAuthenticationStrategySuite) TestShouldFailExpiredToken() { -	assertion := NewAssertion("hs512", s.GetTokenURL(), time.Now().Add(time.Minute*-3), time.Unix(time.Now().Add(time.Minute*-1).Unix(), 0)) - -	assertionJWT := jwt.NewWithClaims(jwt.SigningMethodHS512, assertion) - -	token, err := assertionJWT.SignedString([]byte("client-secret")) - -	s.Require().NoError(oidc.ErrorToDebugRFC6749Error(err)) -	s.Require().NotEqual("", token) - -	r := s.GetAssertionRequest(token) - -	ctx := s.GetCtx() - -	client, err := s.provider.DefaultClientAuthenticationStrategy(ctx, r, r.PostForm) - -	s.EqualError(err, "invalid_client") -	s.EqualError(oidc.ErrorToDebugRFC6749Error(err), "Client authentication failed (e.g., unknown client, no client authentication included, or unsupported authentication method). Unable to verify the integrity of the 'client_assertion' value. The token is expired.") -	s.Nil(client) -} - -func (s *ClientAuthenticationStrategySuite) TestShouldFailNotYetValid() { -	assertion := NewAssertion("hs512", s.GetTokenURL(), time.Now().Add(time.Minute*-3), time.Unix(time.Now().Add(time.Minute*1).Unix(), 0)) - -	assertion.NotBefore = jwt.NewNumericDate(time.Now().Add(time.Second * 10)) - -	assertionJWT := jwt.NewWithClaims(jwt.SigningMethodHS512, assertion) - -	token, err := assertionJWT.SignedString([]byte("client-secret")) - -	s.Require().NoError(oidc.ErrorToDebugRFC6749Error(err)) -	s.Require().NotEqual("", token) - -	r := s.GetAssertionRequest(token) - -	s.NoError(r.ParseForm()) - -	ctx := s.GetCtx() - -	client, err := s.provider.DefaultClientAuthenticationStrategy(ctx, r, r.PostForm) - -	s.EqualError(err, "invalid_client") -	s.EqualError(oidc.ErrorToDebugRFC6749Error(err), "Client authentication failed (e.g., unknown client, no client authentication included, or unsupported authentication method). Unable to verify the integrity of the 'client_assertion' value. The token isn't valid yet.") -	s.Nil(client) -} - -func (s *ClientAuthenticationStrategySuite) TestShouldFailTokenUsedBeforeIssued() { -	assertion := NewAssertion("hs512", s.GetTokenURL(), time.Now().Add(time.Minute*3), time.Unix(time.Now().Add(time.Minute*8).Unix(), 0)) - -	assertionJWT := jwt.NewWithClaims(jwt.SigningMethodHS512, assertion) - -	token, err := assertionJWT.SignedString([]byte("client-secret")) - -	s.Require().NoError(oidc.ErrorToDebugRFC6749Error(err)) -	s.Require().NotEqual("", token) - -	r := s.GetAssertionRequest(token) - -	ctx := s.GetCtx() - -	client, err := s.provider.DefaultClientAuthenticationStrategy(ctx, r, r.PostForm) - -	s.EqualError(err, "invalid_client") -	s.EqualError(oidc.ErrorToDebugRFC6749Error(err), "Client authentication failed (e.g., unknown client, no client authentication included, or unsupported authentication method). Unable to verify the integrity of the 'client_assertion' value. The token was used before it was issued.") -	s.Nil(client) -} - -func (s *ClientAuthenticationStrategySuite) TestShouldFailMalformed() { -	r := s.GetAssertionRequest("bad token") - -	ctx := s.GetCtx() - -	client, err := s.provider.DefaultClientAuthenticationStrategy(ctx, r, r.PostForm) - -	s.EqualError(err, "invalid_client") -	s.EqualError(oidc.ErrorToDebugRFC6749Error(err), "Client authentication failed (e.g., unknown client, no client authentication included, or unsupported authentication method). Unable to verify the integrity of the 'client_assertion' value. The token is malformed.") -	s.Nil(client) -} - -func (s *ClientAuthenticationStrategySuite) TestShouldFailMissingAssertion() { -	r := s.GetAssertionRequest("") - -	ctx := s.GetCtx() - -	client, err := s.provider.DefaultClientAuthenticationStrategy(ctx, r, r.PostForm) - -	s.EqualError(err, "invalid_request") -	s.EqualError(oidc.ErrorToDebugRFC6749Error(err), "The request is missing a required parameter, includes an invalid parameter value, includes a parameter more than once, or is otherwise malformed. The 'client_assertion' request parameter must be set when using 'client_assertion_type' of 'urn:ietf:params:oauth:client-assertion-type:jwt-bearer'.") -	s.Nil(client) -} - -type RegisteredClaims struct { -	jwt.RegisteredClaims -} - -func (r *RegisteredClaims) ToMapClaims() jwt.MapClaims { -	claims := jwt.MapClaims{} - -	if r.ID != "" { -		claims[oidc.ClaimJWTID] = r.ID -	} - -	if r.Subject != "" { -		claims[oidc.ClaimSubject] = r.Subject -	} - -	if r.Issuer != "" { -		claims[oidc.ClaimIssuer] = r.Issuer -	} - -	if len(r.Audience) != 0 { -		claims[oidc.ClaimAudience] = r.Audience -	} - -	if r.NotBefore != nil { -		claims[oidc.ClaimNotBefore] = r.NotBefore -	} - -	if r.ExpiresAt != nil { -		claims[oidc.ClaimExpirationTime] = r.ExpiresAt -	} - -	if r.IssuedAt != nil { -		claims[oidc.ClaimIssuedAt] = r.IssuedAt -	} - -	return claims -} - -func NewAssertion(clientID string, tokenURL *url.URL, iat, exp time.Time) RegisteredClaims { -	return RegisteredClaims{ -		jwt.RegisteredClaims{ -			ID:     uuid.Must(uuid.NewRandom()).String(), -			Issuer: clientID, -			Audience: []string{ -				tokenURL.String(), -			}, -			Subject:   clientID, -			IssuedAt:  jwt.NewNumericDate(iat), -			ExpiresAt: jwt.NewNumericDate(exp), -		}, -	} -} - -func NewAssertionMapClaims(subject, issuer any, tokenURL *url.URL, iat, exp any) jwt.MapClaims { -	return jwt.MapClaims{ -		oidc.ClaimJWTID:          uuid.Must(uuid.NewRandom()).String(), -		oidc.ClaimIssuer:         issuer, -		oidc.ClaimSubject:        subject, -		oidc.ClaimIssuedAt:       iat, -		oidc.ClaimExpirationTime: exp, -		oidc.ClaimAudience: []string{ -			tokenURL.String(), -		}, -	} -} - -func TestPKCEHandler_HandleAuthorizeEndpointRequest_Mock(t *testing.T) { -	ctrl := gomock.NewController(t) -	store := mocks.NewMockPKCERequestStorage(ctrl) - -	defer ctrl.Finish() - -	strategy := &TestCodeStrategy{} -	config := &oidc.Config{ -		ProofKeyCodeExchange: oidc.ProofKeyCodeExchangeConfig{ -			AllowPlainChallengeMethod: true, -		}, -	} - -	handler := &oidc.PKCEHandler{Storage: store, AuthorizeCodeStrategy: strategy, Config: config} - -	request := &fosite.AuthorizeRequest{ -		ResponseTypes: fosite.Arguments{oidc.ResponseTypeAuthorizationCodeFlow}, -		Request: fosite.Request{ -			Client: &fosite.DefaultClient{ID: "test"}, -			Form: url.Values{ -				oidc.FormParameterCodeChallenge: []string{pkceVerifier}, -			}, -		}, -	} - -	response := fosite.NewAuthorizeResponse() - -	response.AddParameter(oidc.FormParameterAuthorizationCode, "abc123") - -	ctx := context.Background() - -	store.EXPECT().CreatePKCERequestSession(ctx, gomock.Any(), gomock.Any()).Return(errors.New("bad storage error!")) - -	assert.EqualError(t, oidc.ErrorToDebugRFC6749Error(handler.HandleAuthorizeEndpointRequest(ctx, request, response)), "The authorization server encountered an unexpected condition that prevented it from fulfilling the request. bad storage error!") -} - -func TestPKCEHandler_Misc(t *testing.T) { -	store := storage.NewMemoryStore() -	strategy := &TestCodeStrategy{} -	config := &oidc.Config{} - -	handler := &oidc.PKCEHandler{Storage: store, AuthorizeCodeStrategy: strategy, Config: config} - -	assert.Nil(t, handler.PopulateTokenEndpointResponse(context.TODO(), nil, nil)) -	assert.False(t, handler.CanSkipClientAuth(context.TODO(), nil)) -} - -func TestPKCEHandler_CanHandleTokenEndpointRequest(t *testing.T) { -	testCases := []struct { -		name     string -		have     fosite.AccessRequester -		expected bool -	}{ -		{ -			"ShouldHandleAuthorizeCode", -			&fosite.AccessRequest{ -				GrantTypes: fosite.Arguments{oidc.GrantTypeAuthorizationCode}, -			}, -			true, -		}, -		{ -			"ShouldNotHandleRefreshToken", -			&fosite.AccessRequest{ -				GrantTypes: fosite.Arguments{oidc.GrantTypeRefreshToken}, -			}, -			false, -		}, -		{ -			"ShouldNotHandleClientCredentials", -			&fosite.AccessRequest{ -				GrantTypes: fosite.Arguments{oidc.GrantTypeClientCredentials}, -			}, -			false, -		}, -		{ -			"ShouldNotHandleImplicit", -			&fosite.AccessRequest{ -				GrantTypes: fosite.Arguments{oidc.GrantTypeImplicit}, -			}, -			false, -		}, -	} - -	store := storage.NewMemoryStore() -	strategy := &TestCodeStrategy{} -	config := &oidc.Config{} - -	handler := &oidc.PKCEHandler{Storage: store, AuthorizeCodeStrategy: strategy, Config: config} - -	for _, tc := range testCases { -		t.Run(tc.name, func(t *testing.T) { -			assert.Equal(t, tc.expected, handler.CanHandleTokenEndpointRequest(context.TODO(), tc.have)) -		}) -	} -} - -func TestPKCEHandler_HandleAuthorizeEndpointRequest(t *testing.T) { -	store := storage.NewMemoryStore() -	strategy := &TestCodeStrategy{} -	config := &oidc.Config{} - -	client := &oidc.RegisteredClient{ID: "test"} - -	handler := &oidc.PKCEHandler{Storage: store, AuthorizeCodeStrategy: strategy, Config: config} - -	testCases := []struct { -		name                                      string -		types                                     fosite.Arguments -		enforce, enforcePublicClients, allowPlain bool -		method, challenge, code                   string -		expected                                  string -		client                                    *oidc.RegisteredClient -	}{ -		{ -			"ShouldNotHandleBlankResponseModes", -			fosite.Arguments{}, -			false, -			false, -			false, -			oidc.PKCEChallengeMethodPlain, -			"challenge", -			"", -			"", -			client, -		}, -		{ -			"ShouldHandleAuthorizeCodeFlow", -			fosite.Arguments{oidc.ResponseTypeAuthorizationCodeFlow}, -			false, -			false, -			true, -			oidc.PKCEChallengeMethodPlain, -			"challenge", -			"abc123", -			"", -			client, -		}, -		{ -			"ShouldErrorHandleAuthorizeCodeFlowWithoutCode", -			fosite.Arguments{oidc.ResponseTypeAuthorizationCodeFlow}, -			false, -			false, -			true, -			oidc.PKCEChallengeMethodPlain, -			"challenge", -			"", -			"The authorization server encountered an unexpected condition that prevented it from fulfilling the request. The PKCE handler must be loaded after the authorize code handler.", -			client, -		}, -		{ -			"ShouldErrorHandleAuthorizeCodeFlowWithoutChallengeMethodWhenEnforced", -			fosite.Arguments{oidc.ResponseTypeAuthorizationCodeFlow}, -			true, -			false, -			true, -			"", -			"", -			"abc123", -			"The request is missing a required parameter, includes an invalid parameter value, includes a parameter more than once, or is otherwise malformed. Clients must include a code_challenge when performing the authorize code flow, but it is missing. The server is configured in a way that enforces PKCE for clients.", -			client, -		}, -		{ -			"ShouldErrorHandleAuthorizeCodeFlowWithoutChallengeWhenEnforced", -			fosite.Arguments{oidc.ResponseTypeAuthorizationCodeFlow}, -			true, -			false, -			true, -			oidc.PKCEChallengeMethodPlain, -			"", -			"abc123", -			"The request is missing a required parameter, includes an invalid parameter value, includes a parameter more than once, or is otherwise malformed. Clients must include a code_challenge when performing the authorize code flow, but it is missing. The server is configured in a way that enforces PKCE for clients.", -			client, -		}, -		{ -			"ShouldSkipNotEnforced", -			fosite.Arguments{oidc.ResponseTypeAuthorizationCodeFlow}, -			false, -			false, -			true, -			"", -			"", -			"abc123", -			"", -			client, -		}, -		{ -			"ShouldErrorUnknownChallengeMethod", -			fosite.Arguments{oidc.ResponseTypeAuthorizationCodeFlow}, -			true, -			false, -			true, -			abc, -			abc, -			"abc123", -			"The request is missing a required parameter, includes an invalid parameter value, includes a parameter more than once, or is otherwise malformed. The code_challenge_method is not supported, use S256 instead.", -			client, -		}, -	} - -	for _, tc := range testCases { -		t.Run(tc.name, func(t *testing.T) { -			config.ProofKeyCodeExchange.Enforce = tc.enforce -			config.ProofKeyCodeExchange.EnforcePublicClients = tc.enforcePublicClients -			config.ProofKeyCodeExchange.AllowPlainChallengeMethod = tc.allowPlain - -			requester := fosite.NewAuthorizeRequest() - -			requester.Client = tc.client - -			if len(tc.method) > 0 { -				requester.Form.Add(oidc.FormParameterCodeChallengeMethod, tc.method) -			} - -			if len(tc.challenge) > 0 { -				requester.Form.Add(oidc.FormParameterCodeChallenge, tc.challenge) -			} - -			requester.ResponseTypes = tc.types - -			responder := fosite.NewAuthorizeResponse() - -			if len(tc.code) > 0 { -				responder.AddParameter(oidc.FormParameterAuthorizationCode, tc.code) -			} - -			err := handler.HandleAuthorizeEndpointRequest(context.TODO(), requester, responder) - -			err = oidc.ErrorToDebugRFC6749Error(err) - -			if len(tc.expected) == 0 { -				assert.NoError(t, err) -			} else { -				assert.EqualError(t, err, tc.expected) -			} -		}) -	} -} - -func TestPKCEHandler_HandleTokenEndpointRequest(t *testing.T) { -	store := storage.NewMemoryStore() -	strategy := &TestCodeStrategy{} -	config := &oidc.Config{} - -	handler := &oidc.PKCEHandler{Storage: store, AuthorizeCodeStrategy: strategy, Config: config} - -	clientConfidential := &oidc.RegisteredClient{ -		ID:     "test", -		Public: false, -	} - -	clientPublic := &oidc.RegisteredClient{ -		ID:     "test", -		Public: true, -	} - -	testCases := []struct { -		name                                      string -		grant                                     string -		enforce, enforcePublicClients, allowPlain bool -		method, challenge, verifier               string -		code                                      string -		expected                                  string -		client                                    *oidc.RegisteredClient -	}{ -		{ -			"ShouldFailNotAuthCode", -			oidc.GrantTypeClientCredentials, -			false, -			false, -			false, -			oidc.PKCEChallengeMethodSHA256, -			pkceChallenge, -			pkceVerifier, -			"code-0", -			"The handler is not responsible for this request.", -			clientConfidential, -		}, -		{ -			"ShouldPassPlainWithConfidentialClientWhenEnforcedWhenAllowPlain", -			oidc.GrantTypeAuthorizationCode, -			true, -			true, -			true, -			oidc.PKCEChallengeMethodPlain, -			"sW6e3dnNWdLMoT9rLrSMgC8xfVNuwnNdAShrGWbysqVoc8s3HK", -			"sW6e3dnNWdLMoT9rLrSMgC8xfVNuwnNdAShrGWbysqVoc8s3HK", -			"code-1", -			"", -			clientConfidential, -		}, -		{ -			"ShouldFailWithConfidentialClientWithNotAllowPlainWhenPlainWhenEnforced", -			oidc.GrantTypeAuthorizationCode, -			true, -			true, -			false, -			oidc.PKCEChallengeMethodPlain, -			"sW6e3dnNWdLMoT9rLrSMgC8xfVNuwnNdAShrGWbysqVoc8s3HK", -			"sW6e3dnNWdLMoT9rLrSMgC8xfVNuwnNdAShrGWbysqVoc8s3HK", -			"code-2", -			"The request is missing a required parameter, includes an invalid parameter value, includes a parameter more than once, or is otherwise malformed. Clients must use the 'S256' PKCE 'code_challenge_method' but the 'plain' method was requested. The server is configured in a way that enforces PKCE 'S256' as challenge method for clients.", -			clientConfidential, -		}, -		{ -			"ShouldPassWithConfidentialClientWhenNotProvidedWhenNotEnforced", -			oidc.GrantTypeAuthorizationCode, -			false, -			false, -			false, -			"", -			"", -			"", -			"code-3", -			"", -			clientConfidential, -		}, -		{ -			"ShouldPassWithPublicClientWhenNotProvidedWhenNotEnforced", -			oidc.GrantTypeAuthorizationCode, -			false, -			false, -			false, -			"", -			"", -			"", -			"code-4", -			"", -			clientPublic, -		}, -		{ -			"ShouldFailWithPublicClientWhenNotProvidedWhenNotEnforcedWhenEnforcedForPublicClients", -			oidc.GrantTypeAuthorizationCode, -			false, -			true, -			false, -			"", -			"", -			"", -			"code-5", -			"The request is missing a required parameter, includes an invalid parameter value, includes a parameter more than once, or is otherwise malformed. This client must include a code_challenge when performing the authorize code flow, but it is missing. The server is configured in a way that enforces PKCE for this client.", -			clientPublic, -		}, -		{ -			"ShouldPassS256WithConfidentialClientWhenEnforcedWhenAllowPlain", -			oidc.GrantTypeAuthorizationCode, -			true, -			true, -			true, -			oidc.PKCEChallengeMethodSHA256, -			pkceChallenge, -			pkceVerifier, -			"code-6", -			"", -			clientConfidential, -		}, -		{ -			"ShouldPassS256WithConfidentialClientWhenEnforcedWhenNotAllowPlain", -			oidc.GrantTypeAuthorizationCode, -			true, -			true, -			false, -			oidc.PKCEChallengeMethodSHA256, -			pkceChallenge, -			pkceVerifier, -			"code-7", -			"", -			clientConfidential, -		}, -		{ -			"ShouldFailS256WithConfidentialClientWhenEnforcedWhenAllowPlain", -			oidc.GrantTypeAuthorizationCode, -			true, -			true, -			true, -			oidc.PKCEChallengeMethodSHA256, -			pkceChallenge, -			"jSfm3f5nS9eD4eYSHaeQxVBVKxXnfmbWAFQiiAdMAK98EhNifm", -			"code-8", -			"The provided authorization grant (e.g., authorization code, resource owner credentials) or refresh token is invalid, expired, revoked, does not match the redirection URI used in the authorization request, or was issued to another client. The PKCE code challenge did not match the code verifier.", -			clientConfidential, -		}, -		{ -			"ShouldFailS256WithConfidentialClientWhenVerifierTooShort", -			oidc.GrantTypeAuthorizationCode, -			true, -			true, -			true, -			oidc.PKCEChallengeMethodSHA256, -			pkceChallenge, -			"aaaaaaaaaaaaa", -			"code-9", -			"The provided authorization grant (e.g., authorization code, resource owner credentials) or refresh token is invalid, expired, revoked, does not match the redirection URI used in the authorization request, or was issued to another client. The PKCE code verifier must be at least 43 characters.", -			clientConfidential, -		}, -		{ -			"ShouldFailPlainWithConfidentialClientWhenNotMatching", -			oidc.GrantTypeAuthorizationCode, -			true, -			true, -			true, -			oidc.PKCEChallengeMethodPlain, -			"jqNhtkWBNbP9oz6BjA2ufPWxADqnHJcUNhS8VNzBWLd44ynFzi", -			"uoPgkXQNCiiMzQ4aXeXdzBQaDArGNN9bke8gQWo7qZZ2djrcJZ", -			"code-10", -			"The provided authorization grant (e.g., authorization code, resource owner credentials) or refresh token is invalid, expired, revoked, does not match the redirection URI used in the authorization request, or was issued to another client. The PKCE code challenge did not match the code verifier.", -			clientConfidential, -		}, -		{ -			"ShouldPassNoPKCESessionWhenNotEnforced", -			oidc.GrantTypeAuthorizationCode, -			false, -			false, -			true, -			"", -			"", -			"", -			"code-11", -			"", -			clientConfidential, -		}, -		{ -			"ShouldFailNoPKCESessionWhenEnforced", -			oidc.GrantTypeAuthorizationCode, -			true, -			false, -			true, -			"", -			"", -			"", -			"code-12", -			"The request is missing a required parameter, includes an invalid parameter value, includes a parameter more than once, or is otherwise malformed. Clients must include a code_challenge when performing the authorize code flow, but it is missing. The server is configured in a way that enforces PKCE for clients.", -			clientConfidential, -		}, -		{ -			"ShouldFailLongVerifier", -			oidc.GrantTypeAuthorizationCode, -			true, -			false, -			true, -			oidc.PKCEChallengeMethodPlain, -			"fUcMF5287hMEieVQcjvxViWUEUGD9NjG63hELzLtSyPiETpwCjuLuZYJCMJkeAMb3wg6WRHXRzj6KSScu48J7KRDJScEAZbRXjMjR79KQavdqHLVDpv4WQra7teJvGjJfUcMF5287hMEieVQcjvxViWUEUGD9NjG63hELzLtSyPiETpwCjuLuZYJCMJkeAMb3wg6WRHXRzj6KSScu48J7KRDJScEAZbRXjMjR79KQavdqHLVDpv4WQra7teJvGjJ", -			"fUcMF5287hMEieVQcjvxViWUEUGD9NjG63hELzLtSyPiETpwCjuLuZYJCMJkeAMb3wg6WRHXRzj6KSScu48J7KRDJScEAZbRXjMjR79KQavdqHLVDpv4WQra7teJvGjJfUcMF5287hMEieVQcjvxViWUEUGD9NjG63hELzLtSyPiETpwCjuLuZYJCMJkeAMb3wg6WRHXRzj6KSScu48J7KRDJScEAZbRXjMjR79KQavdqHLVDpv4WQra7teJvGjJ", -			"code-13", -			"The provided authorization grant (e.g., authorization code, resource owner credentials) or refresh token is invalid, expired, revoked, does not match the redirection URI used in the authorization request, or was issued to another client. The PKCE code verifier can not be longer than 128 characters.", -			clientConfidential, -		}, -		{ -			"ShouldFailBadChars", -			oidc.GrantTypeAuthorizationCode, -			true, -			false, -			true, -			oidc.PKCEChallengeMethodPlain, -			"U5katcVxpTd8jU5YpUD9xdoivT55zzMWdMiy2TDA4DH9FJ5bTK45Mhd@", -			"U5katcVxpTd8jU5YpUD9xdoivT55zzMWdMiy2TDA4DH9FJ5bTK45Mhd@", -			"code-14", -			"The provided authorization grant (e.g., authorization code, resource owner credentials) or refresh token is invalid, expired, revoked, does not match the redirection URI used in the authorization request, or was issued to another client. The PKCE code verifier must only contain [a-Z], [0-9], '-', '.', '_', '~'.", -			clientConfidential, -		}, -		{ -			"ShouldFailNoChallenge", -			oidc.GrantTypeAuthorizationCode, -			false, -			false, -			true, -			oidc.PKCEChallengeMethodPlain, -			"", -			"U5katcVxpTd8jU5YpUD9xdoivT55zzMWdMiy2TDA4DH9FJ5bTK45Mhd", -			"code-15", -			"The provided authorization grant (e.g., authorization code, resource owner credentials) or refresh token is invalid, expired, revoked, does not match the redirection URI used in the authorization request, or was issued to another client. The PKCE code verifier was provided but the code challenge was absent from the authorization request.", -			clientConfidential, -		}, -		{ -			"ShouldFailNoPKCESessionWhenEnforcedNotFound", -			oidc.GrantTypeAuthorizationCode, -			true, -			false, -			true, -			oidc.PKCEChallengeMethodPlain, -			"", -			"abc123", -			"", -			"The provided authorization grant (e.g., authorization code, resource owner credentials) or refresh token is invalid, expired, revoked, does not match the redirection URI used in the authorization request, or was issued to another client. Unable to find initial PKCE data tied to this request. Could not find the requested resource(s).", -			clientConfidential, -		}, -		{ -			"ShouldFailInvalidCode", -			oidc.GrantTypeAuthorizationCode, -			true, -			false, -			true, -			oidc.PKCEChallengeMethodPlain, -			"WXNqfH6FCXcJH5oT9eqTM3HTdTh4b2aSvVe9KWkxcHCJJ3FaXF", -			"WXNqfH6FCXcJH5oT9eqTM3HTdTh4b2aSvVe9KWkxcHCJJ3FaXF", -			"BADCODE", -			"The provided authorization grant (e.g., authorization code, resource owner credentials) or refresh token is invalid, expired, revoked, does not match the redirection URI used in the authorization request, or was issued to another client. Unable to find initial PKCE data tied to this request. Could not find the requested resource(s).", -			clientConfidential, -		}, -		{ -			"ShouldPassNotEnforcedNoSession", -			oidc.GrantTypeAuthorizationCode, -			false, -			false, -			true, -			"", -			"", -			"", -			"", -			"", -			clientConfidential, -		}, -	} - -	for i, tc := range testCases { -		t.Run(tc.name, func(t *testing.T) { -			ctx := context.TODO() - -			config.ProofKeyCodeExchange.Enforce = tc.enforce -			config.ProofKeyCodeExchange.EnforcePublicClients = tc.enforcePublicClients -			config.ProofKeyCodeExchange.AllowPlainChallengeMethod = tc.allowPlain - -			if len(tc.code) > 0 { -				strategy.signature = tc.code -			} else { -				strategy.signature = fmt.Sprintf("code-%d", i) -			} - -			ar := fosite.NewAuthorizeRequest() - -			ar.Client = tc.client - -			if len(tc.challenge) != 0 { -				ar.Form.Add(oidc.FormParameterCodeChallenge, tc.challenge) -			} - -			if len(tc.method) != 0 { -				ar.Form.Add(oidc.FormParameterCodeChallengeMethod, tc.method) -			} - -			if len(tc.code) > 0 { -				require.NoError(t, store.CreatePKCERequestSession(ctx, fmt.Sprintf("code-%d", i), ar)) -			} - -			r := fosite.NewAccessRequest(nil) -			r.Client = tc.client -			r.GrantTypes = fosite.Arguments{tc.grant} - -			if len(tc.verifier) != 0 { -				r.Form.Add(oidc.FormParameterCodeVerifier, tc.verifier) -			} - -			err := handler.HandleTokenEndpointRequest(context.TODO(), r) -			err = oidc.ErrorToDebugRFC6749Error(err) - -			if len(tc.expected) == 0 { -				assert.NoError(t, err) -			} else { -				assert.EqualError(t, err, tc.expected) -			} -		}) -	} -} - -func TestPKCEHandler_HandleTokenEndpointRequest_Mock(t *testing.T) { -	client := &oidc.RegisteredClient{ -		ID:     "test", -		Public: false, -	} - -	testCases := []struct { -		name                                      string -		setup                                     func(mock *mocks.MockPKCERequestStorage) -		grant                                     string -		enforce, enforcePublicClients, allowPlain bool -		method, challenge, verifier               string -		expected                                  string -		client                                    *oidc.RegisteredClient -	}{ -		{ -			"ShouldPassS256WithConfidentialClientWhenEnforcedWhenAllowPlain", -			func(mock *mocks.MockPKCERequestStorage) { -				mock.EXPECT().GetPKCERequestSession(gomock.Any(), gomock.Any(), gomock.Any()).Return(nil, fosite.ErrNotFound.WithDebug("A bad error!")) -			}, -			oidc.GrantTypeAuthorizationCode, -			true, -			true, -			true, -			oidc.PKCEChallengeMethodSHA256, -			pkceChallenge, -			pkceVerifier, -			"The provided authorization grant (e.g., authorization code, resource owner credentials) or refresh token is invalid, expired, revoked, does not match the redirection URI used in the authorization request, or was issued to another client. Unable to find initial PKCE data tied to this request. Could not find the requested resource(s). A bad error!", -			client, -		}, -		{ -			"ShouldPassS256WithConfidentialClientWhenEnforcedWhenAllowPlain", -			func(mock *mocks.MockPKCERequestStorage) { -				mock.EXPECT().GetPKCERequestSession(gomock.Any(), gomock.Any(), gomock.Any()).Return(nil, errors.New("A really bad error!")) -			}, -			oidc.GrantTypeAuthorizationCode, -			true, -			true, -			true, -			oidc.PKCEChallengeMethodSHA256, -			pkceChallenge, -			pkceVerifier, -			"The authorization server encountered an unexpected condition that prevented it from fulfilling the request. A really bad error!", -			client, -		}, -		{ -			"ShouldPassS256WithConfidentialClientWhenEnforcedWhenAllowPlain", -			func(mock *mocks.MockPKCERequestStorage) { -				mock.EXPECT().GetPKCERequestSession(gomock.Any(), gomock.Any(), gomock.Any()).Return(fosite.NewAccessRequest(&fosite.DefaultSession{}), nil) -				mock.EXPECT().DeletePKCERequestSession(gomock.Any(), gomock.Any()).Return(errors.New("Could not delete!")) -			}, -			oidc.GrantTypeAuthorizationCode, -			true, -			true, -			true, -			oidc.PKCEChallengeMethodSHA256, -			pkceChallenge, -			pkceVerifier, -			"The authorization server encountered an unexpected condition that prevented it from fulfilling the request. Could not delete!", -			client, -		}, -	} - -	for _, tc := range testCases { -		t.Run(tc.name, func(t *testing.T) { -			ctrl := gomock.NewController(t) -			store := mocks.NewMockPKCERequestStorage(ctrl) -			defer ctrl.Finish() - -			strategy := &TestCodeStrategy{} -			config := &oidc.Config{ -				ProofKeyCodeExchange: oidc.ProofKeyCodeExchangeConfig{ -					Enforce:                   tc.enforce, -					AllowPlainChallengeMethod: tc.allowPlain, -					EnforcePublicClients:      tc.enforcePublicClients, -				}, -			} - -			handler := &oidc.PKCEHandler{Storage: store, AuthorizeCodeStrategy: strategy, Config: config} - -			if tc.setup != nil { -				tc.setup(store) -			} - -			request := fosite.NewAccessRequest(&fosite.DefaultSession{}) - -			request.GrantTypes = fosite.Arguments{oidc.GrantTypeAuthorizationCode} -			request.Form = url.Values{ -				oidc.FormParameterCodeVerifier: []string{tc.verifier}, -			} - -			err := oidc.ErrorToDebugRFC6749Error(handler.HandleTokenEndpointRequest(context.TODO(), request)) - -			if len(tc.expected) == 0 { -				assert.NoError(t, err) -			} else { -				assert.EqualError(t, err, tc.expected) -			} -		}) -	} -} - -const ( -	pkceChallenge = "GM6jKJIR6JxgxU5m5Y79WzudqoNmo7PogrhI1_F8eGw" -	pkceVerifier  = "Nt6MpT7QXtfme55cKv9b23KEAvSEHyjRVtQt5jcjUUmWU9bTzd" -) diff --git a/internal/oidc/client_test.go b/internal/oidc/client_test.go index 6f783fd92..9878a4bf1 100644 --- a/internal/oidc/client_test.go +++ b/internal/oidc/client_test.go @@ -2,13 +2,11 @@ package oidc_test  import (  	"context" -	"fmt" -	"net/url"  	"testing"  	"time" -	"github.com/go-jose/go-jose/v3" -	"github.com/ory/fosite" +	oauthelia2 "authelia.com/provider/oauth2" +	"github.com/go-jose/go-jose/v4"  	"github.com/stretchr/testify/assert"  	"github.com/stretchr/testify/require" @@ -26,7 +24,7 @@ func TestNewClient(t *testing.T) {  	assert.Equal(t, "", client.GetName())  	assert.Len(t, client.GetResponseModes(), 0)  	assert.Len(t, client.GetResponseTypes(), 1) -	assert.Equal(t, "", client.GetSectorIdentifier()) +	assert.Equal(t, "", client.GetSectorIdentifierURI())  	bclient, ok := client.(*oidc.RegisteredClient)  	require.True(t, ok) @@ -54,9 +52,9 @@ func TestNewClient(t *testing.T) {  	client = oidc.NewClient(config, &schema.IdentityProvidersOpenIDConnect{})  	assert.Equal(t, myclient, client.GetID())  	require.Len(t, client.GetResponseModes(), 1) -	assert.Equal(t, fosite.ResponseModeFormPost, client.GetResponseModes()[0]) +	assert.Equal(t, oauthelia2.ResponseModeFormPost, client.GetResponseModes()[0])  	assert.Equal(t, authorization.TwoFactor, client.GetAuthorizationPolicyRequiredLevel(authorization.Subject{})) -	assert.Equal(t, fosite.Arguments(nil), client.GetAudience()) +	assert.Equal(t, oauthelia2.Arguments(nil), client.GetAudience())  	config = schema.IdentityProvidersOpenIDConnectClient{  		TokenEndpointAuthMethod: oidc.ClientAuthMethodClientSecretPost, @@ -133,15 +131,15 @@ func TestNewClient(t *testing.T) {  	assert.Equal(t, oidc.ClientAuthMethodClientSecretPost, fclient.GetTokenEndpointAuthMethod())  	assert.Equal(t, "", fclient.TokenEndpointAuthSigningAlg) -	assert.Equal(t, oidc.SigningAlgRSAUsingSHA256, fclient.GetTokenEndpointAuthSigningAlgorithm()) +	assert.Equal(t, oidc.SigningAlgRSAUsingSHA256, fclient.GetTokenEndpointAuthSigningAlg())  	assert.Equal(t, oidc.SigningAlgRSAUsingSHA256, fclient.TokenEndpointAuthSigningAlg)  	assert.Equal(t, "", fclient.RequestObjectSigningAlg) -	assert.Equal(t, "", fclient.GetRequestObjectSigningAlgorithm()) +	assert.Equal(t, "", fclient.GetRequestObjectSigningAlg())  	fclient.RequestObjectSigningAlg = oidc.SigningAlgRSAUsingSHA256 -	assert.Equal(t, oidc.SigningAlgRSAUsingSHA256, fclient.GetRequestObjectSigningAlgorithm()) +	assert.Equal(t, oidc.SigningAlgRSAUsingSHA256, fclient.GetRequestObjectSigningAlg())  	assert.Nil(t, fclient.JSONWebKeysURI)  	assert.Equal(t, "", fclient.GetJSONWebKeysURI()) @@ -229,73 +227,6 @@ func TestBaseClient_Misc(t *testing.T) {  	}  } -func TestRegisteredClient_ValidatePARPolicy(t *testing.T) { -	testCases := []struct { -		name     string -		client   *oidc.RegisteredClient -		have     *fosite.Request -		expected string -	}{ -		{ -			"ShouldNotEnforcePAR", -			&oidc.RegisteredClient{ -				RequirePushedAuthorizationRequests: false, -			}, -			&fosite.Request{}, -			"", -		}, -		{ -			"ShouldEnforcePARAndErrorWithoutCorrectRequestURI", -			&oidc.RegisteredClient{ -				RequirePushedAuthorizationRequests: true, -			}, -			&fosite.Request{ -				Form: map[string][]string{ -					oidc.FormParameterRequestURI: {"https://google.com"}, -				}, -			}, -			"invalid_request", -		}, -		{ -			"ShouldEnforcePARAndErrorWithEmptyRequestURI", -			&oidc.RegisteredClient{ -				RequirePushedAuthorizationRequests: true, -			}, -			&fosite.Request{ -				Form: map[string][]string{ -					oidc.FormParameterRequestURI: {""}, -				}, -			}, -			"invalid_request", -		}, -		{ -			"ShouldEnforcePARAndNotErrorWithCorrectRequestURI", -			&oidc.RegisteredClient{ -				RequirePushedAuthorizationRequests: true, -			}, -			&fosite.Request{ -				Form: map[string][]string{ -					oidc.FormParameterRequestURI: {oidc.RedirectURIPrefixPushedAuthorizationRequestURN + abc}, -				}, -			}, -			"", -		}, -	} - -	for _, tc := range testCases { -		t.Run(tc.name, func(t *testing.T) { -			err := tc.client.ValidatePARPolicy(tc.have, oidc.RedirectURIPrefixPushedAuthorizationRequestURN) - -			switch tc.expected { -			case "": -				assert.NoError(t, err) -			default: -				assert.EqualError(t, err, tc.expected) -			} -		}) -	} -} -  func TestIsAuthenticationLevelSufficient(t *testing.T) {  	c := &oidc.RegisteredClient{} @@ -390,24 +321,9 @@ func TestClient_GetGrantTypes(t *testing.T) {  func TestClient_Hashing(t *testing.T) {  	c := &oidc.RegisteredClient{} -	hashedSecret := c.GetHashedSecret() -	assert.Equal(t, []byte(nil), hashedSecret) +	c.ClientSecret = &oidc.ClientSecretDigest{PasswordDigest: tOpenIDConnectPlainTextClientSecret} -	c.Secret = tOpenIDConnectPlainTextClientSecret - -	assert.True(t, c.Secret.MatchBytes([]byte("client-secret"))) -} - -func TestClient_GetHashedSecret(t *testing.T) { -	c := &oidc.RegisteredClient{} - -	hashedSecret := c.GetHashedSecret() -	assert.Equal(t, []byte(nil), hashedSecret) - -	c.Secret = tOpenIDConnectPlainTextClientSecret - -	hashedSecret = c.GetHashedSecret() -	assert.Equal(t, []byte("$plaintext$client-secret"), hashedSecret) +	assert.True(t, c.ClientSecret.MatchBytes([]byte("client-secret")))  }  func TestClient_GetID(t *testing.T) { @@ -441,17 +357,17 @@ func TestClient_GetResponseModes(t *testing.T) {  	responseModes := c.GetResponseModes()  	require.Len(t, responseModes, 0) -	c.ResponseModes = []fosite.ResponseModeType{ -		fosite.ResponseModeDefault, fosite.ResponseModeFormPost, -		fosite.ResponseModeQuery, fosite.ResponseModeFragment, +	c.ResponseModes = []oauthelia2.ResponseModeType{ +		oauthelia2.ResponseModeDefault, oauthelia2.ResponseModeFormPost, +		oauthelia2.ResponseModeQuery, oauthelia2.ResponseModeFragment,  	}  	responseModes = c.GetResponseModes()  	require.Len(t, responseModes, 4) -	assert.Equal(t, fosite.ResponseModeDefault, responseModes[0]) -	assert.Equal(t, fosite.ResponseModeFormPost, responseModes[1]) -	assert.Equal(t, fosite.ResponseModeQuery, responseModes[2]) -	assert.Equal(t, fosite.ResponseModeFragment, responseModes[3]) +	assert.Equal(t, oauthelia2.ResponseModeDefault, responseModes[0]) +	assert.Equal(t, oauthelia2.ResponseModeFormPost, responseModes[1]) +	assert.Equal(t, oauthelia2.ResponseModeQuery, responseModes[2]) +	assert.Equal(t, oauthelia2.ResponseModeFragment, responseModes[3])  }  func TestClient_GetResponseTypes(t *testing.T) { @@ -476,9 +392,6 @@ func TestNewClientPKCE(t *testing.T) {  		expectedEnforcePKCE                bool  		expectedEnforcePKCEChallengeMethod bool  		expected                           string -		r                                  *fosite.Request -		err                                string -		desc                               string  	}{  		{  			"ShouldNotEnforcePKCEAndNotErrorOnNonPKCERequest", @@ -486,9 +399,6 @@ func TestNewClientPKCE(t *testing.T) {  			false,  			false,  			"", -			&fosite.Request{}, -			"", -			"",  		},  		{  			"ShouldEnforcePKCEAndErrorOnNonPKCERequest", @@ -496,9 +406,6 @@ func TestNewClientPKCE(t *testing.T) {  			true,  			false,  			"", -			&fosite.Request{}, -			"invalid_request", -			"The request is missing a required parameter, includes an invalid parameter value, includes a parameter more than once, or is otherwise malformed. Clients must include a code_challenge when performing the authorize code flow, but it is missing. The server is configured in a way that enforces PKCE for this client.",  		},  		{  			"ShouldEnforcePKCEAndNotErrorOnPKCERequest", @@ -506,36 +413,24 @@ func TestNewClientPKCE(t *testing.T) {  			true,  			false,  			"", -			&fosite.Request{Form: map[string][]string{"code_challenge": {abc}}}, -			"", -			"",  		},  		{"ShouldEnforcePKCEFromChallengeMethodAndErrorOnNonPKCERequest",  			schema.IdentityProvidersOpenIDConnectClient{PKCEChallengeMethod: "S256"},  			true,  			true,  			"S256", -			&fosite.Request{}, -			"invalid_request", -			"The request is missing a required parameter, includes an invalid parameter value, includes a parameter more than once, or is otherwise malformed. Clients must include a code_challenge when performing the authorize code flow, but it is missing. The server is configured in a way that enforces PKCE for this client.",  		},  		{"ShouldEnforcePKCEFromChallengeMethodAndErrorOnInvalidChallengeMethod",  			schema.IdentityProvidersOpenIDConnectClient{PKCEChallengeMethod: "S256"},  			true,  			true,  			"S256", -			&fosite.Request{Form: map[string][]string{"code_challenge": {abc}}}, -			"invalid_request", -			"The request is missing a required parameter, includes an invalid parameter value, includes a parameter more than once, or is otherwise malformed. Client must use code_challenge_method=S256,  is not allowed. The server is configured in a way that enforces PKCE S256 as challenge method for this client.",  		},  		{"ShouldEnforcePKCEFromChallengeMethodAndNotErrorOnValidRequest",  			schema.IdentityProvidersOpenIDConnectClient{PKCEChallengeMethod: "S256"},  			true,  			true,  			"S256", -			&fosite.Request{Form: map[string][]string{"code_challenge": {abc}, "code_challenge_method": {"S256"}}}, -			"", -			"",  		},  	} @@ -543,21 +438,9 @@ func TestNewClientPKCE(t *testing.T) {  		t.Run(tc.name, func(t *testing.T) {  			client := oidc.NewClient(tc.have, &schema.IdentityProvidersOpenIDConnect{}) -			assert.Equal(t, tc.expectedEnforcePKCE, client.GetPKCEEnforcement()) -			assert.Equal(t, tc.expectedEnforcePKCEChallengeMethod, client.GetPKCEChallengeMethodEnforcement()) +			assert.Equal(t, tc.expectedEnforcePKCE, client.GetEnforcePKCE()) +			assert.Equal(t, tc.expectedEnforcePKCEChallengeMethod, client.GetEnforcePKCEChallengeMethod())  			assert.Equal(t, tc.expected, client.GetPKCEChallengeMethod()) - -			if tc.r != nil { -				err := client.ValidatePKCEPolicy(tc.r) - -				if tc.err != "" { -					require.NotNil(t, err) -					assert.EqualError(t, err, tc.err) -					assert.Equal(t, tc.desc, fosite.ErrorToRFC6749Error(err).WithExposeDebug(true).GetDescription()) -				} else { -					assert.NoError(t, err) -				} -			}  		})  	}  } @@ -567,40 +450,26 @@ func TestNewClientPAR(t *testing.T) {  		name     string  		have     schema.IdentityProvidersOpenIDConnectClient  		expected bool -		r        *fosite.Request -		err      string -		desc     string  	}{  		{  			"ShouldNotEnforcEPARAndNotErrorOnNonPARRequest",  			schema.IdentityProvidersOpenIDConnectClient{},  			false, -			&fosite.Request{}, -			"", -			"",  		},  		{  			"ShouldEnforcePARAndErrorOnNonPARRequest",  			schema.IdentityProvidersOpenIDConnectClient{RequirePushedAuthorizationRequests: true},  			true, -			&fosite.Request{}, -			"invalid_request", -			"The request is missing a required parameter, includes an invalid parameter value, includes a parameter more than once, or is otherwise malformed. Pushed Authorization Requests are enforced for this client but no such request was sent. The request_uri parameter was empty.",  		},  		{  			"ShouldEnforcePARAndErrorOnNonPARRequest",  			schema.IdentityProvidersOpenIDConnectClient{RequirePushedAuthorizationRequests: true},  			true, -			&fosite.Request{Form: map[string][]string{oidc.FormParameterRequestURI: {"https://example.com"}}}, -			"invalid_request", -			"The request is missing a required parameter, includes an invalid parameter value, includes a parameter more than once, or is otherwise malformed. Pushed Authorization Requests are enforced for this client but no such request was sent. The request_uri parameter 'https://example.com' is malformed."}, +		},  		{  			"ShouldEnforcePARAndNotErrorOnPARRequest",  			schema.IdentityProvidersOpenIDConnectClient{RequirePushedAuthorizationRequests: true},  			true, -			&fosite.Request{Form: map[string][]string{oidc.FormParameterRequestURI: {fmt.Sprintf("%sabc", oidc.RedirectURIPrefixPushedAuthorizationRequestURN)}}}, -			"", -			"",  		},  	} @@ -609,18 +478,6 @@ func TestNewClientPAR(t *testing.T) {  			client := oidc.NewClient(tc.have, &schema.IdentityProvidersOpenIDConnect{})  			assert.Equal(t, tc.expected, client.GetRequirePushedAuthorizationRequests()) - -			if tc.r != nil { -				err := client.ValidatePARPolicy(tc.r, oidc.RedirectURIPrefixPushedAuthorizationRequestURN) - -				if tc.err != "" { -					require.NotNil(t, err) -					assert.EqualError(t, err, tc.err) -					assert.Equal(t, tc.desc, fosite.ErrorToRFC6749Error(err).WithExposeDebug(true).GetDescription()) -				} else { -					assert.NoError(t, err) -				} -			}  		})  	}  } @@ -628,8 +485,8 @@ func TestNewClientPAR(t *testing.T) {  func TestClient_GetEffectiveLifespan(t *testing.T) {  	type subcase struct {  		name     string -		gt       fosite.GrantType -		tt       fosite.TokenType +		gt       oauthelia2.GrantType +		tt       oauthelia2.TokenType  		fallback time.Duration  		expected time.Duration  	} @@ -652,15 +509,15 @@ func TestClient_GetEffectiveLifespan(t *testing.T) {  			[]subcase{  				{  					"ShouldHandleInvalidTokenTypeFallbackToProvidedFallback", -					fosite.GrantTypeAuthorizationCode, -					fosite.TokenType(abc), +					oauthelia2.GrantTypeAuthorizationCode, +					oauthelia2.TokenType(abc),  					time.Minute,  					time.Minute,  				},  				{  					"ShouldHandleInvalidGrantTypeFallbackToTokenType", -					fosite.GrantType(abc), -					fosite.AuthorizeCode, +					oauthelia2.GrantType(abc), +					oauthelia2.AuthorizeCode,  					time.Minute,  					time.Minute * 5,  				}, @@ -672,141 +529,141 @@ func TestClient_GetEffectiveLifespan(t *testing.T) {  			[]subcase{  				{  					"ShouldHandleAuthorizationCodeFlowAuthorizationCode", -					fosite.GrantTypeAuthorizationCode, -					fosite.AuthorizeCode, +					oauthelia2.GrantTypeAuthorizationCode, +					oauthelia2.AuthorizeCode,  					time.Minute,  					time.Minute,  				},  				{  					"ShouldHandleAuthorizationCodeFlowAccessToken", -					fosite.GrantTypeAuthorizationCode, -					fosite.AccessToken, +					oauthelia2.GrantTypeAuthorizationCode, +					oauthelia2.AccessToken,  					time.Minute,  					time.Minute,  				},  				{  					"ShouldHandleAuthorizationCodeFlowRefreshToken", -					fosite.GrantTypeAuthorizationCode, -					fosite.RefreshToken, +					oauthelia2.GrantTypeAuthorizationCode, +					oauthelia2.RefreshToken,  					time.Minute,  					time.Minute,  				},  				{  					"ShouldHandleAuthorizationCodeFlowIDToken", -					fosite.GrantTypeAuthorizationCode, -					fosite.IDToken, +					oauthelia2.GrantTypeAuthorizationCode, +					oauthelia2.IDToken,  					time.Minute,  					time.Minute,  				},  				{  					"ShouldHandleImplicitFlowAuthorizationCode", -					fosite.GrantTypeImplicit, -					fosite.AuthorizeCode, +					oauthelia2.GrantTypeImplicit, +					oauthelia2.AuthorizeCode,  					time.Minute,  					time.Minute,  				},  				{  					"ShouldHandleImplicitFlowAccessToken", -					fosite.GrantTypeImplicit, -					fosite.AccessToken, +					oauthelia2.GrantTypeImplicit, +					oauthelia2.AccessToken,  					time.Minute,  					time.Minute,  				},  				{  					"ShouldHandleImplicitFlowRefreshToken", -					fosite.GrantTypeImplicit, -					fosite.RefreshToken, +					oauthelia2.GrantTypeImplicit, +					oauthelia2.RefreshToken,  					time.Minute,  					time.Minute,  				},  				{  					"ShouldHandleImplicitFlowIDToken", -					fosite.GrantTypeImplicit, -					fosite.IDToken, +					oauthelia2.GrantTypeImplicit, +					oauthelia2.IDToken,  					time.Minute,  					time.Minute,  				},  				{  					"ShouldHandleClientCredentialsFlowAuthorizationCode", -					fosite.GrantTypeClientCredentials, -					fosite.AuthorizeCode, +					oauthelia2.GrantTypeClientCredentials, +					oauthelia2.AuthorizeCode,  					time.Minute,  					time.Minute,  				},  				{  					"ShouldHandleClientCredentialsFlowAccessToken", -					fosite.GrantTypeClientCredentials, -					fosite.AccessToken, +					oauthelia2.GrantTypeClientCredentials, +					oauthelia2.AccessToken,  					time.Minute,  					time.Minute,  				},  				{  					"ShouldHandleClientCredentialsFlowRefreshToken", -					fosite.GrantTypeClientCredentials, -					fosite.RefreshToken, +					oauthelia2.GrantTypeClientCredentials, +					oauthelia2.RefreshToken,  					time.Minute,  					time.Minute,  				},  				{  					"ShouldHandleClientCredentialsFlowIDToken", -					fosite.GrantTypeClientCredentials, -					fosite.IDToken, +					oauthelia2.GrantTypeClientCredentials, +					oauthelia2.IDToken,  					time.Minute,  					time.Minute,  				},  				{  					"ShouldHandleRefreshTokenFlowAuthorizationCode", -					fosite.GrantTypeRefreshToken, -					fosite.AuthorizeCode, +					oauthelia2.GrantTypeRefreshToken, +					oauthelia2.AuthorizeCode,  					time.Minute,  					time.Minute,  				},  				{  					"ShouldHandleRefreshTokenFlowAccessToken", -					fosite.GrantTypeRefreshToken, -					fosite.AccessToken, +					oauthelia2.GrantTypeRefreshToken, +					oauthelia2.AccessToken,  					time.Minute,  					time.Minute,  				},  				{  					"ShouldHandleRefreshTokenFlowRefreshToken", -					fosite.GrantTypeRefreshToken, -					fosite.RefreshToken, +					oauthelia2.GrantTypeRefreshToken, +					oauthelia2.RefreshToken,  					time.Minute,  					time.Minute,  				},  				{  					"ShouldHandleRefreshTokenFlowIDToken", -					fosite.GrantTypeRefreshToken, -					fosite.IDToken, +					oauthelia2.GrantTypeRefreshToken, +					oauthelia2.IDToken,  					time.Minute,  					time.Minute,  				},  				{  					"ShouldHandleJWTBearerFlowAuthorizationCode", -					fosite.GrantTypeJWTBearer, -					fosite.AuthorizeCode, +					oauthelia2.GrantTypeJWTBearer, +					oauthelia2.AuthorizeCode,  					time.Minute,  					time.Minute,  				},  				{  					"ShouldHandleJWTBearerFlowAccessToken", -					fosite.GrantTypeJWTBearer, -					fosite.AccessToken, +					oauthelia2.GrantTypeJWTBearer, +					oauthelia2.AccessToken,  					time.Minute,  					time.Minute,  				},  				{  					"ShouldHandleJWTBearerFlowRefreshToken", -					fosite.GrantTypeJWTBearer, -					fosite.RefreshToken, +					oauthelia2.GrantTypeJWTBearer, +					oauthelia2.RefreshToken,  					time.Minute,  					time.Minute,  				},  				{  					"ShouldHandleJWTBearerFlowIDToken", -					fosite.GrantTypeJWTBearer, -					fosite.IDToken, +					oauthelia2.GrantTypeJWTBearer, +					oauthelia2.IDToken,  					time.Minute,  					time.Minute,  				}, @@ -825,141 +682,141 @@ func TestClient_GetEffectiveLifespan(t *testing.T) {  			[]subcase{  				{  					"ShouldHandleAuthorizationCodeFlowAuthorizationCode", -					fosite.GrantTypeAuthorizationCode, -					fosite.AuthorizeCode, +					oauthelia2.GrantTypeAuthorizationCode, +					oauthelia2.AuthorizeCode,  					time.Minute,  					time.Minute * 5,  				},  				{  					"ShouldHandleAuthorizationCodeFlowAccessToken", -					fosite.GrantTypeAuthorizationCode, -					fosite.AccessToken, +					oauthelia2.GrantTypeAuthorizationCode, +					oauthelia2.AccessToken,  					time.Minute,  					time.Hour * 1,  				},  				{  					"ShouldHandleAuthorizationCodeFlowRefreshToken", -					fosite.GrantTypeAuthorizationCode, -					fosite.RefreshToken, +					oauthelia2.GrantTypeAuthorizationCode, +					oauthelia2.RefreshToken,  					time.Minute,  					time.Hour * 2,  				},  				{  					"ShouldHandleAuthorizationCodeFlowIDToken", -					fosite.GrantTypeAuthorizationCode, -					fosite.IDToken, +					oauthelia2.GrantTypeAuthorizationCode, +					oauthelia2.IDToken,  					time.Minute,  					time.Hour * 3,  				},  				{  					"ShouldHandleImplicitFlowAuthorizationCode", -					fosite.GrantTypeImplicit, -					fosite.AuthorizeCode, +					oauthelia2.GrantTypeImplicit, +					oauthelia2.AuthorizeCode,  					time.Minute,  					time.Minute * 5,  				},  				{  					"ShouldHandleImplicitFlowAccessToken", -					fosite.GrantTypeImplicit, -					fosite.AccessToken, +					oauthelia2.GrantTypeImplicit, +					oauthelia2.AccessToken,  					time.Minute,  					time.Hour * 1,  				},  				{  					"ShouldHandleImplicitFlowRefreshToken", -					fosite.GrantTypeImplicit, -					fosite.RefreshToken, +					oauthelia2.GrantTypeImplicit, +					oauthelia2.RefreshToken,  					time.Minute,  					time.Hour * 2,  				},  				{  					"ShouldHandleImplicitFlowIDToken", -					fosite.GrantTypeImplicit, -					fosite.IDToken, +					oauthelia2.GrantTypeImplicit, +					oauthelia2.IDToken,  					time.Minute,  					time.Hour * 3,  				},  				{  					"ShouldHandleClientCredentialsFlowAuthorizationCode", -					fosite.GrantTypeClientCredentials, -					fosite.AuthorizeCode, +					oauthelia2.GrantTypeClientCredentials, +					oauthelia2.AuthorizeCode,  					time.Minute,  					time.Minute * 5,  				},  				{  					"ShouldHandleClientCredentialsFlowAccessToken", -					fosite.GrantTypeClientCredentials, -					fosite.AccessToken, +					oauthelia2.GrantTypeClientCredentials, +					oauthelia2.AccessToken,  					time.Minute,  					time.Hour * 1,  				},  				{  					"ShouldHandleClientCredentialsFlowRefreshToken", -					fosite.GrantTypeClientCredentials, -					fosite.RefreshToken, +					oauthelia2.GrantTypeClientCredentials, +					oauthelia2.RefreshToken,  					time.Minute,  					time.Hour * 2,  				},  				{  					"ShouldHandleClientCredentialsFlowIDToken", -					fosite.GrantTypeClientCredentials, -					fosite.IDToken, +					oauthelia2.GrantTypeClientCredentials, +					oauthelia2.IDToken,  					time.Minute,  					time.Hour * 3,  				},  				{  					"ShouldHandleRefreshTokenFlowAuthorizationCode", -					fosite.GrantTypeRefreshToken, -					fosite.AuthorizeCode, +					oauthelia2.GrantTypeRefreshToken, +					oauthelia2.AuthorizeCode,  					time.Minute,  					time.Minute * 5,  				},  				{  					"ShouldHandleRefreshTokenFlowAccessToken", -					fosite.GrantTypeRefreshToken, -					fosite.AccessToken, +					oauthelia2.GrantTypeRefreshToken, +					oauthelia2.AccessToken,  					time.Minute,  					time.Hour * 1,  				},  				{  					"ShouldHandleRefreshTokenFlowRefreshToken", -					fosite.GrantTypeRefreshToken, -					fosite.RefreshToken, +					oauthelia2.GrantTypeRefreshToken, +					oauthelia2.RefreshToken,  					time.Minute,  					time.Hour * 2,  				},  				{  					"ShouldHandleRefreshTokenFlowIDToken", -					fosite.GrantTypeRefreshToken, -					fosite.IDToken, +					oauthelia2.GrantTypeRefreshToken, +					oauthelia2.IDToken,  					time.Minute,  					time.Hour * 3,  				},  				{  					"ShouldHandleJWTBearerFlowAuthorizationCode", -					fosite.GrantTypeJWTBearer, -					fosite.AuthorizeCode, +					oauthelia2.GrantTypeJWTBearer, +					oauthelia2.AuthorizeCode,  					time.Minute,  					time.Minute * 5,  				},  				{  					"ShouldHandleJWTBearerFlowAccessToken", -					fosite.GrantTypeJWTBearer, -					fosite.AccessToken, +					oauthelia2.GrantTypeJWTBearer, +					oauthelia2.AccessToken,  					time.Minute,  					time.Hour * 1,  				},  				{  					"ShouldHandleJWTBearerFlowRefreshToken", -					fosite.GrantTypeJWTBearer, -					fosite.RefreshToken, +					oauthelia2.GrantTypeJWTBearer, +					oauthelia2.RefreshToken,  					time.Minute,  					time.Hour * 2,  				},  				{  					"ShouldHandleJWTBearerFlowIDToken", -					fosite.GrantTypeJWTBearer, -					fosite.IDToken, +					oauthelia2.GrantTypeJWTBearer, +					oauthelia2.IDToken,  					time.Minute,  					time.Hour * 3,  				}, @@ -1010,141 +867,141 @@ func TestClient_GetEffectiveLifespan(t *testing.T) {  			[]subcase{  				{  					"ShouldHandleAuthorizationCodeFlowAuthorizationCode", -					fosite.GrantTypeAuthorizationCode, -					fosite.AuthorizeCode, +					oauthelia2.GrantTypeAuthorizationCode, +					oauthelia2.AuthorizeCode,  					time.Minute,  					time.Minute * 15,  				},  				{  					"ShouldHandleAuthorizationCodeFlowAccessToken", -					fosite.GrantTypeAuthorizationCode, -					fosite.AccessToken, +					oauthelia2.GrantTypeAuthorizationCode, +					oauthelia2.AccessToken,  					time.Minute,  					time.Hour * 11,  				},  				{  					"ShouldHandleAuthorizationCodeFlowRefreshToken", -					fosite.GrantTypeAuthorizationCode, -					fosite.RefreshToken, +					oauthelia2.GrantTypeAuthorizationCode, +					oauthelia2.RefreshToken,  					time.Minute,  					time.Hour * 12,  				},  				{  					"ShouldHandleAuthorizationCodeFlowIDToken", -					fosite.GrantTypeAuthorizationCode, -					fosite.IDToken, +					oauthelia2.GrantTypeAuthorizationCode, +					oauthelia2.IDToken,  					time.Minute,  					time.Hour * 13,  				},  				{  					"ShouldHandleImplicitFlowAuthorizationCode", -					fosite.GrantTypeImplicit, -					fosite.AuthorizeCode, +					oauthelia2.GrantTypeImplicit, +					oauthelia2.AuthorizeCode,  					time.Minute,  					time.Minute * 25,  				},  				{  					"ShouldHandleImplicitFlowAccessToken", -					fosite.GrantTypeImplicit, -					fosite.AccessToken, +					oauthelia2.GrantTypeImplicit, +					oauthelia2.AccessToken,  					time.Minute,  					time.Hour * 21,  				},  				{  					"ShouldHandleImplicitFlowRefreshToken", -					fosite.GrantTypeImplicit, -					fosite.RefreshToken, +					oauthelia2.GrantTypeImplicit, +					oauthelia2.RefreshToken,  					time.Minute,  					time.Hour * 22,  				},  				{  					"ShouldHandleImplicitFlowIDToken", -					fosite.GrantTypeImplicit, -					fosite.IDToken, +					oauthelia2.GrantTypeImplicit, +					oauthelia2.IDToken,  					time.Minute,  					time.Hour * 23,  				},  				{  					"ShouldHandleClientCredentialsFlowAuthorizationCode", -					fosite.GrantTypeClientCredentials, -					fosite.AuthorizeCode, +					oauthelia2.GrantTypeClientCredentials, +					oauthelia2.AuthorizeCode,  					time.Minute,  					time.Minute * 35,  				},  				{  					"ShouldHandleClientCredentialsFlowAccessToken", -					fosite.GrantTypeClientCredentials, -					fosite.AccessToken, +					oauthelia2.GrantTypeClientCredentials, +					oauthelia2.AccessToken,  					time.Minute,  					time.Hour * 31,  				},  				{  					"ShouldHandleClientCredentialsFlowRefreshToken", -					fosite.GrantTypeClientCredentials, -					fosite.RefreshToken, +					oauthelia2.GrantTypeClientCredentials, +					oauthelia2.RefreshToken,  					time.Minute,  					time.Hour * 32,  				},  				{  					"ShouldHandleClientCredentialsFlowIDToken", -					fosite.GrantTypeClientCredentials, -					fosite.IDToken, +					oauthelia2.GrantTypeClientCredentials, +					oauthelia2.IDToken,  					time.Minute,  					time.Hour * 33,  				},  				{  					"ShouldHandleRefreshTokenFlowAuthorizationCode", -					fosite.GrantTypeRefreshToken, -					fosite.AuthorizeCode, +					oauthelia2.GrantTypeRefreshToken, +					oauthelia2.AuthorizeCode,  					time.Minute,  					time.Minute * 45,  				},  				{  					"ShouldHandleRefreshTokenFlowAccessToken", -					fosite.GrantTypeRefreshToken, -					fosite.AccessToken, +					oauthelia2.GrantTypeRefreshToken, +					oauthelia2.AccessToken,  					time.Minute,  					time.Hour * 41,  				},  				{  					"ShouldHandleRefreshTokenFlowRefreshToken", -					fosite.GrantTypeRefreshToken, -					fosite.RefreshToken, +					oauthelia2.GrantTypeRefreshToken, +					oauthelia2.RefreshToken,  					time.Minute,  					time.Hour * 42,  				},  				{  					"ShouldHandleRefreshTokenFlowIDToken", -					fosite.GrantTypeRefreshToken, -					fosite.IDToken, +					oauthelia2.GrantTypeRefreshToken, +					oauthelia2.IDToken,  					time.Minute,  					time.Hour * 43,  				},  				{  					"ShouldHandleJWTBearerFlowAuthorizationCode", -					fosite.GrantTypeJWTBearer, -					fosite.AuthorizeCode, +					oauthelia2.GrantTypeJWTBearer, +					oauthelia2.AuthorizeCode,  					time.Minute,  					time.Minute * 55,  				},  				{  					"ShouldHandleJWTBearerFlowAccessToken", -					fosite.GrantTypeJWTBearer, -					fosite.AccessToken, +					oauthelia2.GrantTypeJWTBearer, +					oauthelia2.AccessToken,  					time.Minute,  					time.Hour * 51,  				},  				{  					"ShouldHandleJWTBearerFlowRefreshToken", -					fosite.GrantTypeJWTBearer, -					fosite.RefreshToken, +					oauthelia2.GrantTypeJWTBearer, +					oauthelia2.RefreshToken,  					time.Minute,  					time.Hour * 52,  				},  				{  					"ShouldHandleJWTBearerFlowIDToken", -					fosite.GrantTypeJWTBearer, -					fosite.IDToken, +					oauthelia2.GrantTypeJWTBearer, +					oauthelia2.IDToken,  					time.Minute,  					time.Hour * 53,  				}, @@ -1178,40 +1035,40 @@ func TestNewClientResponseModes(t *testing.T) {  	testCases := []struct {  		name     string  		have     schema.IdentityProvidersOpenIDConnectClient -		expected []fosite.ResponseModeType -		r        *fosite.AuthorizeRequest +		expected []oauthelia2.ResponseModeType +		r        *oauthelia2.AuthorizeRequest  		err      string  		desc     string  	}{  		{  			"ShouldEnforceResponseModePolicyAndAllowDefaultModeQuery",  			schema.IdentityProvidersOpenIDConnectClient{ResponseModes: []string{oidc.ResponseModeQuery}}, -			[]fosite.ResponseModeType{fosite.ResponseModeQuery}, -			&fosite.AuthorizeRequest{DefaultResponseMode: fosite.ResponseModeQuery, ResponseMode: fosite.ResponseModeDefault, Request: fosite.Request{Form: map[string][]string{oidc.FormParameterResponseMode: nil}}}, +			[]oauthelia2.ResponseModeType{oauthelia2.ResponseModeQuery}, +			&oauthelia2.AuthorizeRequest{DefaultResponseMode: oauthelia2.ResponseModeQuery, ResponseMode: oauthelia2.ResponseModeDefault, Request: oauthelia2.Request{Form: map[string][]string{oidc.FormParameterResponseMode: nil}}},  			"",  			"",  		},  		{  			"ShouldEnforceResponseModePolicyAndFailOnDefaultMode",  			schema.IdentityProvidersOpenIDConnectClient{ResponseModes: []string{oidc.ResponseModeFormPost}}, -			[]fosite.ResponseModeType{fosite.ResponseModeFormPost}, -			&fosite.AuthorizeRequest{DefaultResponseMode: fosite.ResponseModeQuery, ResponseMode: fosite.ResponseModeDefault, Request: fosite.Request{Form: map[string][]string{oidc.FormParameterResponseMode: nil}}}, +			[]oauthelia2.ResponseModeType{oauthelia2.ResponseModeFormPost}, +			&oauthelia2.AuthorizeRequest{DefaultResponseMode: oauthelia2.ResponseModeQuery, ResponseMode: oauthelia2.ResponseModeDefault, Request: oauthelia2.Request{Form: map[string][]string{oidc.FormParameterResponseMode: nil}}},  			"unsupported_response_mode",  			"The authorization server does not support obtaining a response using this response mode. The request omitted the response_mode making the default response_mode 'query' based on the other authorization request parameters but registered OAuth 2.0 client doesn't support this response_mode",  		},  		{  			"ShouldNotEnforceConfiguredResponseMode",  			schema.IdentityProvidersOpenIDConnectClient{ResponseModes: []string{oidc.ResponseModeFormPost}}, -			[]fosite.ResponseModeType{fosite.ResponseModeFormPost}, -			&fosite.AuthorizeRequest{DefaultResponseMode: fosite.ResponseModeQuery, ResponseMode: fosite.ResponseModeQuery, Request: fosite.Request{Form: map[string][]string{oidc.FormParameterResponseMode: {oidc.ResponseModeQuery}}}}, +			[]oauthelia2.ResponseModeType{oauthelia2.ResponseModeFormPost}, +			&oauthelia2.AuthorizeRequest{DefaultResponseMode: oauthelia2.ResponseModeQuery, ResponseMode: oauthelia2.ResponseModeQuery, Request: oauthelia2.Request{Form: map[string][]string{oidc.FormParameterResponseMode: {oidc.ResponseModeQuery}}}},  			"",  			"",  		},  		{  			"ShouldNotEnforceUnconfiguredResponseMode",  			schema.IdentityProvidersOpenIDConnectClient{ResponseModes: []string{}}, -			[]fosite.ResponseModeType{}, -			&fosite.AuthorizeRequest{DefaultResponseMode: fosite.ResponseModeQuery, ResponseMode: fosite.ResponseModeDefault, Request: fosite.Request{Form: map[string][]string{oidc.FormParameterResponseMode: {oidc.ResponseModeQuery}}}}, +			[]oauthelia2.ResponseModeType{}, +			&oauthelia2.AuthorizeRequest{DefaultResponseMode: oauthelia2.ResponseModeQuery, ResponseMode: oauthelia2.ResponseModeDefault, Request: oauthelia2.Request{Form: map[string][]string{oidc.FormParameterResponseMode: {oidc.ResponseModeQuery}}}},  			"",  			"",  		}, @@ -1229,7 +1086,7 @@ func TestNewClientResponseModes(t *testing.T) {  				if tc.err != "" {  					require.NotNil(t, err)  					assert.EqualError(t, err, tc.err) -					assert.Equal(t, tc.desc, fosite.ErrorToRFC6749Error(err).WithExposeDebug(true).GetDescription()) +					assert.Equal(t, tc.desc, oauthelia2.ErrorToRFC6749Error(err).WithExposeDebug(true).GetDescription())  				} else {  					assert.NoError(t, err)  				} @@ -1280,55 +1137,3 @@ func TestNewClient_JSONWebKeySetURI(t *testing.T) {  	assert.Equal(t, "", registered.GetJSONWebKeysURI())  } - -func TestBaseClient_ApplyRequestedAudiencePolicy(t *testing.T) { -	testCases := []struct { -		name     string -		have     fosite.Arguments -		audience []string -		form     url.Values -		policy   oidc.ClientRequestedAudienceMode -		expected fosite.Arguments -	}{ -		{ -			"ShouldNotModifyExplicit", -			fosite.Arguments(nil), -			[]string{"example", "end"}, -			nil, -			oidc.ClientRequestedAudienceModeExplicit, -			fosite.Arguments(nil), -		}, -		{ -			"ShouldModifyImplicit", -			fosite.Arguments(nil), -			[]string{"example", "end"}, -			nil, -			oidc.ClientRequestedAudienceModeImplicit, -			[]string{"example", "end"}, -		}, -		{ -			"ShouldNotModifyImplicitFormParameter", -			fosite.Arguments(nil), -			[]string{"example", "end"}, -			url.Values{"audience": []string{}}, -			oidc.ClientRequestedAudienceModeImplicit, -			fosite.Arguments(nil), -		}, -	} - -	for _, tc := range testCases { -		t.Run(tc.name, func(t *testing.T) { -			client := &oidc.RegisteredClient{ -				ID:                    "test", -				Audience:              tc.audience, -				RequestedAudienceMode: tc.policy, -			} - -			actual := &fosite.Request{RequestedAudience: tc.have, Form: tc.form} - -			client.ApplyRequestedAudiencePolicy(actual) - -			assert.Equal(t, tc.expected, actual.RequestedAudience) -		}) -	} -} diff --git a/internal/oidc/config.go b/internal/oidc/config.go index f829c00ab..2d39b9358 100644 --- a/internal/oidc/config.go +++ b/internal/oidc/config.go @@ -9,14 +9,14 @@ import (  	"strings"  	"time" +	oauthelia2 "authelia.com/provider/oauth2" +	"authelia.com/provider/oauth2/handler/oauth2" +	"authelia.com/provider/oauth2/handler/openid" +	"authelia.com/provider/oauth2/handler/par" +	"authelia.com/provider/oauth2/handler/pkce" +	"authelia.com/provider/oauth2/i18n" +	"authelia.com/provider/oauth2/token/jwt"  	"github.com/hashicorp/go-retryablehttp" -	"github.com/ory/fosite" -	"github.com/ory/fosite/handler/oauth2" -	"github.com/ory/fosite/handler/openid" -	"github.com/ory/fosite/handler/par" -	"github.com/ory/fosite/i18n" -	"github.com/ory/fosite/token/hmac" -	"github.com/ory/fosite/token/jwt"  	"github.com/authelia/authelia/v4/internal/configuration/schema"  	"github.com/authelia/authelia/v4/internal/templates" @@ -29,41 +29,34 @@ func NewConfig(config *schema.IdentityProvidersOpenIDConnect, signer jwt.Signer,  		GlobalSecret:               []byte(utils.HashSHA256FromString(config.HMACSecret)),  		SendDebugMessagesToClients: config.EnableClientDebugMessages,  		MinParameterEntropy:        config.MinimumParameterEntropy, -		Lifespans:                  config.Lifespans.IdentityProvidersOpenIDConnectLifespanToken, +		Lifespans: LifespansConfig{ +			IdentityProvidersOpenIDConnectLifespanToken: config.Lifespans.IdentityProvidersOpenIDConnectLifespanToken, +		},  		ProofKeyCodeExchange: ProofKeyCodeExchangeConfig{  			Enforce:                   config.EnforcePKCE == "always",  			EnforcePublicClients:      config.EnforcePKCE != "never",  			AllowPlainChallengeMethod: config.EnablePKCEPlainChallenge,  		},  		PAR: PARConfig{ -			Enforced:        config.PAR.Enforce, -			ContextLifespan: config.PAR.ContextLifespan, +			Require:         config.RequirePushedAuthorizationRequests, +			ContextLifespan: 5 * time.Minute,  			URIPrefix:       RedirectURIPrefixPushedAuthorizationRequestURN,  		},  		JWTAccessToken: JWTAccessTokenConfig{  			Enable:                       config.Discovery.JWTResponseAccessTokens,  			EnableStatelessIntrospection: config.EnableJWTAccessTokenStatelessIntrospection,  		}, -		JWTSecuredAuthorizationLifespan: config.Lifespans.JWTSecuredAuthorization, -		Templates:                       templates, +		JWTSecuredAuthorizationLifespan:                    config.Lifespans.JWTSecuredAuthorization, +		RevokeRefreshTokensExplicit:                        true, +		EnforceRevokeFlowRevokeRefreshTokensExplicitClient: true, +		ClientCredentialsFlowImplicitGrantRequested:        true, +		Templates: templates,  	} -	c.Handlers.ResponseMode = &ResponseModeHandler{c} -  	if config.Discovery.JWTResponseAccessTokens { -		c.Strategy.Core = &JWTCoreStrategy{ -			Signer: signer, -			HMACCoreStrategy: &HMACCoreStrategy{ -				Enigma: &hmac.HMACStrategy{Config: c}, -				Config: c, -			}, -			Config: c, -		} +		c.Strategy.Core = oauth2.NewCoreStrategy(c, "authelia_%s_", signer)  	} else { -		c.Strategy.Core = &HMACCoreStrategy{ -			Enigma: &hmac.HMACStrategy{Config: c}, -			Config: c, -		} +		c.Strategy.Core = oauth2.NewCoreStrategy(c, "authelia_%s_", nil)  	}  	c.Strategy.OpenID = &openid.DefaultStrategy{ @@ -74,7 +67,7 @@ func NewConfig(config *schema.IdentityProvidersOpenIDConnect, signer jwt.Signer,  	return c  } -// Config is an implementation of the fosite.Configurator. +// Config is an implementation of the oauthelia2.Configurator.  type Config struct {  	Signer jwt.Signer @@ -97,19 +90,25 @@ type Config struct {  	JWTAccessToken JWTAccessTokenConfig -	Hasher    *Hasher  	Hash      HashConfig  	Strategy  StrategyConfig  	PAR       PARConfig  	Handlers  HandlersConfig -	Lifespans schema.IdentityProvidersOpenIDConnectLifespanToken - -	VerifiableCredentialsNonceLifespan time.Duration +	Lifespans LifespansConfig +	RFC8693   RFC8693Config  	ProofKeyCodeExchange ProofKeyCodeExchangeConfig  	GrantTypeJWTBearer   GrantTypeJWTBearerConfig -	TokenURL            string +	TokenURL string + +	RFC8628UserVerificationURL string + +	RevokeRefreshTokensExplicit                        bool +	EnforceRevokeFlowRevokeRefreshTokensExplicitClient bool +	EnforceJWTProfileAccessTokens                      bool +	ClientCredentialsFlowImplicitGrantRequested        bool +  	TokenEntropy        int  	MinParameterEntropy int @@ -123,20 +122,34 @@ type Config struct {  	Templates *templates.Provider  } -// HashConfig holds specific fosite.Configurator information for hashing. +type RFC8693Config struct { +	TokenTypes                map[string]oauthelia2.RFC8693TokenType +	DefaultRequestedTokenType string +} + +type LifespansConfig struct { +	schema.IdentityProvidersOpenIDConnectLifespanToken + +	VerifiableCredentialsNonce time.Duration + +	RFC8628Code    time.Duration +	RFC8628Polling time.Duration +} + +// HashConfig holds specific oauthelia2.Configurator information for hashing.  type HashConfig struct { -	ClientSecrets fosite.Hasher +	ClientSecrets oauthelia2.Hasher  	HMAC          func() (h hash.Hash)  } -// StrategyConfig holds specific fosite.Configurator information for various strategies. +// StrategyConfig holds specific oauthelia2.Configurator information for various strategies.  type StrategyConfig struct {  	Core                 oauth2.CoreStrategy  	OpenID               openid.OpenIDConnectTokenStrategy -	Audience             fosite.AudienceMatchingStrategy -	Scope                fosite.ScopeStrategy -	JWKSFetcher          fosite.JWKSFetcherStrategy -	ClientAuthentication fosite.ClientAuthenticationStrategy +	Audience             oauthelia2.AudienceMatchingStrategy +	Scope                oauthelia2.ScopeStrategy +	JWKSFetcher          oauthelia2.JWKSFetcherStrategy +	ClientAuthentication oauthelia2.ClientAuthenticationStrategy  }  // JWTAccessTokenConfig represents the JWT Access Token config. @@ -145,14 +158,14 @@ type JWTAccessTokenConfig struct {  	EnableStatelessIntrospection bool  } -// PARConfig holds specific fosite.Configurator information for Pushed Authorization Requests. +// PARConfig holds specific oauthelia2.Configurator information for Pushed Authorization Requests.  type PARConfig struct { -	Enforced        bool +	Require         bool  	URIPrefix       string  	ContextLifespan time.Duration  } -// IssuersConfig holds specific fosite.Configurator information for the issuer. +// IssuersConfig holds specific oauthelia2.Configurator information for the issuer.  type IssuersConfig struct {  	IDToken     string  	AccessToken string @@ -161,35 +174,39 @@ type IssuersConfig struct {  	JWTSecuredResponseMode                  string  } -// HandlersConfig holds specific fosite.Configurator handlers configuration information. +// HandlersConfig holds specific oauthelia2.Configurator handlers configuration information.  type HandlersConfig struct {  	// ResponseMode provides an extension handler for custom response modes. -	ResponseMode fosite.ResponseModeHandler +	ResponseMode []oauthelia2.ResponseModeHandler  	// AuthorizeEndpoint is a list of handlers that are called before the authorization endpoint is served. -	AuthorizeEndpoint fosite.AuthorizeEndpointHandlers +	AuthorizeEndpoint oauthelia2.AuthorizeEndpointHandlers  	// TokenEndpoint is a list of handlers that are called before the token endpoint is served. -	TokenEndpoint fosite.TokenEndpointHandlers +	TokenEndpoint oauthelia2.TokenEndpointHandlers  	// TokenIntrospection is a list of handlers that are called before the token introspection endpoint is served. -	TokenIntrospection fosite.TokenIntrospectionHandlers +	TokenIntrospection oauthelia2.TokenIntrospectionHandlers  	// Revocation is a list of handlers that are called before the revocation endpoint is served. -	Revocation fosite.RevocationHandlers +	Revocation oauthelia2.RevocationHandlers  	// PushedAuthorizeEndpoint is a list of handlers that are called before the PAR endpoint is served. -	PushedAuthorizeEndpoint fosite.PushedAuthorizeEndpointHandlers +	PushedAuthorizeEndpoint oauthelia2.PushedAuthorizeEndpointHandlers + +	RFC8628DeviceAuthorizeEndpoint oauthelia2.RFC8628DeviceAuthorizeEndpointHandlers + +	RFC8628UserAuthorizeEndpoint oauthelia2.RFC8628UserAuthorizeEndpointHandlers  } -// GrantTypeJWTBearerConfig holds specific fosite.Configurator information for the JWT Bearer Grant Type. +// GrantTypeJWTBearerConfig holds specific oauthelia2.Configurator information for the JWT Bearer Grant Type.  type GrantTypeJWTBearerConfig struct {  	OptionalClientAuth bool  	OptionalJTIClaim   bool  	OptionalIssuedDate bool  } -// ProofKeyCodeExchangeConfig holds specific fosite.Configurator information for PKCE. +// ProofKeyCodeExchangeConfig holds specific oauthelia2.Configurator information for PKCE.  type ProofKeyCodeExchangeConfig struct {  	Enforce                   bool  	EnforcePublicClients      bool @@ -203,7 +220,7 @@ func (c *Config) LoadHandlers(store *Store) {  	var statelessJWT any  	if c.JWTAccessToken.Enable && c.JWTAccessToken.EnableStatelessIntrospection { -		statelessJWT = &StatelessJWTValidator{ +		statelessJWT = &oauth2.StatelessJWTValidator{  			Signer: c.Signer,  			Config: c,  		} @@ -223,7 +240,7 @@ func (c *Config) LoadHandlers(store *Store) {  			AccessTokenStorage:  store,  			Config:              c,  		}, -		&ClientCredentialsGrantHandler{ +		&oauth2.ClientCredentialsGrantHandler{  			HandleHelper: &oauth2.HandleHelper{  				AccessTokenStrategy: c.Strategy.Core,  				AccessTokenStorage:  store, @@ -231,7 +248,7 @@ func (c *Config) LoadHandlers(store *Store) {  			},  			Config: c,  		}, -		&RefreshTokenGrantHandler{ +		&oauth2.RefreshTokenGrantHandler{  			AccessTokenStrategy:    c.Strategy.Core,  			RefreshTokenStrategy:   c.Strategy.Core,  			TokenRevocationStorage: store, @@ -293,8 +310,9 @@ func (c *Config) LoadHandlers(store *Store) {  			AccessTokenStrategy:    c.Strategy.Core,  			RefreshTokenStrategy:   c.Strategy.Core,  			TokenRevocationStorage: store, +			Config:                 c,  		}, -		&PKCEHandler{ +		&pkce.Handler{  			AuthorizeCodeStrategy: c.Strategy.Core,  			Storage:               store,  			Config:                c, @@ -303,13 +321,10 @@ func (c *Config) LoadHandlers(store *Store) {  			Storage: store,  			Config:  c,  		}, -		&AuthorizationServerIssuerIdentificationHandler{ -			Config: c, -		},  	}  	x := HandlersConfig{ -		ResponseMode: c.Handlers.ResponseMode, +		ResponseMode: []oauthelia2.ResponseModeHandler{&oauthelia2.DefaultResponseModeHandler{Config: c}},  	}  	for _, handler := range handlers { @@ -317,23 +332,23 @@ func (c *Config) LoadHandlers(store *Store) {  			continue  		} -		if h, ok := handler.(fosite.AuthorizeEndpointHandler); ok { +		if h, ok := handler.(oauthelia2.AuthorizeEndpointHandler); ok {  			x.AuthorizeEndpoint.Append(h)  		} -		if h, ok := handler.(fosite.TokenEndpointHandler); ok { +		if h, ok := handler.(oauthelia2.TokenEndpointHandler); ok {  			x.TokenEndpoint.Append(h)  		} -		if h, ok := handler.(fosite.TokenIntrospector); ok { +		if h, ok := handler.(oauthelia2.TokenIntrospector); ok {  			x.TokenIntrospection.Append(h)  		} -		if h, ok := handler.(fosite.RevocationHandler); ok { +		if h, ok := handler.(oauthelia2.RevocationHandler); ok {  			x.Revocation.Append(h)  		} -		if h, ok := handler.(fosite.PushedAuthorizeEndpointHandler); ok { +		if h, ok := handler.(oauthelia2.PushedAuthorizeEndpointHandler); ok {  			x.PushedAuthorizeEndpoint.Append(h)  		}  	} @@ -391,7 +406,7 @@ func (c *Config) GetJWTMaxDuration(ctx context.Context) (duration time.Duration)  // GetRedirectSecureChecker returns the redirect URL security validator.  func (c *Config) GetRedirectSecureChecker(ctx context.Context) func(context.Context, *url.URL) (secure bool) { -	return fosite.IsRedirectURISecure +	return oauthelia2.IsRedirectURISecure  }  // GetOmitRedirectScopeParam must be set to true if the scope query param is to be omitted @@ -539,27 +554,31 @@ func (c *Config) GetRefreshTokenScopes(ctx context.Context) (scopes []string) {  }  // GetScopeStrategy returns the scope strategy. -func (c *Config) GetScopeStrategy(ctx context.Context) (strategy fosite.ScopeStrategy) { +func (c *Config) GetScopeStrategy(ctx context.Context) (strategy oauthelia2.ScopeStrategy) {  	if c.Strategy.Scope == nil { -		c.Strategy.Scope = fosite.ExactScopeStrategy +		c.Strategy.Scope = oauthelia2.ExactScopeStrategy  	}  	return c.Strategy.Scope  }  // GetAudienceStrategy returns the audience strategy. -func (c *Config) GetAudienceStrategy(ctx context.Context) (strategy fosite.AudienceMatchingStrategy) { +func (c *Config) GetAudienceStrategy(ctx context.Context) (strategy oauthelia2.AudienceMatchingStrategy) {  	if c.Strategy.Audience == nil { -		c.Strategy.Audience = fosite.DefaultAudienceMatchingStrategy +		c.Strategy.Audience = oauthelia2.DefaultAudienceMatchingStrategy  	}  	return c.Strategy.Audience  } +func (c *Config) GetClientCredentialsFlowImplicitGrantRequested(ctx context.Context) (implicit bool) { +	return c.ClientCredentialsFlowImplicitGrantRequested +} +  // GetMinParameterEntropy returns the minimum parameter entropy.  func (c *Config) GetMinParameterEntropy(_ context.Context) (entropy int) {  	if c.MinParameterEntropy == 0 { -		c.MinParameterEntropy = fosite.MinParameterEntropy +		c.MinParameterEntropy = oauthelia2.MinParameterEntropy  	}  	return c.MinParameterEntropy @@ -580,16 +599,16 @@ func (c *Config) GetSendDebugMessagesToClients(ctx context.Context) (send bool)  }  // GetJWKSFetcherStrategy returns the JWKS fetcher strategy. -func (c *Config) GetJWKSFetcherStrategy(ctx context.Context) (strategy fosite.JWKSFetcherStrategy) { +func (c *Config) GetJWKSFetcherStrategy(ctx context.Context) (strategy oauthelia2.JWKSFetcherStrategy) {  	if c.Strategy.JWKSFetcher == nil { -		c.Strategy.JWKSFetcher = fosite.NewDefaultJWKSFetcherStrategy() +		c.Strategy.JWKSFetcher = oauthelia2.NewDefaultJWKSFetcherStrategy()  	}  	return c.Strategy.JWKSFetcher  }  // GetClientAuthenticationStrategy returns the client authentication strategy. -func (c *Config) GetClientAuthenticationStrategy(ctx context.Context) (strategy fosite.ClientAuthenticationStrategy) { +func (c *Config) GetClientAuthenticationStrategy(ctx context.Context) (strategy oauthelia2.ClientAuthenticationStrategy) {  	return c.Strategy.ClientAuthentication  } @@ -607,27 +626,22 @@ func (c *Config) GetFormPostHTMLTemplate(ctx context.Context) (tmpl *template.Te  	return c.Templates.GetOpenIDConnectAuthorizeResponseFormPostTemplate()  } -// GetTokenURL returns the token URL. +// GetTokenURLs returns the token URL.  func (c *Config) GetTokenURLs(ctx context.Context) (tokenURLs []string) { +	return []string{c.getEndpointURL(ctx, EndpointPathToken, c.TokenURL)} +} + +func (c *Config) getEndpointURL(ctx context.Context, path, fallback string) (endpointURL string) {  	if octx, ok := ctx.(Context); ok {  		switch issuerURL, err := octx.IssuerURL(); err {  		case nil: -			return []string{strings.ToLower(issuerURL.JoinPath(EndpointPathToken).String())} +			return strings.ToLower(issuerURL.JoinPath(path).String())  		default: -			return []string{c.TokenURL} +			return fallback  		}  	} -	return []string{c.TokenURL} -} - -// GetSecretsHasher returns the client secrets hashing function. -func (c *Config) GetSecretsHasher(ctx context.Context) (hasher fosite.Hasher) { -	if c.Hash.ClientSecrets == nil { -		c.Hash.ClientSecrets, _ = NewHasher() -	} - -	return c.Hash.ClientSecrets +	return fallback  }  // GetUseLegacyErrorFormat returns whether to use the legacy error format. @@ -638,35 +652,30 @@ func (c *Config) GetUseLegacyErrorFormat(ctx context.Context) (use bool) {  }  // GetAuthorizeEndpointHandlers returns the authorize endpoint handlers. -func (c *Config) GetAuthorizeEndpointHandlers(ctx context.Context) (handlers fosite.AuthorizeEndpointHandlers) { +func (c *Config) GetAuthorizeEndpointHandlers(ctx context.Context) (handlers oauthelia2.AuthorizeEndpointHandlers) {  	return c.Handlers.AuthorizeEndpoint  }  // GetTokenEndpointHandlers returns the token endpoint handlers. -func (c *Config) GetTokenEndpointHandlers(ctx context.Context) (handlers fosite.TokenEndpointHandlers) { +func (c *Config) GetTokenEndpointHandlers(ctx context.Context) (handlers oauthelia2.TokenEndpointHandlers) {  	return c.Handlers.TokenEndpoint  }  // GetTokenIntrospectionHandlers returns the token introspection handlers. -func (c *Config) GetTokenIntrospectionHandlers(ctx context.Context) (handlers fosite.TokenIntrospectionHandlers) { +func (c *Config) GetTokenIntrospectionHandlers(ctx context.Context) (handlers oauthelia2.TokenIntrospectionHandlers) {  	return c.Handlers.TokenIntrospection  }  // GetRevocationHandlers returns the revocation handlers. -func (c *Config) GetRevocationHandlers(ctx context.Context) (handlers fosite.RevocationHandlers) { +func (c *Config) GetRevocationHandlers(ctx context.Context) (handlers oauthelia2.RevocationHandlers) {  	return c.Handlers.Revocation  }  // GetPushedAuthorizeEndpointHandlers returns the handlers. -func (c *Config) GetPushedAuthorizeEndpointHandlers(ctx context.Context) fosite.PushedAuthorizeEndpointHandlers { +func (c *Config) GetPushedAuthorizeEndpointHandlers(ctx context.Context) oauthelia2.PushedAuthorizeEndpointHandlers {  	return c.Handlers.PushedAuthorizeEndpoint  } -// GetResponseModeHandlerExtension returns the response mode handler extension. -func (c *Config) GetResponseModeHandlerExtension(ctx context.Context) (handler fosite.ResponseModeHandler) { -	return c.Handlers.ResponseMode -} -  // GetPushedAuthorizeRequestURIPrefix is the request URI prefix. This is  // usually 'urn:ietf:params:oauth:request_uri:'.  func (c *Config) GetPushedAuthorizeRequestURIPrefix(ctx context.Context) string { @@ -677,11 +686,11 @@ func (c *Config) GetPushedAuthorizeRequestURIPrefix(ctx context.Context) string  	return c.PAR.URIPrefix  } -// EnforcePushedAuthorize indicates if PAR is enforced. In this mode, a client -// cannot pass authorize parameters at the 'authorize' endpoint. The 'authorize' endpoint +// GetRequirePushedAuthorizationRequests indicates if the use of Pushed Authorization Requests is gobally required. +// In this mode, a client cannot pass authorize parameters at the 'authorize' endpoint. The 'authorize' endpoint  // must contain the PAR request_uri. -func (c *Config) EnforcePushedAuthorize(ctx context.Context) bool { -	return c.PAR.Enforced +func (c *Config) GetRequirePushedAuthorizationRequests(ctx context.Context) (enforce bool) { +	return c.PAR.Require  }  // GetPushedAuthorizeContextLifespan is the lifespan of the short-lived PAR context. @@ -695,5 +704,65 @@ func (c *Config) GetPushedAuthorizeContextLifespan(ctx context.Context) (lifespa  // GetVerifiableCredentialsNonceLifespan is the lifespan of the verifiable credentials' nonce.  func (c *Config) GetVerifiableCredentialsNonceLifespan(ctx context.Context) (lifespan time.Duration) { -	return c.VerifiableCredentialsNonceLifespan +	if c.Lifespans.VerifiableCredentialsNonce.Seconds() == 0 { +		c.Lifespans.VerifiableCredentialsNonce = lifespanVerifiableCredentialsNonceDefault +	} + +	return c.Lifespans.VerifiableCredentialsNonce +} + +func (c *Config) GetResponseModeHandlers(ctx context.Context) []oauthelia2.ResponseModeHandler { +	return c.Handlers.ResponseMode +} + +func (c *Config) GetRevokeRefreshTokensExplicit(ctx context.Context) bool { +	return c.RevokeRefreshTokensExplicit +} + +func (c *Config) GetEnforceRevokeFlowRevokeRefreshTokensExplicitClient(ctx context.Context) bool { +	return c.EnforceRevokeFlowRevokeRefreshTokensExplicitClient +} + +func (c *Config) GetTokenURL(ctx context.Context) string { +	return c.getEndpointURL(ctx, EndpointPathToken, c.TokenURL) +} + +func (c *Config) GetRFC8628CodeLifespan(ctx context.Context) time.Duration { +	if c.Lifespans.RFC8628Code.Seconds() <= 0 { +		c.Lifespans.RFC8628Code = lifespanRFC8628CodeDefault +	} + +	return c.Lifespans.RFC8628Code +} + +func (c *Config) GetRFC8628UserVerificationURL(ctx context.Context) string { +	return c.getEndpointURL(ctx, EndpointPathRFC8628UserVerificationURL, c.RFC8628UserVerificationURL) +} + +func (c *Config) GetRFC8628TokenPollingInterval(ctx context.Context) (interval time.Duration) { +	if c.Lifespans.RFC8628Polling.Seconds() == 0 { +		c.Lifespans.RFC8628Polling = lifespanRFC8628PollingIntervalDefault +	} + +	return c.Lifespans.RFC8628Polling +} + +func (c *Config) GetRFC8628DeviceAuthorizeEndpointHandlers(ctx context.Context) oauthelia2.RFC8628DeviceAuthorizeEndpointHandlers { +	return c.Handlers.RFC8628DeviceAuthorizeEndpoint +} + +func (c *Config) GetRFC8628UserAuthorizeEndpointHandlers(ctx context.Context) oauthelia2.RFC8628UserAuthorizeEndpointHandlers { +	return c.Handlers.RFC8628UserAuthorizeEndpoint +} + +func (c *Config) GetRFC8693TokenTypes(ctx context.Context) map[string]oauthelia2.RFC8693TokenType { +	return c.RFC8693.TokenTypes +} + +func (c *Config) GetDefaultRFC8693RequestedTokenType(ctx context.Context) string { +	return c.RFC8693.DefaultRequestedTokenType +} + +func (c *Config) GetEnforceJWTProfileAccessTokens(ctx context.Context) (enforce bool) { +	return c.EnforceJWTProfileAccessTokens  } diff --git a/internal/oidc/config_test.go b/internal/oidc/config_test.go index fe404c0df..2eba722df 100644 --- a/internal/oidc/config_test.go +++ b/internal/oidc/config_test.go @@ -7,7 +7,8 @@ import (  	"testing"  	"time" -	"github.com/ory/fosite/token/jwt" +	"authelia.com/provider/oauth2/handler/oauth2" +	"authelia.com/provider/oauth2/token/jwt"  	"github.com/stretchr/testify/assert"  	"github.com/stretchr/testify/require" @@ -193,7 +194,6 @@ func TestConfig_Misc(t *testing.T) {  	assert.Nil(t, config.GetTokenIntrospectionHandlers(ctx))  	assert.Nil(t, config.GetRevocationHandlers(ctx))  	assert.Nil(t, config.GetPushedAuthorizeEndpointHandlers(ctx)) -	assert.Nil(t, config.GetResponseModeHandlerExtension(ctx))  	assert.Equal(t, []string{""}, config.GetTokenURLs(ctx)) @@ -216,13 +216,13 @@ func TestConfig_PAR(t *testing.T) {  	assert.Equal(t, "urn:ietf:params:oauth:request_uri:", config.GetPushedAuthorizeRequestURIPrefix(ctx))  	assert.Equal(t, "urn:ietf:params:oauth:request_uri:", config.PAR.URIPrefix) -	assert.False(t, config.PAR.Enforced) -	assert.False(t, config.EnforcePushedAuthorize(ctx)) -	assert.False(t, config.PAR.Enforced) +	assert.False(t, config.PAR.Require) +	assert.False(t, config.GetRequirePushedAuthorizationRequests(ctx)) +	assert.False(t, config.PAR.Require) -	config.PAR.Enforced = true +	config.PAR.Require = true -	assert.True(t, config.EnforcePushedAuthorize(ctx)) +	assert.True(t, config.GetRequirePushedAuthorizationRequests(ctx))  	assert.Equal(t, time.Duration(0), config.PAR.ContextLifespan)  	assert.Equal(t, time.Minute*5, config.GetPushedAuthorizeContextLifespan(ctx)) @@ -240,9 +240,11 @@ func TestNewConfig(t *testing.T) {  	require.NoError(t, err) -	config := oidc.NewConfig(c, nil, tmpl) +	signer := oidc.NewKeyManager(c) -	assert.IsType(t, &oidc.JWTCoreStrategy{}, config.Strategy.Core) +	config := oidc.NewConfig(c, signer, tmpl) + +	assert.IsType(t, &oauth2.JWTProfileCoreStrategy{}, config.Strategy.Core)  	config.LoadHandlers(nil) diff --git a/internal/oidc/const.go b/internal/oidc/const.go index fc0338207..37880186d 100644 --- a/internal/oidc/const.go +++ b/internal/oidc/const.go @@ -49,18 +49,14 @@ const (  )  const ( -	// ClientAssertionJWTBearerType is the JWT bearer assertion. -	ClientAssertionJWTBearerType = "urn:ietf:params:oauth:client-assertion-type:jwt-bearer" //nolint:gosec // False Positive. -) - -const httpAuthSchemeBasic = "Basic" - -const ( -	lifespanTokenDefault                   = time.Hour -	lifespanRefreshTokenDefault            = time.Hour * 24 * 30 -	lifespanAuthorizeCodeDefault           = time.Minute * 15 -	lifespanJWTSecuredAuthorizationDefault = time.Minute * 5 -	lifespanPARContextDefault              = time.Minute * 5 +	lifespanTokenDefault                      = time.Hour +	lifespanRefreshTokenDefault               = time.Hour * 24 * 30 +	lifespanAuthorizeCodeDefault              = time.Minute * 15 +	lifespanJWTSecuredAuthorizationDefault    = time.Minute * 5 +	lifespanPARContextDefault                 = time.Minute * 5 +	lifespanRFC8628CodeDefault                = time.Minute * 10 +	lifespanRFC8628PollingIntervalDefault     = time.Second * 10 +	lifespanVerifiableCredentialsNonceDefault = time.Hour  )  const ( @@ -159,26 +155,14 @@ const (  )  const ( -	FormParameterState               = "state" -	FormParameterAuthorizationCode   = "code" -	FormParameterClientID            = valueClientID -	FormParameterClientSecret        = "client_secret" -	FormParameterRequestURI          = "request_uri" -	FormParameterRedirectURI         = "redirect_uri" -	FormParameterResponse            = "response" -	FormParameterResponseMode        = "response_mode" -	FormParameterResponseType        = "response_type" -	FormParameterCodeChallenge       = "code_challenge" -	FormParameterCodeVerifier        = "code_verifier" -	FormParameterCodeChallengeMethod = "code_challenge_method" -	FormParameterClientAssertionType = "client_assertion_type" -	FormParameterClientAssertion     = "client_assertion" -	FormParameterScope               = valueScope -	FormParameterAudience            = "audience" -	FormParameterRefreshToken        = valueRefreshToken -	FormParameterIssuer              = valueIss -	FormParameterToken               = "token" -	FormParameterTokenTypeHint       = "token_type_hint" +	FormParameterState        = "state" +	FormParameterClientID     = valueClientID +	FormParameterRequestURI   = "request_uri" +	FormParameterRedirectURI  = "redirect_uri" +	FormParameterResponseMode = "response_mode" +	FormParameterResponseType = "response_type" +	FormParameterScope        = valueScope +	FormParameterIssuer       = valueIss  )  const ( @@ -215,20 +199,6 @@ const (  	JWTHeaderTypeValueAccessTokenJWT        = "at+jwt"  ) -const ( -	headerContentTypeTextHTML        = "text/html; charset=utf-8" -	headerContentTypeApplicationJSON = "application/json; charset=utf-8" -) - -const ( -	tokenPrefixOrgAutheliaFmt = "authelia_%s_" //nolint:gosec -	tokenPrefixOrgOryFmt      = "ory_%s_"      //nolint:gosec - -	TokenPrefixPartAccessToken   = "at" -	TokenPrefixPartRefreshToken  = "rt" -	TokenPrefixPartAuthorizeCode = "ac" -) -  // Paths.  const (  	EndpointPathConsent                           = "/consent" @@ -245,6 +215,8 @@ const (  	EndpointPathRevocation    = EndpointPathRoot + "/" + EndpointRevocation  	EndpointPathPushedAuthorizationRequest = EndpointPathRoot + "/" + EndpointPushedAuthorizationRequest + +	EndpointPathRFC8628UserVerificationURL = EndpointPathRoot + "/device-code/user-verification"  )  // Authentication Method Reference Values https://datatracker.ietf.org/doc/html/rfc8176 diff --git a/internal/oidc/const_test.go b/internal/oidc/const_test.go index cff4f0497..9d53102c5 100644 --- a/internal/oidc/const_test.go +++ b/internal/oidc/const_test.go @@ -27,11 +27,6 @@ const (  )  const ( -	rs256 = "rs256" -	es512 = "es512" -) - -const (  	abc = "abc"  ) diff --git a/internal/oidc/core_strategy_hmac.go b/internal/oidc/core_strategy_hmac.go deleted file mode 100644 index 2ad0f0a3e..000000000 --- a/internal/oidc/core_strategy_hmac.go +++ /dev/null @@ -1,128 +0,0 @@ -package oidc - -import ( -	"context" -	"fmt" -	"strings" -	"time" - -	"github.com/ory/fosite" -	"github.com/ory/fosite/token/hmac" -	"github.com/ory/x/errorsx" -) - -// HMACCoreStrategy implements oauth2.CoreStrategy. It's a copy of the oauth2.HMACSHAStrategy. -type HMACCoreStrategy struct { -	Enigma *hmac.HMACStrategy -	Config interface { -		fosite.AccessTokenLifespanProvider -		fosite.RefreshTokenLifespanProvider -		fosite.AuthorizeCodeLifespanProvider -	} -} - -// AccessTokenSignature implements oauth2.AccessTokenStrategy. -func (h *HMACCoreStrategy) AccessTokenSignature(ctx context.Context, tokenString string) string { -	return h.Enigma.Signature(tokenString) -} - -// GenerateAccessToken implements oauth2.AccessTokenStrategy. -func (h *HMACCoreStrategy) GenerateAccessToken(ctx context.Context, _ fosite.Requester) (tokenString string, sig string, err error) { -	if tokenString, sig, err = h.Enigma.Generate(ctx); err != nil { -		return "", "", err -	} - -	return h.setPrefix(tokenString, TokenPrefixPartAccessToken), sig, nil -} - -// ValidateAccessToken implements oauth2.AccessTokenStrategy. -func (h *HMACCoreStrategy) ValidateAccessToken(ctx context.Context, r fosite.Requester, tokenString string) (err error) { -	var exp = r.GetSession().GetExpiresAt(fosite.AccessToken) -	if exp.IsZero() && r.GetRequestedAt().Add(h.Config.GetAccessTokenLifespan(ctx)).Before(time.Now().UTC()) { -		return errorsx.WithStack(fosite.ErrTokenExpired.WithHintf("Access token expired at '%s'.", r.GetRequestedAt().Add(h.Config.GetAccessTokenLifespan(ctx)))) -	} - -	if !exp.IsZero() && exp.Before(time.Now().UTC()) { -		return errorsx.WithStack(fosite.ErrTokenExpired.WithHintf("Access token expired at '%s'.", exp)) -	} - -	return h.Enigma.Validate(ctx, h.trimPrefix(tokenString, TokenPrefixPartAccessToken)) -} - -// RefreshTokenSignature implements oauth2.RefreshTokenStrategy. -func (h *HMACCoreStrategy) RefreshTokenSignature(ctx context.Context, tokenString string) string { -	return h.Enigma.Signature(tokenString) -} - -// GenerateRefreshToken implements oauth2.RefreshTokenStrategy. -func (h *HMACCoreStrategy) GenerateRefreshToken(ctx context.Context, _ fosite.Requester) (tokenString string, sig string, err error) { -	if tokenString, sig, err = h.Enigma.Generate(ctx); err != nil { -		return "", "", err -	} - -	return h.setPrefix(tokenString, TokenPrefixPartRefreshToken), sig, nil -} - -// ValidateRefreshToken implements oauth2.RefreshTokenStrategy. -func (h *HMACCoreStrategy) ValidateRefreshToken(ctx context.Context, r fosite.Requester, tokenString string) (err error) { -	var exp = r.GetSession().GetExpiresAt(fosite.RefreshToken) - -	if exp.IsZero() { -		return h.Enigma.Validate(ctx, h.trimPrefix(tokenString, TokenPrefixPartRefreshToken)) -	} - -	if exp.Before(time.Now().UTC()) { -		return errorsx.WithStack(fosite.ErrTokenExpired.WithHintf("Refresh token expired at '%s'.", exp)) -	} - -	return h.Enigma.Validate(ctx, h.trimPrefix(tokenString, TokenPrefixPartRefreshToken)) -} - -// AuthorizeCodeSignature implements oauth2.AuthorizeCodeStrategy. -func (h *HMACCoreStrategy) AuthorizeCodeSignature(ctx context.Context, token string) string { -	return h.Enigma.Signature(token) -} - -// GenerateAuthorizeCode implements oauth2.AuthorizeCodeStrategy. -func (h *HMACCoreStrategy) GenerateAuthorizeCode(ctx context.Context, _ fosite.Requester) (tokenString string, sig string, err error) { -	if tokenString, sig, err = h.Enigma.Generate(ctx); err != nil { -		return "", "", err -	} - -	return h.setPrefix(tokenString, TokenPrefixPartAuthorizeCode), sig, nil -} - -// ValidateAuthorizeCode implements oauth2.AuthorizeCodeStrategy. -func (h *HMACCoreStrategy) ValidateAuthorizeCode(ctx context.Context, r fosite.Requester, tokenString string) (err error) { -	var exp = r.GetSession().GetExpiresAt(fosite.AuthorizeCode) - -	if exp.IsZero() && r.GetRequestedAt().Add(h.Config.GetAuthorizeCodeLifespan(ctx)).Before(time.Now().UTC()) { -		return errorsx.WithStack(fosite.ErrTokenExpired.WithHintf("Authorize code expired at '%s'.", r.GetRequestedAt().Add(h.Config.GetAuthorizeCodeLifespan(ctx)))) -	} - -	if !exp.IsZero() && exp.Before(time.Now().UTC()) { -		return errorsx.WithStack(fosite.ErrTokenExpired.WithHintf("Authorize code expired at '%s'.", exp)) -	} - -	return h.Enigma.Validate(ctx, h.trimPrefix(tokenString, TokenPrefixPartAuthorizeCode)) -} - -func (h *HMACCoreStrategy) getPrefix(part string) string { -	return h.getCustomPrefix(tokenPrefixOrgAutheliaFmt, part) -} - -func (h *HMACCoreStrategy) getCustomPrefix(tokenPrefixFmt, part string) string { -	return fmt.Sprintf(tokenPrefixFmt, part) -} - -func (h *HMACCoreStrategy) setPrefix(tokenString, part string) string { -	return h.getPrefix(part) + tokenString -} - -func (h *HMACCoreStrategy) trimPrefix(tokenString, part string) string { -	if strings.HasPrefix(tokenString, h.getCustomPrefix(tokenPrefixOrgOryFmt, part)) { -		return strings.TrimPrefix(tokenString, h.getCustomPrefix(tokenPrefixOrgOryFmt, part)) -	} - -	return strings.TrimPrefix(tokenString, h.getPrefix(part)) -} diff --git a/internal/oidc/core_strategy_hmac_blackbox_test.go b/internal/oidc/core_strategy_hmac_blackbox_test.go deleted file mode 100644 index 214a77695..000000000 --- a/internal/oidc/core_strategy_hmac_blackbox_test.go +++ /dev/null @@ -1,124 +0,0 @@ -package oidc_test - -import ( -	"context" -	"fmt" -	"regexp" -	"testing" -	"time" - -	"github.com/ory/fosite" -	"github.com/ory/fosite/token/hmac" -	"github.com/stretchr/testify/assert" - -	"github.com/authelia/authelia/v4/internal/configuration/schema" -	"github.com/authelia/authelia/v4/internal/oidc" -) - -func TestHMACCoreStrategy(t *testing.T) { -	goodsecret := []byte("R7VCSUfnKc7Y5zE84q6GstYqfMGjL4wM") -	secreta := []byte("a") - -	config := &oidc.Config{ -		TokenEntropy: 10, -		GlobalSecret: secreta, -		Lifespans: schema.IdentityProvidersOpenIDConnectLifespanToken{ -			AccessToken:   time.Hour, -			RefreshToken:  time.Hour, -			AuthorizeCode: time.Minute, -		}, -	} - -	strategy := &oidc.HMACCoreStrategy{ -		Enigma: &hmac.HMACStrategy{Config: config}, -		Config: config, -	} - -	var ( -		token, signature string -		err              error -	) - -	ctx := context.Background() - -	token, signature, err = strategy.GenerateAuthorizeCode(ctx, &fosite.Request{}) -	assert.EqualError(t, err, "secret for signing HMAC-SHA512/256 is expected to be 32 byte long, got 1 byte") -	assert.Empty(t, token) -	assert.Empty(t, signature) - -	config.GlobalSecret = goodsecret - -	token, signature, err = strategy.GenerateAuthorizeCode(ctx, &fosite.Request{}) -	assert.NoError(t, err) - -	assert.NotEmpty(t, token) -	assert.NotEmpty(t, signature) -	assert.Equal(t, signature, strategy.AuthorizeCodeSignature(ctx, token)) -	assert.Regexp(t, regexp.MustCompile(`^authelia_ac_`), token) - -	assert.NoError(t, strategy.ValidateAuthorizeCode(ctx, &fosite.Request{RequestedAt: time.Now(), Session: &fosite.DefaultSession{}}, token)) -	assert.NoError(t, strategy.ValidateAuthorizeCode(ctx, &fosite.Request{RequestedAt: time.Now(), Session: &fosite.DefaultSession{}}, token)) -	assert.EqualError(t, strategy.ValidateAuthorizeCode(ctx, &fosite.Request{RequestedAt: time.Now().Add(time.Hour * -2400), Session: &fosite.DefaultSession{}}, token), "invalid_token") -	assert.NoError(t, strategy.ValidateAuthorizeCode(ctx, &fosite.Request{RequestedAt: time.Now().Add(time.Hour * -2400), Session: &fosite.DefaultSession{ExpiresAt: map[fosite.TokenType]time.Time{fosite.AuthorizeCode: time.Now().Add(100 * time.Hour)}}}, token)) -	assert.EqualError(t, strategy.ValidateAuthorizeCode(ctx, &fosite.Request{RequestedAt: time.Now(), Session: &fosite.DefaultSession{ExpiresAt: map[fosite.TokenType]time.Time{fosite.AuthorizeCode: time.Now().Add(-100 * time.Second)}}}, token), "invalid_token") - -	token, signature, err = strategy.GenerateRefreshToken(ctx, &fosite.Request{}) -	assert.NoError(t, err) - -	assert.NotEmpty(t, token) -	assert.NotEmpty(t, signature) -	assert.Equal(t, signature, strategy.RefreshTokenSignature(ctx, token)) -	assert.Regexp(t, regexp.MustCompile(`^authelia_rt_`), token) - -	assert.NoError(t, strategy.ValidateRefreshToken(ctx, &fosite.Request{RequestedAt: time.Now(), Session: &fosite.DefaultSession{}}, token)) -	assert.NoError(t, strategy.ValidateRefreshToken(ctx, &fosite.Request{RequestedAt: time.Now(), Session: &fosite.DefaultSession{}}, token)) -	assert.NoError(t, strategy.ValidateRefreshToken(ctx, &fosite.Request{RequestedAt: time.Now().Add(time.Hour * -2400), Session: &fosite.DefaultSession{ExpiresAt: map[fosite.TokenType]time.Time{fosite.RefreshToken: time.Now().Add(100 * time.Hour)}}}, token)) -	assert.EqualError(t, strategy.ValidateRefreshToken(ctx, &fosite.Request{RequestedAt: time.Now(), Session: &fosite.DefaultSession{ExpiresAt: map[fosite.TokenType]time.Time{fosite.RefreshToken: time.Now().Add(-100 * time.Second)}}}, token), "invalid_token") - -	token, signature, err = strategy.GenerateAccessToken(ctx, &fosite.Request{}) -	assert.NoError(t, err) - -	assert.NotEmpty(t, token) -	assert.NotEmpty(t, signature) -	assert.Equal(t, signature, strategy.AccessTokenSignature(ctx, token)) -	assert.Regexp(t, regexp.MustCompile(`^authelia_at_`), token) - -	assert.NoError(t, strategy.ValidateAccessToken(ctx, &fosite.Request{RequestedAt: time.Now(), Session: &fosite.DefaultSession{}}, token)) -	assert.NoError(t, strategy.ValidateAccessToken(ctx, &fosite.Request{RequestedAt: time.Now(), Session: &fosite.DefaultSession{}}, token)) -	assert.EqualError(t, strategy.ValidateAccessToken(ctx, &fosite.Request{RequestedAt: time.Now().Add(time.Hour * -2400), Session: &fosite.DefaultSession{}}, token), "invalid_token") -	assert.NoError(t, strategy.ValidateAccessToken(ctx, &fosite.Request{RequestedAt: time.Now().Add(time.Hour * -2400), Session: &fosite.DefaultSession{ExpiresAt: map[fosite.TokenType]time.Time{fosite.AccessToken: time.Now().Add(100 * time.Hour)}}}, token)) -	assert.EqualError(t, strategy.ValidateAccessToken(ctx, &fosite.Request{RequestedAt: time.Now(), Session: &fosite.DefaultSession{ExpiresAt: map[fosite.TokenType]time.Time{fosite.AccessToken: time.Now().Add(-100 * time.Second)}}}, token), "invalid_token") - -	badconfig := &BadGlobalSecretConfig{ -		Config: config, -	} - -	badstrategy := &oidc.HMACCoreStrategy{ -		Enigma: &hmac.HMACStrategy{Config: badconfig}, -		Config: badconfig, -	} - -	token, signature, err = badstrategy.GenerateRefreshToken(ctx, &fosite.Request{}) -	assert.Equal(t, "", token) -	assert.Equal(t, "", signature) -	assert.EqualError(t, oidc.ErrorToDebugRFC6749Error(err), "bad secret") - -	token, signature, err = badstrategy.GenerateAccessToken(ctx, &fosite.Request{}) -	assert.Equal(t, "", token) -	assert.Equal(t, "", signature) -	assert.EqualError(t, oidc.ErrorToDebugRFC6749Error(err), "bad secret") - -	token, signature, err = badstrategy.GenerateAuthorizeCode(ctx, &fosite.Request{}) - -	assert.Equal(t, "", token) -	assert.Equal(t, "", signature) -	assert.EqualError(t, oidc.ErrorToDebugRFC6749Error(err), "bad secret") -} - -type BadGlobalSecretConfig struct { -	*oidc.Config -} - -func (*BadGlobalSecretConfig) GetGlobalSecret(ctx context.Context) ([]byte, error) { -	return nil, fmt.Errorf("bad secret") -} diff --git a/internal/oidc/core_strategy_hmac_whitebox_test.go b/internal/oidc/core_strategy_hmac_whitebox_test.go deleted file mode 100644 index 261b22a8e..000000000 --- a/internal/oidc/core_strategy_hmac_whitebox_test.go +++ /dev/null @@ -1,56 +0,0 @@ -package oidc - -import ( -	"fmt" -	"strings" -	"testing" - -	"github.com/stretchr/testify/assert" -) - -func TestHMACCoreStrategy_TrimPrefix(t *testing.T) { -	testCases := []struct { -		name     string -		have     string -		part     string -		expected string -	}{ -		{"ShouldTrimAutheliaPrefix", "authelia_at_example", TokenPrefixPartAccessToken, "example"}, -		{"ShouldTrimOryPrefix", "ory_at_example", TokenPrefixPartAccessToken, "example"}, -		{"ShouldTrimOnlyAutheliaPrefix", "authelia_at_ory_at_example", TokenPrefixPartAccessToken, "ory_at_example"}, -		{"ShouldTrimOnlyOryPrefix", "ory_at_authelia_at_example", TokenPrefixPartAccessToken, "authelia_at_example"}, -		{"ShouldNotTrimGitHubPrefix", "gh_at_example", TokenPrefixPartAccessToken, "gh_at_example"}, -	} - -	strategy := &HMACCoreStrategy{} - -	for _, tc := range testCases { -		t.Run(tc.name, func(t *testing.T) { -			assert.Equal(t, tc.expected, strategy.trimPrefix(tc.have, tc.part)) -		}) -	} -} - -func TestHMACCoreStrategy_GetSetPrefix(t *testing.T) { -	testCases := []struct { -		name        string -		have        string -		expectedSet string -		expectedGet string -	}{ -		{"ShouldAddPrefix", "example", "authelia_%s_example", "authelia_%s_"}, -	} - -	strategy := &HMACCoreStrategy{} - -	for _, tc := range testCases { -		t.Run(tc.name, func(t *testing.T) { -			for _, part := range []string{TokenPrefixPartAccessToken, TokenPrefixPartAuthorizeCode, TokenPrefixPartRefreshToken} { -				t.Run(strings.ToUpper(part), func(t *testing.T) { -					assert.Equal(t, fmt.Sprintf(tc.expectedSet, part), strategy.setPrefix(tc.have, part)) -					assert.Equal(t, fmt.Sprintf(tc.expectedGet, part), strategy.getPrefix(part)) -				}) -			} -		}) -	} -} diff --git a/internal/oidc/core_strategy_jwt.go b/internal/oidc/core_strategy_jwt.go deleted file mode 100644 index cc42de237..000000000 --- a/internal/oidc/core_strategy_jwt.go +++ /dev/null @@ -1,172 +0,0 @@ -package oidc - -import ( -	"context" -	"fmt" -	"strings" -	"time" - -	"github.com/ory/fosite" -	"github.com/ory/fosite/handler/oauth2" -	"github.com/ory/fosite/token/jwt" -	"github.com/ory/x/errorsx" -	"github.com/pkg/errors" -) - -// JWTCoreStrategy wraps the HMACCoreStrategy for the purpose of -// implementing RFC9068 JWT Profile for OAuth 2.0 Access Tokens. -type JWTCoreStrategy struct { -	jwt.Signer - -	HMACCoreStrategy *HMACCoreStrategy -	Config           interface { -		fosite.AccessTokenIssuerProvider -		fosite.JWTScopeFieldProvider -	} -} - -// AccessTokenSignature implements oauth2.AccessTokenStrategy. -func (s *JWTCoreStrategy) AccessTokenSignature(ctx context.Context, token string) (signature string) { -	var ok bool - -	if ok, signature = isAccessTokenJWT(token); ok { -		return signature -	} - -	return s.HMACCoreStrategy.AccessTokenSignature(ctx, token) -} - -// GenerateAccessToken implements oauth2.AccessTokenStrategy. -func (s *JWTCoreStrategy) GenerateAccessToken(ctx context.Context, requester fosite.Requester) (token string, signature string, err error) { -	var ( -		client Client -		ok     bool -	) - -	if client, ok = requester.GetClient().(Client); ok && client.GetJWTProfileOAuthAccessTokensEnabled() { -		return s.GenerateJWT(ctx, fosite.AccessToken, requester) -	} - -	return s.HMACCoreStrategy.GenerateAccessToken(ctx, requester) -} - -// ValidateAccessToken implements oauth2.AccessTokenStrategy. -func (s *JWTCoreStrategy) ValidateAccessToken(ctx context.Context, requester fosite.Requester, token string) (err error) { -	if ok, _ := isAccessTokenJWT(token); ok { -		_, err = jwtValidate(ctx, s.Signer, token) - -		return err -	} - -	return s.HMACCoreStrategy.ValidateAccessToken(ctx, requester, token) -} - -// RefreshTokenSignature implements oauth2.RefreshTokenStrategy. -func (s *JWTCoreStrategy) RefreshTokenSignature(ctx context.Context, token string) (signature string) { -	return s.HMACCoreStrategy.RefreshTokenSignature(ctx, token) -} - -// GenerateRefreshToken implements oauth2.RefreshTokenStrategy. -func (s *JWTCoreStrategy) GenerateRefreshToken(ctx context.Context, req fosite.Requester) (token string, signature string, err error) { -	return s.HMACCoreStrategy.GenerateRefreshToken(ctx, req) -} - -// ValidateRefreshToken implements oauth2.RefreshTokenStrategy. -func (s *JWTCoreStrategy) ValidateRefreshToken(ctx context.Context, req fosite.Requester, token string) error { -	return s.HMACCoreStrategy.ValidateRefreshToken(ctx, req, token) -} - -// AuthorizeCodeSignature implements oauth2.AuthorizeCodeStrategy. -func (s *JWTCoreStrategy) AuthorizeCodeSignature(ctx context.Context, token string) (signature string) { -	return s.HMACCoreStrategy.AuthorizeCodeSignature(ctx, token) -} - -// GenerateAuthorizeCode implements oauth2.AuthorizeCodeStrategy. -func (s *JWTCoreStrategy) GenerateAuthorizeCode(ctx context.Context, req fosite.Requester) (token string, signature string, err error) { -	return s.HMACCoreStrategy.GenerateAuthorizeCode(ctx, req) -} - -// ValidateAuthorizeCode implements oauth2.AuthorizeCodeStrategy. -func (s *JWTCoreStrategy) ValidateAuthorizeCode(ctx context.Context, req fosite.Requester, token string) error { -	return s.HMACCoreStrategy.ValidateAuthorizeCode(ctx, req, token) -} - -func jwtValidate(ctx context.Context, signer jwt.Signer, rawToken string) (token *jwt.Token, err error) { -	if token, err = signer.Decode(ctx, rawToken); err == nil { -		return token, token.Claims.Valid() -	} - -	var e *jwt.ValidationError - -	if err != nil && errors.As(err, &e) { -		return token, errorsx.WithStack(jwtValidationErrorToRFC6749Error(e).WithWrap(err).WithDebug(ErrorToDebugRFC6749Error(err).Error())) -	} - -	return token, nil -} - -func (s *JWTCoreStrategy) GenerateJWT(ctx context.Context, tokenType fosite.TokenType, requester fosite.Requester) (string, string, error) { -	var ( -		session oauth2.JWTSessionContainer -		ok      bool -		claims  jwt.JWTClaimsContainer -	) - -	if session, ok = requester.GetSession().(oauth2.JWTSessionContainer); !ok { -		return "", "", errors.Errorf("Session must be of type JWTSessionContainer but got type: %T", requester.GetSession()) -	} - -	if claims = session.GetJWTClaims(); claims == nil { -		return "", "", errors.New("JWT Claims must not be nil") -	} - -	return s.Signer.Generate(ctx, claims. -		With( -			session.GetExpiresAt(tokenType), -			requester.GetGrantedScopes(), -			requester.GetGrantedAudience(), -		). -		WithDefaults( -			time.Now().UTC(), -			s.Config.GetAccessTokenIssuer(ctx), -		). -		WithScopeField( -			s.Config.GetJWTScopeField(ctx), -		).ToMapClaims(), session.GetJWTHeader()) -} - -func isAccessTokenJWT(token string) (jwt bool, signature string) { -	parts := strings.Split(token, ".") - -	if len(parts) != 3 { -		return false, "" -	} - -	if strings.HasPrefix(token, fmt.Sprintf(tokenPrefixOrgAutheliaFmt, TokenPrefixPartAccessToken)) { -		return false, "" -	} - -	return true, parts[2] -} - -func jwtValidationErrorToRFC6749Error(v *jwt.ValidationError) *fosite.RFC6749Error { -	switch { -	case v == nil: -		return nil -	case v.Has(jwt.ValidationErrorMalformed): -		return fosite.ErrInvalidTokenFormat -	case v.Has(jwt.ValidationErrorUnverifiable | jwt.ValidationErrorSignatureInvalid): -		return fosite.ErrTokenSignatureMismatch -	case v.Has(jwt.ValidationErrorExpired): -		return fosite.ErrTokenExpired -	case v.Has(jwt.ValidationErrorAudience | -		jwt.ValidationErrorIssuedAt | -		jwt.ValidationErrorIssuer | -		jwt.ValidationErrorNotValidYet | -		jwt.ValidationErrorId | -		jwt.ValidationErrorClaimsInvalid): -		return fosite.ErrTokenClaim -	default: -		return fosite.ErrRequestUnauthorized -	} -} diff --git a/internal/oidc/core_strategy_jwt_blackbox_test.go b/internal/oidc/core_strategy_jwt_blackbox_test.go deleted file mode 100644 index 3f3af2945..000000000 --- a/internal/oidc/core_strategy_jwt_blackbox_test.go +++ /dev/null @@ -1,131 +0,0 @@ -package oidc_test - -import ( -	"context" -	"regexp" -	"strings" -	"testing" -	"time" - -	"github.com/ory/fosite" -	"github.com/ory/fosite/token/hmac" -	"github.com/ory/fosite/token/jwt" -	"github.com/stretchr/testify/assert" - -	"github.com/authelia/authelia/v4/internal/configuration/schema" -	"github.com/authelia/authelia/v4/internal/oidc" -) - -func TestJWTCoreStrategy(t *testing.T) { -	goodsecret := []byte("R7VCSUfnKc7Y5zE84q6GstYqfMGjL4wM") -	secreta := []byte("a") - -	config := &oidc.Config{ -		TokenEntropy: 10, -		GlobalSecret: secreta, -		Lifespans: schema.IdentityProvidersOpenIDConnectLifespanToken{ -			AccessToken:   time.Hour, -			RefreshToken:  time.Hour, -			AuthorizeCode: time.Minute, -		}, -	} - -	strategy := &oidc.JWTCoreStrategy{ -		Signer: &jwt.DefaultSigner{ -			GetPrivateKey: func(ctx context.Context) (interface{}, error) { -				return x509PrivateKeyRSA2048, nil -			}, -		}, -		HMACCoreStrategy: &oidc.HMACCoreStrategy{ -			Enigma: &hmac.HMACStrategy{Config: config}, -			Config: config, -		}, -		Config: config, -	} - -	var ( -		token, signature string -		err              error -	) - -	ctx := context.Background() - -	token, signature, err = strategy.GenerateAuthorizeCode(ctx, &fosite.Request{}) -	assert.EqualError(t, err, "secret for signing HMAC-SHA512/256 is expected to be 32 byte long, got 1 byte") -	assert.Empty(t, token) -	assert.Empty(t, signature) - -	config.GlobalSecret = goodsecret - -	token, signature, err = strategy.GenerateAuthorizeCode(ctx, &fosite.Request{}) -	assert.NoError(t, err) - -	assert.NotEmpty(t, token) -	assert.NotEmpty(t, signature) -	assert.Equal(t, signature, strategy.AuthorizeCodeSignature(ctx, token)) -	assert.Regexp(t, regexp.MustCompile(`^authelia_ac_`), token) - -	assert.NoError(t, strategy.ValidateAuthorizeCode(ctx, &fosite.Request{RequestedAt: time.Now(), Session: &fosite.DefaultSession{}}, token)) -	assert.NoError(t, strategy.ValidateAuthorizeCode(ctx, &fosite.Request{RequestedAt: time.Now(), Session: &fosite.DefaultSession{}}, token)) -	assert.EqualError(t, strategy.ValidateAuthorizeCode(ctx, &fosite.Request{RequestedAt: time.Now().Add(time.Hour * -2400), Session: &fosite.DefaultSession{}}, token), "invalid_token") -	assert.NoError(t, strategy.ValidateAuthorizeCode(ctx, &fosite.Request{RequestedAt: time.Now().Add(time.Hour * -2400), Session: &fosite.DefaultSession{ExpiresAt: map[fosite.TokenType]time.Time{fosite.AuthorizeCode: time.Now().Add(100 * time.Hour)}}}, token)) -	assert.EqualError(t, strategy.ValidateAuthorizeCode(ctx, &fosite.Request{RequestedAt: time.Now(), Session: &fosite.DefaultSession{ExpiresAt: map[fosite.TokenType]time.Time{fosite.AuthorizeCode: time.Now().Add(-100 * time.Second)}}}, token), "invalid_token") - -	token, signature, err = strategy.GenerateRefreshToken(ctx, &fosite.Request{}) -	assert.NoError(t, err) - -	assert.NotEmpty(t, token) -	assert.NotEmpty(t, signature) -	assert.Equal(t, signature, strategy.RefreshTokenSignature(ctx, token)) -	assert.Regexp(t, regexp.MustCompile(`^authelia_rt_`), token) - -	assert.NoError(t, strategy.ValidateRefreshToken(ctx, &fosite.Request{RequestedAt: time.Now(), Session: &fosite.DefaultSession{}}, token)) -	assert.NoError(t, strategy.ValidateRefreshToken(ctx, &fosite.Request{RequestedAt: time.Now(), Session: &fosite.DefaultSession{}}, token)) -	assert.NoError(t, strategy.ValidateRefreshToken(ctx, &fosite.Request{RequestedAt: time.Now().Add(time.Hour * -2400), Session: &fosite.DefaultSession{ExpiresAt: map[fosite.TokenType]time.Time{fosite.RefreshToken: time.Now().Add(100 * time.Hour)}}}, token)) -	assert.EqualError(t, strategy.ValidateRefreshToken(ctx, &fosite.Request{RequestedAt: time.Now(), Session: &fosite.DefaultSession{ExpiresAt: map[fosite.TokenType]time.Time{fosite.RefreshToken: time.Now().Add(-100 * time.Second)}}}, token), "invalid_token") - -	token, signature, err = strategy.GenerateAccessToken(ctx, &fosite.Request{}) -	assert.NoError(t, err) - -	assert.NotEmpty(t, token) -	assert.NotEmpty(t, signature) -	assert.Equal(t, signature, strategy.AccessTokenSignature(ctx, token)) -	assert.Regexp(t, regexp.MustCompile(`^authelia_at_`), token) - -	assert.NoError(t, strategy.ValidateAccessToken(ctx, &fosite.Request{RequestedAt: time.Now(), Session: &fosite.DefaultSession{}}, token)) -	assert.EqualError(t, strategy.ValidateAccessToken(ctx, &fosite.Request{RequestedAt: time.Now().Add(time.Hour * -2400), Session: &fosite.DefaultSession{}}, token), "invalid_token") -	assert.NoError(t, strategy.ValidateAccessToken(ctx, &fosite.Request{RequestedAt: time.Now().Add(time.Hour * -2400), Session: &fosite.DefaultSession{ExpiresAt: map[fosite.TokenType]time.Time{fosite.AccessToken: time.Now().Add(100 * time.Hour)}}}, token)) -	assert.EqualError(t, strategy.ValidateAccessToken(ctx, &fosite.Request{RequestedAt: time.Now(), Session: &fosite.DefaultSession{ExpiresAt: map[fosite.TokenType]time.Time{fosite.AccessToken: time.Now().Add(-100 * time.Second)}}}, token), "invalid_token") - -	token, signature, err = strategy.GenerateAccessToken(ctx, &fosite.Request{Client: &oidc.RegisteredClient{AccessTokenSignedResponseAlg: oidc.SigningAlgRSAUsingSHA256}}) -	assert.Equal(t, "", token) -	assert.Equal(t, "", signature) -	assert.EqualError(t, err, "Session must be of type JWTSessionContainer but got type: <nil>") - -	token, signature, err = strategy.GenerateAccessToken(ctx, &fosite.Request{Client: &oidc.RegisteredClient{AccessTokenSignedResponseAlg: oidc.SigningAlgRSAUsingSHA256}, Session: oidc.NewSession()}) -	assert.Regexp(t, regexp.MustCompile(`^[a-zA-Z0-9-_]+\.[a-zA-Z0-9-_]+\.[a-zA-Z0-9-_]+$`), token) -	assert.Regexp(t, regexp.MustCompile(`^[a-zA-Z0-9-_]+$`), signature) -	assert.True(t, strings.HasSuffix(token, signature)) -	assert.NoError(t, err) - -	assert.NoError(t, strategy.ValidateAccessToken(ctx, &fosite.Request{RequestedAt: time.Now(), Session: &fosite.DefaultSession{}}, token)) -	assert.Equal(t, signature, strategy.AccessTokenSignature(ctx, token)) -	assert.EqualError(t, oidc.ErrorToDebugRFC6749Error(strategy.ValidateAccessToken(ctx, &fosite.Request{RequestedAt: time.Now(), Session: &fosite.DefaultSession{}}, strings.Replace(token, signature, "qePeTyHu389VN_1woLEGR2v1LDJxUWhxrZZfDgUEf_hPtdnRKZv9fVLWJFNI06r87sC9Uu7IjuLqzAuqjwnE86BKZLYkMf780fPr-73Ohoq4jXUQI40uUodxaY4LVPuvq_5W2bAqLm5F03snKOYDQc_GQggek4SVmyDKqSUdvH4M5KXFhp2XyCu7BYv-retZG3K5Z0s_VS_tE8FF_S7_k1MXqSv_wwndmrn8ik-58bXlQe1bAHpvWCrtVQFJWEdtGaQoVDK40PHzLEaWEx47ys8jnAM4-rwNoBbxbP9NnK4Y1XRD1hzOpMYJ7UGa7hUwaIoOkmfEuhWGUZnNeyQRHQ", 1))), "Token signature mismatch. Check that you provided  a valid token in the right format. go-jose/go-jose: error in cryptographic primitive") - -	token, signature, err = strategy.GenerateAccessToken(ctx, &fosite.Request{Client: &oidc.RegisteredClient{AccessTokenSignedResponseAlg: oidc.SigningAlgRSAUsingSHA256}, Session: &BadJWTSessionContainer{Session: &fosite.DefaultSession{}}}) -	assert.EqualError(t, err, "JWT Claims must not be nil") -	assert.Empty(t, token) -	assert.Empty(t, signature) -} - -type BadJWTSessionContainer struct { -	fosite.Session -} - -func (c *BadJWTSessionContainer) GetJWTClaims() jwt.JWTClaimsContainer { -	return nil -} - -func (c *BadJWTSessionContainer) GetJWTHeader() *jwt.Headers { -	return nil -} diff --git a/internal/oidc/discovery.go b/internal/oidc/discovery.go index d1a25f7e5..cd9fdec5d 100644 --- a/internal/oidc/discovery.go +++ b/internal/oidc/discovery.go @@ -126,7 +126,7 @@ func NewOpenIDConnectWellKnownConfiguration(c *schema.IdentityProvidersOpenIDCon  				},  			},  			OAuth2PushedAuthorizationDiscoveryOptions: &OAuth2PushedAuthorizationDiscoveryOptions{ -				RequirePushedAuthorizationRequests: c.PAR.Enforce, +				RequirePushedAuthorizationRequests: c.RequirePushedAuthorizationRequests,  			},  			OAuth2IssuerIdentificationDiscoveryOptions: &OAuth2IssuerIdentificationDiscoveryOptions{  				AuthorizationResponseIssuerParameterSupported: true, diff --git a/internal/oidc/discovery_test.go b/internal/oidc/discovery_test.go index 0ba06016d..53286ce44 100644 --- a/internal/oidc/discovery_test.go +++ b/internal/oidc/discovery_test.go @@ -68,11 +68,9 @@ func TestNewOpenIDConnectWellKnownConfiguration(t *testing.T) {  	for _, tc := range testCases {  		t.Run(tc.desc, func(t *testing.T) {  			c := schema.IdentityProvidersOpenIDConnect{ -				EnablePKCEPlainChallenge: tc.pkcePlainChallenge, -				PAR: schema.IdentityProvidersOpenIDConnectPAR{ -					Enforce: tc.enforcePAR, -				}, -				Discovery: tc.discovery, +				EnablePKCEPlainChallenge:           tc.pkcePlainChallenge, +				RequirePushedAuthorizationRequests: tc.enforcePAR, +				Discovery:                          tc.discovery,  			}  			actual := oidc.NewOpenIDConnectWellKnownConfiguration(&c) diff --git a/internal/oidc/errors.go b/internal/oidc/errors.go index 1bb53d4ec..34e483c91 100644 --- a/internal/oidc/errors.go +++ b/internal/oidc/errors.go @@ -3,40 +3,31 @@ package oidc  import (  	"errors" -	"github.com/ory/fosite" +	oauthelia2 "authelia.com/provider/oauth2"  ) -var errPasswordsDoNotMatch = errors.New("The provided client secret did not match the registered client secret.") -  var ( -	// ErrIssuerCouldNotDerive is sent when the issuer couldn't be determined from the headers. -	ErrIssuerCouldNotDerive = fosite.ErrServerError.WithHint("Could not safely derive the issuer.") +	errClientSecretMismatch = errors.New("The provided client secret did not match the registered client secret.") +) +var (  	// ErrSubjectCouldNotLookup is sent when the Subject Identifier for a user couldn't be generated or obtained from the database. -	ErrSubjectCouldNotLookup = fosite.ErrServerError.WithHint("Could not lookup user subject.") +	ErrSubjectCouldNotLookup = oauthelia2.ErrServerError.WithHint("Could not lookup user subject.")  	// ErrConsentCouldNotPerform is sent when the Consent Session couldn't be performed for varying reasons. -	ErrConsentCouldNotPerform = fosite.ErrServerError.WithHint("Could not perform consent.") +	ErrConsentCouldNotPerform = oauthelia2.ErrServerError.WithHint("Could not perform consent.")  	// ErrConsentCouldNotGenerate is sent when the Consent Session failed to be generated for some reason, usually a failed UUIDv4 generation. -	ErrConsentCouldNotGenerate = fosite.ErrServerError.WithHint("Could not generate the consent session.") +	ErrConsentCouldNotGenerate = oauthelia2.ErrServerError.WithHint("Could not generate the consent session.")  	// ErrConsentCouldNotSave is sent when the Consent Session couldn't be saved to the database. -	ErrConsentCouldNotSave = fosite.ErrServerError.WithHint("Could not save the consent session.") +	ErrConsentCouldNotSave = oauthelia2.ErrServerError.WithHint("Could not save the consent session.")  	// ErrConsentCouldNotLookup is sent when the Consent ID is not a known UUID. -	ErrConsentCouldNotLookup = fosite.ErrServerError.WithHint("Failed to lookup the consent session.") +	ErrConsentCouldNotLookup = oauthelia2.ErrServerError.WithHint("Failed to lookup the consent session.")  	// ErrConsentMalformedChallengeID is sent when the Consent ID is not a UUID. -	ErrConsentMalformedChallengeID = fosite.ErrServerError.WithHint("Malformed consent session challenge ID.") - -	// ErrPAREnforcedClientMissingPAR is sent when a client has RequirePushedAuthorizationRequests configured but the Authorization Request was not Pushed. -	ErrPAREnforcedClientMissingPAR = fosite.ErrInvalidRequest.WithHint("Pushed Authorization Requests are enforced for this client but no such request was sent.") - -	ErrClientAuthorizationUserAccessDenied = fosite.ErrAccessDenied.WithHint("The user was denied access to this client.") -) +	ErrConsentMalformedChallengeID = oauthelia2.ErrServerError.WithHint("Malformed consent session challenge ID.") -const ( -	errHintFmtClientAuthMethodMismatch = "The OAuth 2.0 Client supports client authentication method '%s', but method '%s' was requested. You must configure the OAuth 2.0 client's 'token_endpoint_auth_method' value to accept '%s'." -	errDebugFmtParameterMatchClaim     = "The claim '%s' with value '%s' did not match the '%s' with value '%s'." +	ErrClientAuthorizationUserAccessDenied = oauthelia2.ErrAccessDenied.WithHint("The user was denied access to this client.")  ) diff --git a/internal/oidc/flow_client_credentials.go b/internal/oidc/flow_client_credentials.go deleted file mode 100644 index 118f9496e..000000000 --- a/internal/oidc/flow_client_credentials.go +++ /dev/null @@ -1,170 +0,0 @@ -package oidc - -import ( -	"context" -	"net/url" -	"time" - -	"github.com/ory/fosite" -	"github.com/ory/fosite/handler/oauth2" -	"github.com/ory/x/errorsx" -) - -// ClientCredentialsGrantHandler handles access requests for the Client Credentials Flow. -type ClientCredentialsGrantHandler struct { -	*oauth2.HandleHelper -	Config interface { -		fosite.ScopeStrategyProvider -		fosite.AudienceStrategyProvider -		fosite.AccessTokenLifespanProvider -	} -} - -// HandleTokenEndpointRequest implements https://tools.ietf.org/html/rfc6749#section-6 and the -// fosite.TokenEndpointHandler for the Client Credentials Flow. -func (c *ClientCredentialsGrantHandler) HandleTokenEndpointRequest(ctx context.Context, request fosite.AccessRequester) error { -	if !c.CanHandleTokenEndpointRequest(ctx, request) { -		return errorsx.WithStack(fosite.ErrUnknownRequest) -	} - -	client := request.GetClient() - -	// The client MUST authenticate with the authorization server as described in Section 3.2.1. -	// This requirement is already fulfilled because fosite requires all token requests to be authenticated as described -	// in https://tools.ietf.org/html/rfc6749#section-3.2.1 -	if client.IsPublic() { -		return errorsx.WithStack(fosite.ErrInvalidGrant.WithHint("The OAuth 2.0 Client is marked as public and is thus not allowed to use authorization grant 'client_credentials'.")) -	} - -	scopes := request.GetRequestedScopes() - -	if len(scopes) == 0 { -		scopes = client.GetScopes() -	} - -	for _, scope := range scopes { -		if !c.Config.GetScopeStrategy(ctx)(client.GetScopes(), scope) { -			return errorsx.WithStack(fosite.ErrInvalidScope.WithHintf("The OAuth 2.0 Client is not allowed to request scope '%s'.", scope)) -		} - -		request.GrantScope(scope) -	} - -	if err := c.Config.GetAudienceStrategy(ctx)(client.GetAudience(), request.GetRequestedAudience()); err != nil { -		return err -	} - -	// if the client is not public, he has already been authenticated by the access request handler. -	atLifespan := fosite.GetEffectiveLifespan(client, fosite.GrantTypeClientCredentials, fosite.AccessToken, c.Config.GetAccessTokenLifespan(ctx)) -	request.GetSession().SetExpiresAt(fosite.AccessToken, time.Now().UTC().Add(atLifespan)) - -	return nil -} - -// PopulateTokenEndpointResponse implements https://tools.ietf.org/html/rfc6749#section-4.4.3 and the -// fosite.TokenEndpointHandler for the Client Credentials Flow. -func (c *ClientCredentialsGrantHandler) PopulateTokenEndpointResponse(ctx context.Context, request fosite.AccessRequester, response fosite.AccessResponder) error { -	if !c.CanHandleTokenEndpointRequest(ctx, request) { -		return errorsx.WithStack(fosite.ErrUnknownRequest) -	} - -	// TODO: remove? -	if !request.GetClient().GetGrantTypes().Has("client_credentials") { -		return errorsx.WithStack(fosite.ErrUnauthorizedClient.WithHint("The OAuth 2.0 Client is not allowed to use authorization grant 'client_credentials'.")) -	} - -	atLifespan := fosite.GetEffectiveLifespan(request.GetClient(), fosite.GrantTypeClientCredentials, fosite.AccessToken, c.Config.GetAccessTokenLifespan(ctx)) - -	return c.IssueAccessToken(ctx, atLifespan, request, response) -} - -// CanSkipClientAuth implements the fosite.TokenEndpointHandler for the Client Credentials Flow. -func (c *ClientCredentialsGrantHandler) CanSkipClientAuth(ctx context.Context, requester fosite.AccessRequester) bool { -	return false -} - -// CanHandleTokenEndpointRequest implements the fosite.TokenEndpointHandler for the Client Credentials Flow. -func (c *ClientCredentialsGrantHandler) CanHandleTokenEndpointRequest(ctx context.Context, requester fosite.AccessRequester) bool { -	// grant_type REQUIRED. -	// Value MUST be set to "client_credentials". -	return requester.GetGrantTypes().ExactOne(GrantTypeClientCredentials) -} - -var ( -	_ fosite.TokenEndpointHandler = (*ClientCredentialsGrantHandler)(nil) -) - -// PopulateClientCredentialsFlowSessionWithAccessRequest is used to configure a session when performing a client credentials grant. -func PopulateClientCredentialsFlowSessionWithAccessRequest(ctx Context, client fosite.Client, session *Session) (err error) { -	var ( -		issuer *url.URL -	) - -	if issuer, err = ctx.IssuerURL(); err != nil { -		return fosite.ErrServerError.WithWrap(err).WithDebugf("Failed to determine the issuer with error: %s.", err.Error()) -	} - -	if client == nil { -		return fosite.ErrServerError.WithDebug("Failed to get the client for the request.") -	} - -	session.Subject = "" -	session.Claims.Subject = client.GetID() -	session.ClientID = client.GetID() -	session.DefaultSession.Claims.Issuer = issuer.String() -	session.DefaultSession.Claims.IssuedAt = ctx.GetClock().Now().UTC() -	session.DefaultSession.Claims.RequestedAt = ctx.GetClock().Now().UTC() -	session.ClientCredentials = true - -	return nil -} - -// PopulateClientCredentialsFlowRequester is used to grant the authorized scopes and audiences when performing a client -// credentials grant. -func PopulateClientCredentialsFlowRequester(ctx Context, config fosite.Configurator, client fosite.Client, requester fosite.Requester) (err error) { -	if client == nil || config == nil || requester == nil { -		return fosite.ErrServerError.WithDebug("Failed to get the client, configuration, or requester for the request.") -	} - -	scopes := requester.GetRequestedScopes() -	audience := requester.GetRequestedAudience() - -	var authz, nauthz bool - -	strategy := config.GetScopeStrategy(ctx) - -	for _, scope := range scopes { -		switch scope { -		case ScopeOffline, ScopeOfflineAccess: -			break -		case ScopeAutheliaBearerAuthz: -			authz = true -		default: -			nauthz = true -		} - -		if strategy(client.GetScopes(), scope) { -			requester.GrantScope(scope) -		} else { -			return fosite.ErrInvalidScope.WithDebugf("The scope '%s' is not authorized on client with id '%s'.", scope, client.GetID()) -		} -	} - -	if authz && nauthz { -		return fosite.ErrInvalidScope.WithDebugf("The scope '%s' must only be requested by itself or with the '%s' scope, no other scopes are permitted.", ScopeAutheliaBearerAuthz, ScopeOfflineAccess) -	} - -	if authz && len(audience) == 0 { -		return fosite.ErrInvalidRequest.WithDebugf("The scope '%s' requires the request also include an audience.", ScopeAutheliaBearerAuthz) -	} - -	if err = config.GetAudienceStrategy(ctx)(client.GetAudience(), audience); err != nil { -		return err -	} - -	for _, aud := range audience { -		requester.GrantAudience(aud) -	} - -	return nil -} diff --git a/internal/oidc/flow_client_credentials_test.go b/internal/oidc/flow_client_credentials_test.go deleted file mode 100644 index f3ee7bf7d..000000000 --- a/internal/oidc/flow_client_credentials_test.go +++ /dev/null @@ -1,485 +0,0 @@ -package oidc_test - -import ( -	"context" -	"errors" -	"net/http" -	"net/url" -	"testing" -	"time" - -	"github.com/ory/fosite" -	"github.com/ory/fosite/handler/oauth2" -	"github.com/ory/fosite/handler/openid" -	fjwt "github.com/ory/fosite/token/jwt" -	"github.com/stretchr/testify/assert" -	"go.uber.org/mock/gomock" - -	"github.com/authelia/authelia/v4/internal/clock" -	"github.com/authelia/authelia/v4/internal/mocks" -	"github.com/authelia/authelia/v4/internal/oidc" -) - -func TestClientCredentialsGrantHandler_CanSkipClientAuth(t *testing.T) { -	handler := oidc.ClientCredentialsGrantHandler{} - -	assert.False(t, handler.CanSkipClientAuth(context.TODO(), &fosite.AccessRequest{})) -} - -func TestClientCredentialsGrantHandler_HandleTokenEndpointRequest(t *testing.T) { -	ctrl := gomock.NewController(t) -	store := mocks.NewMockClientCredentialsGrantStorage(ctrl) -	chgen := mocks.NewMockAccessTokenStrategy(ctrl) - -	defer ctrl.Finish() - -	handler := oidc.ClientCredentialsGrantHandler{ -		HandleHelper: &oauth2.HandleHelper{ -			AccessTokenStorage:  store, -			AccessTokenStrategy: chgen, -			Config: &fosite.Config{ -				AccessTokenLifespan: time.Hour, -			}, -		}, -		Config: &fosite.Config{ -			ScopeStrategy:            fosite.HierarchicScopeStrategy, -			AudienceMatchingStrategy: fosite.DefaultAudienceMatchingStrategy, -		}, -	} - -	testCases := []struct { -		name     string -		setup    func(mock *mocks.MockAccessRequester) -		err      error -		expected string -	}{ -		{ -			name: "ShouldFailNotResponsible", -			setup: func(mock *mocks.MockAccessRequester) { -				mock.EXPECT().GetGrantTypes().Return(fosite.Arguments{""}) -			}, -			err:      fosite.ErrUnknownRequest, -			expected: "The handler is not responsible for this request.", -		}, -		{ -			name: "ShouldFailInvalidAudience", -			setup: func(mock *mocks.MockAccessRequester) { -				mock.EXPECT().GetGrantTypes().Return(fosite.Arguments{oidc.GrantTypeClientCredentials}) -				mock.EXPECT().GetRequestedScopes().Return([]string{}) -				mock.EXPECT().GetRequestedAudience().Return([]string{"https://www.ory.sh/not-api"}) -				mock.EXPECT().GetClient().Return(&fosite.DefaultClient{ -					GrantTypes: fosite.Arguments{oidc.GrantTypeClientCredentials}, -					Audience:   []string{"https://www.ory.sh/api"}, -				}) -			}, -			err:      fosite.ErrInvalidRequest, -			expected: "The request is missing a required parameter, includes an invalid parameter value, includes a parameter more than once, or is otherwise malformed. Requested audience 'https://www.ory.sh/not-api' has not been whitelisted by the OAuth 2.0 Client.", -		}, -		{ -			name: "ShouldFailInvalidScope", -			setup: func(mock *mocks.MockAccessRequester) { -				mock.EXPECT().GetGrantTypes().Return(fosite.Arguments{oidc.GrantTypeClientCredentials}) -				mock.EXPECT().GetRequestedScopes().Return([]string{"foo", "bar", "baz.bar"}) -				mock.EXPECT().GetClient().Return(&fosite.DefaultClient{ -					GrantTypes: fosite.Arguments{oidc.GrantTypeClientCredentials}, -					Scopes:     []string{"foo"}, -				}) -				mock.EXPECT().GrantScope("foo") -			}, -			err:      fosite.ErrInvalidScope, -			expected: "The requested scope is invalid, unknown, or malformed. The OAuth 2.0 Client is not allowed to request scope 'bar'.", -		}, -		{ -			name: "ShouldPass", -			setup: func(mock *mocks.MockAccessRequester) { -				mock.EXPECT().GetSession().Return(new(fosite.DefaultSession)) -				mock.EXPECT().GetGrantTypes().Return(fosite.Arguments{oidc.GrantTypeClientCredentials}) -				mock.EXPECT().GetRequestedScopes().Return([]string{"foo", "bar", "baz.bar"}) -				mock.EXPECT().GetRequestedAudience().Return([]string{}) -				mock.EXPECT().GetClient().Return(&fosite.DefaultClient{ -					GrantTypes: fosite.Arguments{oidc.GrantTypeClientCredentials}, -					Scopes:     []string{"foo", "bar", "baz"}, -				}) - -				mock.EXPECT().GrantScope("foo") -				mock.EXPECT().GrantScope("bar") -				mock.EXPECT().GrantScope("baz.bar") -			}, -		}, -		{ -			name: "ShouldFailPublicClient", -			setup: func(mock *mocks.MockAccessRequester) { -				mock.EXPECT().GetGrantTypes().Return(fosite.Arguments{oidc.GrantTypeClientCredentials}) -				mock.EXPECT().GetClient().Return(&fosite.DefaultClient{ -					GrantTypes: fosite.Arguments{oidc.GrantTypeClientCredentials}, -					Scopes:     []string{"foo", "bar", "baz"}, -					Public:     true, -				}) -			}, -			err:      fosite.ErrInvalidGrant, -			expected: "The provided authorization grant (e.g., authorization code, resource owner credentials) or refresh token is invalid, expired, revoked, does not match the redirection URI used in the authorization request, or was issued to another client. The OAuth 2.0 Client is marked as public and is thus not allowed to use authorization grant 'client_credentials'.", -		}, -		{ -			name: "ShouldPassBaseClient", -			setup: func(mock *mocks.MockAccessRequester) { -				mock.EXPECT().GetSession().Return(new(fosite.DefaultSession)) -				mock.EXPECT().GetGrantTypes().Return(fosite.Arguments{oidc.GrantTypeClientCredentials}) -				mock.EXPECT().GetRequestedScopes().Return([]string{"foo", "bar", "baz.bar"}) -				mock.EXPECT().GetRequestedAudience().Return([]string{}) -				mock.EXPECT().GetClient().Return(&oidc.RegisteredClient{ -					GrantTypes: fosite.Arguments{oidc.GrantTypeClientCredentials}, -					Scopes:     []string{"foo", "bar", "baz"}, -				}) - -				mock.EXPECT().GrantScope("foo") -				mock.EXPECT().GrantScope("bar") -				mock.EXPECT().GrantScope("baz.bar") -			}, -		}, -		{ -			name: "ShouldPassBaseNoScopes", -			setup: func(mock *mocks.MockAccessRequester) { -				mock.EXPECT().GetSession().Return(new(fosite.DefaultSession)) -				mock.EXPECT().GetGrantTypes().Return(fosite.Arguments{oidc.GrantTypeClientCredentials}) -				mock.EXPECT().GetRequestedScopes().Return([]string{}) -				mock.EXPECT().GetRequestedAudience().Return([]string{}) -				mock.EXPECT().GetClient().Return(&oidc.RegisteredClient{ -					GrantTypes: fosite.Arguments{oidc.GrantTypeClientCredentials}, -					Scopes:     []string{"foo", "bar", "baz"}, -				}) - -				mock.EXPECT().GrantScope("foo") -				mock.EXPECT().GrantScope("bar") -				mock.EXPECT().GrantScope("baz") -			}, -		}, -		{ -			name: "ShouldPassBaseNoScopesWithAuto", -			setup: func(mock *mocks.MockAccessRequester) { -				mock.EXPECT().GetSession().Return(new(fosite.DefaultSession)) -				mock.EXPECT().GetGrantTypes().Return(fosite.Arguments{oidc.GrantTypeClientCredentials}) -				mock.EXPECT().GetRequestedScopes().Return([]string{}) -				mock.EXPECT().GetRequestedAudience().Return([]string{}) -				mock.EXPECT().GetClient().Return(&oidc.RegisteredClient{ -					GrantTypes: fosite.Arguments{oidc.GrantTypeClientCredentials}, -					Scopes:     []string{"foo", "bar", "baz"}, -				}) - -				mock.EXPECT().GrantScope("foo") -				mock.EXPECT().GrantScope("bar") -				mock.EXPECT().GrantScope("baz") -			}, -		}, -	} - -	for _, tc := range testCases { -		t.Run(tc.name, func(t *testing.T) { -			mock := mocks.NewMockAccessRequester(ctrl) - -			tc.setup(mock) - -			err := handler.HandleTokenEndpointRequest(context.Background(), mock) - -			if tc.err != nil { -				assert.EqualError(t, err, tc.err.Error()) -				assert.EqualError(t, oidc.ErrorToDebugRFC6749Error(err), tc.expected) -			} else { -				assert.NoError(t, oidc.ErrorToDebugRFC6749Error(err)) -			} -		}) -	} -} - -func TestClientCredentialsGrantHandler_PopulateTokenEndpointResponse(t *testing.T) { -	testCases := []struct { -		name     string -		setup    func(request *fosite.AccessRequest, store *mocks.MockClientCredentialsGrantStorage, strategy *mocks.MockAccessTokenStrategy) -		req      *http.Request -		err      error -		expected string -	}{ -		{ -			name: "ShouldFailNotResponsible", -			setup: func(request *fosite.AccessRequest, store *mocks.MockClientCredentialsGrantStorage, strategy *mocks.MockAccessTokenStrategy) { -				request.GrantTypes = fosite.Arguments{""} -			}, -			err:      fosite.ErrUnknownRequest, -			expected: "The handler is not responsible for this request.", -		}, -		{ -			name: "ShouldFailGrantTypeNotAllowed", -			setup: func(request *fosite.AccessRequest, store *mocks.MockClientCredentialsGrantStorage, strategy *mocks.MockAccessTokenStrategy) { -				request.GrantTypes = fosite.Arguments{oidc.GrantTypeClientCredentials} -				request.Client = &fosite.DefaultClient{GrantTypes: fosite.Arguments{oidc.GrantTypeAuthorizationCode}} -			}, -			err:      fosite.ErrUnauthorizedClient, -			expected: "The client is not authorized to request a token using this method. The OAuth 2.0 Client is not allowed to use authorization grant 'client_credentials'.", -		}, -		{ -			name: "ShouldPass", -			setup: func(request *fosite.AccessRequest, store *mocks.MockClientCredentialsGrantStorage, strategy *mocks.MockAccessTokenStrategy) { -				request.GrantTypes = fosite.Arguments{oidc.GrantTypeClientCredentials} -				request.Session = &fosite.DefaultSession{} -				request.Client = &fosite.DefaultClient{GrantTypes: fosite.Arguments{oidc.GrantTypeClientCredentials}} -				strategy.EXPECT().GenerateAccessToken(gomock.Any(), request).Return("tokenfoo.bar", "bar", nil) -				store.EXPECT().CreateAccessTokenSession(gomock.Any(), "bar", gomock.Eq(request.Sanitize([]string{}))).Return(nil) -			}, -		}, -	} - -	for _, tc := range testCases { -		t.Run(tc.name, func(t *testing.T) { -			ctrl := gomock.NewController(t) -			store := mocks.NewMockClientCredentialsGrantStorage(ctrl) -			strategy := mocks.NewMockAccessTokenStrategy(ctrl) -			request := fosite.NewAccessRequest(new(fosite.DefaultSession)) -			response := fosite.NewAccessResponse() - -			defer ctrl.Finish() - -			handler := oidc.ClientCredentialsGrantHandler{ -				HandleHelper: &oauth2.HandleHelper{ -					AccessTokenStorage:  store, -					AccessTokenStrategy: strategy, -					Config: &fosite.Config{ -						AccessTokenLifespan: time.Hour, -					}, -				}, -				Config: &fosite.Config{ -					ScopeStrategy: fosite.HierarchicScopeStrategy, -				}, -			} - -			tc.setup(request, store, strategy) - -			err := handler.PopulateTokenEndpointResponse(context.Background(), request, response) - -			if tc.err != nil { -				assert.EqualError(t, err, tc.err.Error()) -				assert.EqualError(t, oidc.ErrorToDebugRFC6749Error(err), tc.expected) -			} else { -				assert.NoError(t, oidc.ErrorToDebugRFC6749Error(err)) -			} -		}) -	} -} - -func TestPopulateClientCredentialsFlowSessionWithAccessRequest(t *testing.T) { -	testCases := []struct { -		name     string -		setup    func(ctx oidc.Context) -		ctx      oidc.Context -		client   fosite.Client -		have     *oidc.Session -		expected *oidc.Session -		err      string -	}{ -		{ -			"ShouldHandleIssuerError", -			nil, -			&TestContext{ -				IssuerURLFunc: func() (issuerURL *url.URL, err error) { -					return nil, errors.New("an error") -				}, -			}, -			nil, -			oidc.NewSession(), -			nil, -			"The authorization server encountered an unexpected condition that prevented it from fulfilling the request. Failed to determine the issuer with error: an error.", -		}, -		{ -			"ShouldHandleClientError", -			nil, -			&TestContext{ -				IssuerURLFunc: func() (issuerURL *url.URL, err error) { -					return &url.URL{Scheme: "https", Host: "example.com"}, nil -				}, -			}, -			nil, -			oidc.NewSession(), -			nil, -			"The authorization server encountered an unexpected condition that prevented it from fulfilling the request. Failed to get the client for the request.", -		}, -		{ -			"ShouldUpdateValues", -			func(ctx oidc.Context) { -				c := ctx.(*TestContext) - -				c.Clock = clock.NewFixed(time.Unix(10000000000, 0)) -			}, -			&TestContext{ -				IssuerURLFunc: func() (issuerURL *url.URL, err error) { -					return &url.URL{Scheme: "https", Host: "example.com"}, nil -				}, -			}, -			&oidc.RegisteredClient{ -				ID: abc, -			}, -			oidc.NewSession(), -			&oidc.Session{ -				Extra: map[string]any{}, -				DefaultSession: &openid.DefaultSession{ -					Headers: &fjwt.Headers{ -						Extra: map[string]any{}, -					}, -					Claims: &fjwt.IDTokenClaims{ -						Issuer:      "https://example.com", -						IssuedAt:    time.Unix(10000000000, 0).UTC(), -						RequestedAt: time.Unix(10000000000, 0).UTC(), -						Subject:     abc, -						Extra:       map[string]any{}, -					}, -				}, -				ClientID:          abc, -				ClientCredentials: true, -			}, -			"", -		}, -	} - -	for _, tc := range testCases { -		t.Run(tc.name, func(t *testing.T) { -			if tc.setup != nil { -				tc.setup(tc.ctx) -			} - -			err := oidc.PopulateClientCredentialsFlowSessionWithAccessRequest(tc.ctx, tc.client, tc.have) - -			assert.Equal(t, "", tc.have.GetSubject()) - -			if len(tc.err) == 0 { -				assert.NoError(t, err) -				assert.EqualValues(t, tc.expected, tc.have) -			} else { -				assert.EqualError(t, oidc.ErrorToDebugRFC6749Error(err), tc.err) -			} -		}) -	} -} - -func TestPopulateClientCredentialsFlowRequester(t *testing.T) { -	testCases := []struct { -		name     string -		setup    func(ctx oidc.Context) -		ctx      oidc.Context -		config   fosite.Configurator -		client   fosite.Client -		have     *fosite.Request -		expected *fosite.Request -		err      string -	}{ -		{ -			"ShouldHandleBasic", -			nil, -			&TestContext{}, -			&oidc.Config{}, -			&oidc.RegisteredClient{}, -			&fosite.Request{}, -			&fosite.Request{}, -			"", -		}, -		{ -			"ShouldHandleNilErrorClient", -			nil, -			&TestContext{}, -			&oidc.Config{}, -			nil, -			&fosite.Request{}, -			&fosite.Request{}, -			"The authorization server encountered an unexpected condition that prevented it from fulfilling the request. Failed to get the client, configuration, or requester for the request.", -		}, -		{ -			"ShouldHandleBadScopeCombinationAuthz", -			nil, -			&TestContext{}, -			&oidc.Config{}, -			&oidc.RegisteredClient{ID: "abc", Scopes: []string{oidc.ScopeAutheliaBearerAuthz, oidc.ScopeOpenID}}, -			&fosite.Request{RequestedScope: fosite.Arguments{oidc.ScopeAutheliaBearerAuthz, oidc.ScopeOpenID}}, -			&fosite.Request{}, -			"The requested scope is invalid, unknown, or malformed. The scope 'authelia.bearer.authz' must only be requested by itself or with the 'offline_access' scope, no other scopes are permitted.", -		}, -		{ -			"ShouldHandleScopeNotPermitted", -			nil, -			&TestContext{}, -			&oidc.Config{}, -			&oidc.RegisteredClient{ID: "abc", Scopes: []string{oidc.ScopeAutheliaBearerAuthz}}, -			&fosite.Request{RequestedScope: fosite.Arguments{oidc.ScopeAutheliaBearerAuthz, oidc.ScopeOfflineAccess}}, -			&fosite.Request{}, -			"The requested scope is invalid, unknown, or malformed. The scope 'offline_access' is not authorized on client with id 'abc'.", -		}, -		{ -			"ShouldHandleGoodScopesWithoutAudience", -			nil, -			&TestContext{}, -			&oidc.Config{}, -			&oidc.RegisteredClient{ID: "abc", Scopes: []string{oidc.ScopeAutheliaBearerAuthz, oidc.ScopeOfflineAccess}}, -			&fosite.Request{RequestedScope: fosite.Arguments{oidc.ScopeAutheliaBearerAuthz, oidc.ScopeOfflineAccess}}, -			&fosite.Request{}, -			"The request is missing a required parameter, includes an invalid parameter value, includes a parameter more than once, or is otherwise malformed. Make sure that the various parameters are correct, be aware of case sensitivity and trim your parameters. Make sure that the client you are using has exactly whitelisted the redirect_uri you specified. The scope 'authelia.bearer.authz' requires the request also include an audience.", -		}, -		{ -			"ShouldHandleGoodScopesAndBadAudience", -			nil, -			&TestContext{}, -			&oidc.Config{}, -			&oidc.RegisteredClient{ID: "abc", Scopes: []string{oidc.ScopeAutheliaBearerAuthz, oidc.ScopeOfflineAccess}}, -			&fosite.Request{RequestedScope: fosite.Arguments{oidc.ScopeAutheliaBearerAuthz, oidc.ScopeOfflineAccess}, RequestedAudience: fosite.Arguments{"https://example.com"}}, -			&fosite.Request{}, -			"The request is missing a required parameter, includes an invalid parameter value, includes a parameter more than once, or is otherwise malformed. Requested audience 'https://example.com' has not been whitelisted by the OAuth 2.0 Client.", -		}, -		{ -			"ShouldHandleGoodScopesAndAudience", -			nil, -			&TestContext{}, -			&oidc.Config{}, -			&oidc.RegisteredClient{ID: "abc", Scopes: []string{oidc.ScopeAutheliaBearerAuthz, oidc.ScopeOfflineAccess}, Audience: fosite.Arguments{"https://example.com"}}, -			&fosite.Request{ -				RequestedScope:    fosite.Arguments{oidc.ScopeAutheliaBearerAuthz, oidc.ScopeOfflineAccess}, -				RequestedAudience: fosite.Arguments{"https://example.com"}, -			}, -			&fosite.Request{ -				RequestedScope:    fosite.Arguments{oidc.ScopeAutheliaBearerAuthz, oidc.ScopeOfflineAccess}, -				GrantedScope:      fosite.Arguments{oidc.ScopeAutheliaBearerAuthz, oidc.ScopeOfflineAccess}, -				RequestedAudience: fosite.Arguments{"https://example.com"}, -				GrantedAudience:   fosite.Arguments{"https://example.com"}, -			}, -			"", -		}, -		{ -			"ShouldHandleGoodScopesAndAudienceSubSet", -			nil, -			&TestContext{}, -			&oidc.Config{}, -			&oidc.RegisteredClient{ID: "abc", Scopes: []string{oidc.ScopeAutheliaBearerAuthz, oidc.ScopeOfflineAccess}, Audience: fosite.Arguments{"https://example.com", "https://app.example.com"}}, -			&fosite.Request{ -				RequestedScope:    fosite.Arguments{oidc.ScopeAutheliaBearerAuthz, oidc.ScopeOfflineAccess}, -				RequestedAudience: fosite.Arguments{"https://example.com"}, -			}, -			&fosite.Request{ -				RequestedScope:    fosite.Arguments{oidc.ScopeAutheliaBearerAuthz, oidc.ScopeOfflineAccess}, -				GrantedScope:      fosite.Arguments{oidc.ScopeAutheliaBearerAuthz, oidc.ScopeOfflineAccess}, -				RequestedAudience: fosite.Arguments{"https://example.com"}, -				GrantedAudience:   fosite.Arguments{"https://example.com"}, -			}, -			"", -		}, -	} - -	for _, tc := range testCases { -		t.Run(tc.name, func(t *testing.T) { -			if tc.setup != nil { -				tc.setup(tc.ctx) -			} - -			err := oidc.PopulateClientCredentialsFlowRequester(tc.ctx, tc.config, tc.client, tc.have) - -			if len(tc.err) == 0 { -				assert.NoError(t, err) -				assert.EqualValues(t, tc.expected, tc.have) -			} else { -				assert.EqualError(t, oidc.ErrorToDebugRFC6749Error(err), tc.err) -			} -		}) -	} -} diff --git a/internal/oidc/flow_refresh.go b/internal/oidc/flow_refresh.go deleted file mode 100644 index 6d4769b82..000000000 --- a/internal/oidc/flow_refresh.go +++ /dev/null @@ -1,322 +0,0 @@ -package oidc - -import ( -	"context" -	"fmt" -	"strings" -	"time" - -	"github.com/ory/fosite" -	"github.com/ory/fosite/handler/oauth2" -	"github.com/ory/fosite/storage" -	"github.com/ory/x/errorsx" -	"github.com/pkg/errors" -) - -// RefreshTokenGrantHandler handles access requests for the Refresh Token Flow. -type RefreshTokenGrantHandler struct { -	AccessTokenStrategy    oauth2.AccessTokenStrategy -	RefreshTokenStrategy   oauth2.RefreshTokenStrategy -	TokenRevocationStorage oauth2.TokenRevocationStorage -	Config                 interface { -		fosite.AccessTokenLifespanProvider -		fosite.RefreshTokenLifespanProvider -		fosite.ScopeStrategyProvider -		fosite.AudienceStrategyProvider -		fosite.RefreshTokenScopesProvider -	} -} - -// HandleTokenEndpointRequest implements https://tools.ietf.org/html/rfc6749#section-6 -// -//nolint:gocyclo -func (c *RefreshTokenGrantHandler) HandleTokenEndpointRequest(ctx context.Context, request fosite.AccessRequester) error { -	if !c.CanHandleTokenEndpointRequest(ctx, request) { -		return errorsx.WithStack(fosite.ErrUnknownRequest) -	} - -	if !request.GetClient().GetGrantTypes().Has(GrantTypeRefreshToken) { -		return errorsx.WithStack(fosite.ErrUnauthorizedClient.WithHint("The OAuth 2.0 Client is not allowed to use authorization grant 'refresh_token'.")) -	} - -	refresh := request.GetRequestForm().Get(FormParameterRefreshToken) -	signature := c.RefreshTokenStrategy.RefreshTokenSignature(ctx, refresh) -	originalRequest, err := c.TokenRevocationStorage.GetRefreshTokenSession(ctx, signature, request.GetSession()) - -	switch { -	case err == nil: -		if err = c.RefreshTokenStrategy.ValidateRefreshToken(ctx, originalRequest, refresh); err != nil { -			// The authorization server MUST ... validate the refresh token. -			// This needs to happen after store retrieval for the session to be hydrated properly. -			if errors.Is(err, fosite.ErrTokenExpired) { -				return errorsx.WithStack(fosite.ErrInvalidGrant.WithWrap(err).WithDebug(ErrorToDebugRFC6749Error(err).Error())) -			} - -			return errorsx.WithStack(fosite.ErrInvalidRequest.WithWrap(err).WithDebug(ErrorToDebugRFC6749Error(err).Error())) -		} -	case errors.Is(err, fosite.ErrInactiveToken): -		// Detected refresh token reuse. -		if e := c.handleRefreshTokenReuse(ctx, signature, originalRequest); e != nil { -			return errorsx.WithStack(e) -		} - -		return errorsx.WithStack(fosite.ErrInactiveToken.WithWrap(err).WithDebug(ErrorToDebugRFC6749Error(err).Error())) -	case errors.Is(err, fosite.ErrNotFound): -		return errorsx.WithStack(fosite.ErrInvalidGrant.WithWrap(err).WithDebugf("The refresh token has not been found: %s", ErrorToDebugRFC6749Error(err).Error())) -	default: -		return errorsx.WithStack(fosite.ErrServerError.WithWrap(err).WithDebug(ErrorToDebugRFC6749Error(err).Error())) -	} - -	if !(len(c.Config.GetRefreshTokenScopes(ctx)) == 0 || originalRequest.GetGrantedScopes().HasOneOf(c.Config.GetRefreshTokenScopes(ctx)...)) { -		scopeNames := strings.Join(c.Config.GetRefreshTokenScopes(ctx), " or ") -		hint := fmt.Sprintf("The OAuth 2.0 Client was not granted scope %s and may thus not perform the 'refresh_token' authorization grant.", scopeNames) - -		return errorsx.WithStack(fosite.ErrScopeNotGranted.WithHint(hint)) -	} - -	// The authorization server MUST ... and ensure that the refresh token was issued to the authenticated client. -	if originalRequest.GetClient().GetID() != request.GetClient().GetID() { -		return errorsx.WithStack(fosite.ErrInvalidGrant.WithHint("The OAuth 2.0 Client ID from this request does not match the ID during the initial token issuance.")) -	} - -	request.SetID(originalRequest.GetID()) -	request.SetSession(originalRequest.GetSession().Clone()) - -	/* -			There are two key points in the following spec section this addresses: -				1. If omitted the scope param should be treated as the same as the scope originally granted by the resource owner. -				2. The REQUESTED scope MUST NOT include any scope not originally granted. - -			scope -					OPTIONAL.  The scope of the access request as described by Section 3.3.  The requested scope MUST NOT -		  			include any scope not originally granted by the resource owner, and if omitted is treated as equal to -		   			the scope originally granted by the resource owner. - -			See https://datatracker.ietf.org/doc/html/rfc6749#section-6 -	*/ - -	// Addresses point 1 of the text in RFC6749 Section 6. -	if len(request.GetRequestedScopes()) == 0 { -		request.SetRequestedScopes(originalRequest.GetGrantedScopes()) -	} - -	request.SetRequestedAudience(originalRequest.GetRequestedAudience()) - -	strategy := c.Config.GetScopeStrategy(ctx) -	originalScopes := originalRequest.GetGrantedScopes() - -	for _, scope := range request.GetRequestedScopes() { -		if !originalScopes.Has(scope) { -			if client, ok := request.GetClient().(RefreshFlowScopeClient); ok && client.GetRefreshFlowIgnoreOriginalGrantedScopes(ctx) { -				// Skips addressing point 2 of the text in RFC6749 Section 6 and instead just prevents the scope -				// requested from being granted. -				continue -			} - -			// Addresses point 2 of the text in RFC6749 Section 6. -			return errorsx.WithStack(fosite.ErrInvalidScope.WithHintf("The requested scope '%s' was not originally granted by the resource owner.", scope)) -		} - -		if !strategy(request.GetClient().GetScopes(), scope) { -			return errorsx.WithStack(fosite.ErrInvalidScope.WithHintf("The OAuth 2.0 Client is not allowed to request scope '%s'.", scope)) -		} - -		request.GrantScope(scope) -	} - -	if err = c.Config.GetAudienceStrategy(ctx)(request.GetClient().GetAudience(), originalRequest.GetGrantedAudience()); err != nil { -		return err -	} - -	for _, audience := range originalRequest.GetGrantedAudience() { -		request.GrantAudience(audience) -	} - -	atLifespan := fosite.GetEffectiveLifespan(request.GetClient(), fosite.GrantTypeRefreshToken, fosite.AccessToken, c.Config.GetAccessTokenLifespan(ctx)) -	request.GetSession().SetExpiresAt(fosite.AccessToken, time.Now().UTC().Add(atLifespan).Round(time.Second)) - -	rtLifespan := fosite.GetEffectiveLifespan(request.GetClient(), fosite.GrantTypeRefreshToken, fosite.RefreshToken, c.Config.GetRefreshTokenLifespan(ctx)) -	if rtLifespan > -1 { -		request.GetSession().SetExpiresAt(fosite.RefreshToken, time.Now().UTC().Add(rtLifespan).Round(time.Second)) -	} - -	return nil -} - -// PopulateTokenEndpointResponse implements https://tools.ietf.org/html/rfc6749#section-6 -func (c *RefreshTokenGrantHandler) PopulateTokenEndpointResponse(ctx context.Context, requester fosite.AccessRequester, responder fosite.AccessResponder) (err error) { -	if !c.CanHandleTokenEndpointRequest(ctx, requester) { -		return errorsx.WithStack(fosite.ErrUnknownRequest) -	} - -	var ( -		accessToken, refreshToken         string -		accessSignature, refreshSignature string -	) - -	if accessToken, accessSignature, err = c.AccessTokenStrategy.GenerateAccessToken(ctx, requester); err != nil { -		return errorsx.WithStack(fosite.ErrServerError.WithWrap(err).WithDebug(ErrorToDebugRFC6749Error(err).Error())) -	} - -	if refreshToken, refreshSignature, err = c.RefreshTokenStrategy.GenerateRefreshToken(ctx, requester); err != nil { -		return errorsx.WithStack(fosite.ErrServerError.WithWrap(err).WithDebug(ErrorToDebugRFC6749Error(err).Error())) -	} - -	signature := c.RefreshTokenStrategy.RefreshTokenSignature(ctx, requester.GetRequestForm().Get(GrantTypeRefreshToken)) - -	if ctx, err = storage.MaybeBeginTx(ctx, c.TokenRevocationStorage); err != nil { -		return errorsx.WithStack(fosite.ErrServerError.WithWrap(err).WithDebug(ErrorToDebugRFC6749Error(err).Error())) -	} - -	defer func() { -		err = c.handleRefreshTokenEndpointStorageError(ctx, err) -	}() - -	var original fosite.Requester - -	if original, err = c.TokenRevocationStorage.GetRefreshTokenSession(ctx, signature, nil); err != nil { -		return err -	} - -	if err = c.TokenRevocationStorage.RevokeAccessToken(ctx, original.GetID()); err != nil { -		return err -	} - -	if err = c.TokenRevocationStorage.RevokeRefreshTokenMaybeGracePeriod(ctx, original.GetID(), signature); err != nil { -		return err -	} - -	if err = c.TokenRevocationStorage.CreateAccessTokenSession(ctx, accessSignature, RefreshFlowSanitizeRestoreOriginalRequestBasic(requester, original)); err != nil { -		return err -	} - -	if err = c.TokenRevocationStorage.CreateRefreshTokenSession(ctx, refreshSignature, RefreshFlowSanitizeRestoreOriginalRequest(requester, original)); err != nil { -		return err -	} - -	responder.SetAccessToken(accessToken) -	responder.SetTokenType(fosite.BearerAccessToken) -	responder.SetExpiresIn(getExpiresIn(requester, fosite.AccessToken, fosite.GetEffectiveLifespan(requester.GetClient(), fosite.GrantTypeRefreshToken, fosite.AccessToken, c.Config.GetAccessTokenLifespan(ctx)), time.Now().UTC())) -	responder.SetScopes(requester.GetGrantedScopes()) -	responder.SetExtra(GrantTypeRefreshToken, refreshToken) - -	if err = storage.MaybeCommitTx(ctx, c.TokenRevocationStorage); err != nil { -		return err -	} - -	return nil -} - -// Reference: https://tools.ietf.org/html/rfc6819#section-5.2.2.3 -// -//	The basic idea is to change the refresh token -//	value with every refresh request in order to detect attempts to -//	obtain access tokens using old refresh tokens.  Since the -//	authorization server cannot determine whether the attacker or the -//	legitimate client is trying to access, in case of such an access -//	attempt the valid refresh token and the access authorization -//	associated with it are both revoked. -func (c *RefreshTokenGrantHandler) handleRefreshTokenReuse(ctx context.Context, signature string, req fosite.Requester) (err error) { -	if ctx, err = storage.MaybeBeginTx(ctx, c.TokenRevocationStorage); err != nil { -		return errorsx.WithStack(fosite.ErrServerError.WithWrap(err).WithDebug(ErrorToDebugRFC6749Error(err).Error())) -	} - -	defer func() { -		err = c.handleRefreshTokenEndpointStorageError(ctx, err) -	}() - -	if err = c.TokenRevocationStorage.DeleteRefreshTokenSession(ctx, signature); err != nil { -		return err -	} - -	if err = c.TokenRevocationStorage.RevokeRefreshToken(ctx, req.GetID()); err != nil && !errors.Is(err, fosite.ErrNotFound) { -		return err -	} - -	if err = c.TokenRevocationStorage.RevokeAccessToken(ctx, req.GetID()); err != nil && !errors.Is(err, fosite.ErrNotFound) { -		return err -	} - -	if err = storage.MaybeCommitTx(ctx, c.TokenRevocationStorage); err != nil { -		return err -	} - -	return nil -} - -func (c *RefreshTokenGrantHandler) handleRefreshTokenEndpointStorageError(ctx context.Context, storageErr error) (err error) { -	if storageErr == nil { -		return nil -	} - -	defer func() { -		if rollBackTxnErr := storage.MaybeRollbackTx(ctx, c.TokenRevocationStorage); rollBackTxnErr != nil { -			err = errorsx.WithStack(fosite.ErrServerError.WithWrap(err).WithDebugf("error: %s; rollback error: %s", err, rollBackTxnErr)) -		} -	}() - -	if errors.Is(storageErr, fosite.ErrSerializationFailure) { -		return errorsx.WithStack(fosite.ErrInvalidRequest. -			WithDebug(ErrorToDebugRFC6749Error(storageErr).Error()). -			WithHint("Failed to refresh token because of multiple concurrent requests using the same token which is not allowed.")) -	} - -	if errors.Is(storageErr, fosite.ErrNotFound) || errors.Is(storageErr, fosite.ErrInactiveToken) { -		return errorsx.WithStack(fosite.ErrInvalidRequest. -			WithDebug(ErrorToDebugRFC6749Error(storageErr).Error()). -			WithHint("Failed to refresh token because of multiple concurrent requests using the same token which is not allowed.")) -	} - -	return errorsx.WithStack(fosite.ErrServerError.WithWrap(storageErr).WithDebug(ErrorToDebugRFC6749Error(storageErr).Error())) -} - -func (c *RefreshTokenGrantHandler) CanSkipClientAuth(ctx context.Context, requester fosite.AccessRequester) bool { -	return false -} - -func (c *RefreshTokenGrantHandler) CanHandleTokenEndpointRequest(ctx context.Context, requester fosite.AccessRequester) bool { -	// grant_type REQUIRED. -	// Value MUST be set to "refresh_token". -	return requester.GetGrantTypes().ExactOne(GrantTypeRefreshToken) -} - -// RefreshFlowSanitizeRestoreOriginalRequest sanitizes input requester with the ID of original, and if the underlying type -// of requester is a *fosite.AccessRequest it also restores the originally granted scopes. This ensures the granted -// scopes for the refresh token session never change and can be referenced when determining if a session can grant -// the respective scopes. -func RefreshFlowSanitizeRestoreOriginalRequest(requester, original fosite.Requester) fosite.Requester { -	var ( -		ar *fosite.AccessRequest -		ok bool -	) - -	if ar, ok = requester.(*fosite.AccessRequest); !ok { -		return RefreshFlowSanitizeRestoreOriginalRequestBasic(requester, original) -	} - -	var sr *fosite.Request - -	if sr, ok = ar.Sanitize(nil).(*fosite.Request); !ok { -		return RefreshFlowSanitizeRestoreOriginalRequestBasic(requester, original) -	} - -	sr.SetID(original.GetID()) - -	sr.SetRequestedScopes(original.GetRequestedScopes()) -	sr.GrantedScope = original.GetGrantedScopes() - -	return sr -} - -// RefreshFlowSanitizeRestoreOriginalRequestBasic is the fallback sanitizer if the RefreshFlowSanitizeRestoreOriginalRequest -// function fails to assert the requester as *fosite.AccessRequest. -func RefreshFlowSanitizeRestoreOriginalRequestBasic(r, o fosite.Requester) fosite.Requester { -	sr := r.Sanitize(nil) -	sr.SetID(o.GetID()) - -	return sr -} - -var ( -	_ fosite.TokenEndpointHandler = (*RefreshTokenGrantHandler)(nil) -) diff --git a/internal/oidc/flow_refresh_test.go b/internal/oidc/flow_refresh_test.go deleted file mode 100644 index 523c6884c..000000000 --- a/internal/oidc/flow_refresh_test.go +++ /dev/null @@ -1,1546 +0,0 @@ -package oidc_test - -import ( -	"context" -	"errors" -	"net/url" -	"regexp" -	"testing" -	"time" - -	"github.com/ory/fosite" -	"github.com/ory/fosite/handler/oauth2" -	"github.com/ory/fosite/storage" -	"github.com/ory/fosite/token/hmac" -	"github.com/stretchr/testify/assert" -	"github.com/stretchr/testify/require" -	"go.uber.org/mock/gomock" - -	"github.com/authelia/authelia/v4/internal/mocks" -	"github.com/authelia/authelia/v4/internal/oidc" -) - -func TestRefreshTokenGrantHandler_CanSkipClientAuth(t *testing.T) { -	factory := func() (oauth2.RefreshTokenStrategy, *storage.MemoryStore, *fosite.AccessRequest) { -		return &oauth2.HMACSHAStrategy{ -			Enigma: &hmac.HMACStrategy{Config: &fosite.Config{GlobalSecret: []byte("foobarfoobarfoobarfoobarfoobarfoobarfoobarfoobar")}}, -			Config: &fosite.Config{ -				AccessTokenLifespan:   time.Hour * 24, -				AuthorizeCodeLifespan: time.Hour * 24, -			}, -		}, storage.NewMemoryStore(), fosite.NewAccessRequest(&fosite.DefaultSession{}) -	} - -	strategy, store, requester := factory() - -	config := &fosite.Config{ -		AccessTokenLifespan:      time.Hour, -		RefreshTokenLifespan:     time.Hour, -		ScopeStrategy:            fosite.HierarchicScopeStrategy, -		AudienceMatchingStrategy: fosite.DefaultAudienceMatchingStrategy, -		RefreshTokenScopes:       []string{oidc.ScopeOffline}, -	} - -	handler := &oidc.RefreshTokenGrantHandler{ -		TokenRevocationStorage: store, -		RefreshTokenStrategy:   strategy, -		Config:                 config, -	} - -	assert.False(t, handler.CanSkipClientAuth(context.TODO(), requester)) -} - -func TestRefreshTokenGrantHandler_HandleTokenEndpointRequest(t *testing.T) { -	sess := &fosite.DefaultSession{Subject: "othersub"} - -	expiredSess := &fosite.DefaultSession{ -		ExpiresAt: map[fosite.TokenType]time.Time{ -			fosite.RefreshToken: time.Now().UTC().Add(-time.Hour), -		}, -	} - -	type testCase struct { -		name      string -		setup     func(config *fosite.Config, strategy oauth2.RefreshTokenStrategy, store *storage.MemoryStore, requester *fosite.AccessRequest) -		err       error -		expected  string -		rexpected *regexp.Regexp -		texpected func(t *testing.T, requester *fosite.AccessRequest) -	} - -	testCases := []testCase{ -		{ -			name: "ShouldFailNotResponsible", -			setup: func(config *fosite.Config, strategy oauth2.RefreshTokenStrategy, store *storage.MemoryStore, requester *fosite.AccessRequest) { -				requester.GrantTypes = fosite.Arguments{"123"} -			}, -			err:      fosite.ErrUnknownRequest, -			expected: "The handler is not responsible for this request.", -		}, -		{ -			name: "ShouldFailInvalidGrant", -			setup: func(config *fosite.Config, strategy oauth2.RefreshTokenStrategy, store *storage.MemoryStore, requester *fosite.AccessRequest) { -				requester.GrantTypes = fosite.Arguments{oidc.GrantTypeRefreshToken} -				requester.Client = &fosite.DefaultClient{GrantTypes: fosite.Arguments{oidc.GrantTypeRefreshToken}} - -				requester.Form.Add(oidc.FormParameterRefreshToken, "some.refreshtokensig") -			}, -			err:      fosite.ErrInvalidGrant, -			expected: "The provided authorization grant (e.g., authorization code, resource owner credentials) or refresh token is invalid, expired, revoked, does not match the redirection URI used in the authorization request, or was issued to another client. The refresh token has not been found: Could not find the requested resource(s).", -		}, -		{ -			name: "ShouldFailTokenValidButDoesNotExist", -			setup: func(config *fosite.Config, strategy oauth2.RefreshTokenStrategy, store *storage.MemoryStore, requester *fosite.AccessRequest) { -				requester.GrantTypes = fosite.Arguments{oidc.GrantTypeRefreshToken} -				requester.Client = &fosite.DefaultClient{GrantTypes: fosite.Arguments{oidc.GrantTypeRefreshToken}} - -				token, _, err := strategy.GenerateRefreshToken(context.TODO(), nil) -				require.NoError(t, err) -				requester.Form.Add(oidc.FormParameterRefreshToken, token) -			}, -			err:      fosite.ErrInvalidGrant, -			expected: "The provided authorization grant (e.g., authorization code, resource owner credentials) or refresh token is invalid, expired, revoked, does not match the redirection URI used in the authorization request, or was issued to another client. The refresh token has not been found: Could not find the requested resource(s).", -		}, -		{ -			name: "ShouldFailBecauseClientDoesNotMatch", -			setup: func(config *fosite.Config, strategy oauth2.RefreshTokenStrategy, store *storage.MemoryStore, requester *fosite.AccessRequest) { -				requester.GrantTypes = fosite.Arguments{oidc.GrantTypeRefreshToken} -				requester.Client = &fosite.DefaultClient{ -					ID:         "foo", -					GrantTypes: fosite.Arguments{oidc.GrantTypeRefreshToken}, -				} - -				token, sig, err := strategy.GenerateRefreshToken(context.TODO(), nil) -				require.NoError(t, err) - -				requester.Form.Add(oidc.FormParameterRefreshToken, token) -				err = store.CreateRefreshTokenSession(context.TODO(), sig, &fosite.Request{ -					Client:       &fosite.DefaultClient{ID: ""}, -					GrantedScope: []string{oidc.ScopeOffline}, -					Session:      sess, -				}) -				require.NoError(t, err) -			}, -			err:      fosite.ErrInvalidGrant, -			expected: "The provided authorization grant (e.g., authorization code, resource owner credentials) or refresh token is invalid, expired, revoked, does not match the redirection URI used in the authorization request, or was issued to another client. The OAuth 2.0 Client ID from this request does not match the ID during the initial token issuance.", -		}, -		{ -			name: "ShouldFailExpiredToken", -			setup: func(config *fosite.Config, strategy oauth2.RefreshTokenStrategy, store *storage.MemoryStore, requester *fosite.AccessRequest) { -				requester.GrantTypes = fosite.Arguments{oidc.GrantTypeRefreshToken} -				requester.Client = &fosite.DefaultClient{ -					ID:         "foo", -					GrantTypes: fosite.Arguments{oidc.GrantTypeRefreshToken}, -					Scopes:     []string{"foo", "bar", oidc.ScopeOffline}, -				} - -				token, sig, err := strategy.GenerateRefreshToken(context.TODO(), nil) -				require.NoError(t, err) - -				requester.Form.Add(oidc.FormParameterRefreshToken, token) -				err = store.CreateRefreshTokenSession(context.TODO(), sig, &fosite.Request{ -					Client:         requester.Client, -					GrantedScope:   fosite.Arguments{"foo", oidc.ScopeOffline}, -					RequestedScope: fosite.Arguments{"foo", "bar", oidc.ScopeOffline}, -					Session:        expiredSess, -					Form:           url.Values{"foo": []string{"bar"}}, -					RequestedAt:    time.Now().UTC().Add(-time.Hour * 2).Round(time.Hour), -				}) -				require.NoError(t, err) -			}, -			err:       fosite.ErrInvalidGrant, -			rexpected: regexp.MustCompile(`^The provided authorization grant \(e.g., authorization code, resource owner credentials\) or refresh token is invalid, expired, revoked, does not match the redirection URI used in the authorization request, or was issued to another client\. Token expired\. Refresh token expired at '\d{4}-\d{2}-\d{2} \d{2}:\d{2}:\d{2}(.\d+)? \+0000 UTC'\.$`), -		}, -		{ -			name: "ShouldFailOfflineScopeRequestedButClientNotPermitted", -			setup: func(config *fosite.Config, strategy oauth2.RefreshTokenStrategy, store *storage.MemoryStore, requester *fosite.AccessRequest) { -				requester.GrantTypes = fosite.Arguments{oidc.GrantTypeRefreshToken} -				requester.Client = &fosite.DefaultClient{ -					ID:         "foo", -					GrantTypes: fosite.Arguments{oidc.GrantTypeRefreshToken}, -				} - -				token, sig, err := strategy.GenerateRefreshToken(context.TODO(), nil) -				require.NoError(t, err) - -				requester.Form.Add(oidc.FormParameterRefreshToken, token) -				err = store.CreateRefreshTokenSession(context.TODO(), sig, &fosite.Request{ -					Client:         requester.Client, -					GrantedScope:   fosite.Arguments{"foo", oidc.ScopeOffline}, -					RequestedScope: fosite.Arguments{"foo", oidc.ScopeOffline}, -					Session:        sess, -					Form:           url.Values{"foo": []string{"bar"}}, -					RequestedAt:    time.Now().UTC().Add(-time.Hour).Round(time.Hour), -				}) -				require.NoError(t, err) -			}, -			err:      fosite.ErrInvalidScope, -			expected: "The requested scope is invalid, unknown, or malformed. The OAuth 2.0 Client is not allowed to request scope 'foo'.", -		}, -		{ -			name: "ShouldPass", -			setup: func(config *fosite.Config, strategy oauth2.RefreshTokenStrategy, store *storage.MemoryStore, requester *fosite.AccessRequest) { -				requester.GrantTypes = fosite.Arguments{oidc.GrantTypeRefreshToken} -				requester.Client = &fosite.DefaultClient{ -					ID:         "foo", -					GrantTypes: fosite.Arguments{oidc.GrantTypeRefreshToken}, -					Scopes:     []string{"foo", "bar", oidc.ScopeOffline}, -				} - -				token, sig, err := strategy.GenerateRefreshToken(context.TODO(), nil) -				require.NoError(t, err) - -				requester.Form.Add(oidc.FormParameterRefreshToken, token) -				err = store.CreateRefreshTokenSession(context.TODO(), sig, &fosite.Request{ -					Client:         requester.Client, -					GrantedScope:   fosite.Arguments{"foo", oidc.ScopeOffline}, -					RequestedScope: fosite.Arguments{"foo", "bar", oidc.ScopeOffline}, -					Session:        sess, -					Form:           url.Values{"foo": []string{"bar"}}, -					RequestedAt:    time.Now().UTC().Add(-time.Hour).Round(time.Hour), -				}) -				require.NoError(t, err) -			}, -			texpected: func(t *testing.T, requester *fosite.AccessRequest) { -				assert.NotEqual(t, sess, requester.Session) -				assert.NotEqual(t, time.Now().UTC().Add(-time.Hour).Round(time.Hour), requester.RequestedAt) -				assert.Equal(t, fosite.Arguments{"foo", oidc.ScopeOffline}, requester.GrantedScope) -				assert.Equal(t, fosite.Arguments{"foo", oidc.ScopeOffline}, requester.RequestedScope) -				assert.NotEqual(t, url.Values{"foo": []string{"bar"}}, requester.Form) -				assert.Equal(t, time.Now().Add(time.Hour).UTC().Round(time.Second), requester.GetSession().GetExpiresAt(fosite.AccessToken)) -				assert.Equal(t, time.Now().Add(time.Hour).UTC().Round(time.Second), requester.GetSession().GetExpiresAt(fosite.RefreshToken)) -			}, -		}, -		{ -			name: "ShouldPassKeepOriginalID", -			setup: func(config *fosite.Config, strategy oauth2.RefreshTokenStrategy, store *storage.MemoryStore, requester *fosite.AccessRequest) { -				requester.GrantTypes = fosite.Arguments{oidc.GrantTypeRefreshToken} -				requester.Client = &fosite.DefaultClient{ -					ID:         "foo", -					GrantTypes: fosite.Arguments{oidc.GrantTypeRefreshToken}, -					Scopes:     []string{"foo", "bar", oidc.ScopeOffline}, -				} - -				token, sig, err := strategy.GenerateRefreshToken(context.TODO(), nil) -				require.NoError(t, err) - -				requester.Form.Add(oidc.FormParameterRefreshToken, token) -				err = store.CreateRefreshTokenSession(context.TODO(), sig, &fosite.Request{ -					ID:             "foo", -					Client:         requester.Client, -					GrantedScope:   fosite.Arguments{"foo", oidc.ScopeOffline}, -					RequestedScope: fosite.Arguments{"foo", "bar", oidc.ScopeOffline}, -					Session:        sess, -					Form:           url.Values{"foo": []string{"bar"}}, -					RequestedAt:    time.Now().UTC().Add(-time.Hour).Round(time.Hour), -				}) -				require.NoError(t, err) -			}, -			texpected: func(t *testing.T, requester *fosite.AccessRequest) { -				assert.Equal(t, "foo", requester.GetID()) -				assert.NotEqual(t, sess, requester.Session) -				assert.NotEqual(t, time.Now().UTC().Add(-time.Hour).Round(time.Hour), requester.RequestedAt) -				assert.Equal(t, fosite.Arguments{"foo", oidc.ScopeOffline}, requester.GrantedScope) -				assert.Equal(t, fosite.Arguments{"foo", oidc.ScopeOffline}, requester.RequestedScope) -				assert.NotEqual(t, url.Values{"foo": []string{"bar"}}, requester.Form) -				assert.Equal(t, time.Now().Add(time.Hour).UTC().Round(time.Second), requester.GetSession().GetExpiresAt(fosite.AccessToken)) -				assert.Equal(t, time.Now().Add(time.Hour).UTC().Round(time.Second), requester.GetSession().GetExpiresAt(fosite.RefreshToken)) -			}, -		}, -		{ -			name: "ShouldPassWithScope", -			setup: func(config *fosite.Config, strategy oauth2.RefreshTokenStrategy, store *storage.MemoryStore, requester *fosite.AccessRequest) { -				requester.GrantTypes = fosite.Arguments{oidc.GrantTypeRefreshToken} -				requester.Client = &fosite.DefaultClient{ -					ID:         "foo", -					GrantTypes: fosite.Arguments{oidc.GrantTypeRefreshToken}, -					Scopes:     []string{"foo", "bar", "baz", oidc.ScopeOffline}, -				} - -				token, sig, err := strategy.GenerateRefreshToken(context.TODO(), nil) -				require.NoError(t, err) - -				requester.Form.Add(oidc.FormParameterRefreshToken, token) -				requester.Form.Add(oidc.FormParameterScope, "foo bar baz offline") -				err = store.CreateRefreshTokenSession(context.TODO(), sig, &fosite.Request{ -					Client:         requester.Client, -					GrantedScope:   fosite.Arguments{"foo", "bar", "baz", oidc.ScopeOffline}, -					RequestedScope: fosite.Arguments{"foo", "bar", "baz", oidc.ScopeOffline}, -					Session:        sess, -					Form:           url.Values{"foo": []string{"bar"}}, -					RequestedAt:    time.Now().UTC().Add(-time.Hour).Round(time.Hour), -				}) -				require.NoError(t, err) -			}, -			texpected: func(t *testing.T, requester *fosite.AccessRequest) { -				assert.Equal(t, fosite.Arguments{"foo", "bar", "baz", oidc.ScopeOffline}, requester.GrantedScope) -				assert.Equal(t, fosite.Arguments{"foo", "bar", "baz", oidc.ScopeOffline}, requester.RequestedScope) -			}, -		}, -		{ -			name: "ShouldPassWithScopeNarrowing", -			setup: func(config *fosite.Config, strategy oauth2.RefreshTokenStrategy, store *storage.MemoryStore, requester *fosite.AccessRequest) { -				requester.GrantTypes = fosite.Arguments{oidc.GrantTypeRefreshToken} -				requester.Client = &fosite.DefaultClient{ -					ID:         "foo", -					GrantTypes: fosite.Arguments{oidc.GrantTypeRefreshToken}, -					Scopes:     []string{"foo", "bar", "baz", oidc.ScopeOffline}, -				} - -				token, sig, err := strategy.GenerateRefreshToken(context.TODO(), nil) -				require.NoError(t, err) - -				requester.Form.Add(oidc.FormParameterRefreshToken, token) -				requester.Form.Add(oidc.FormParameterScope, "foo bar offline") -				requester.SetRequestedScopes(fosite.Arguments{"foo", "bar", oidc.ScopeOffline}) - -				err = store.CreateRefreshTokenSession(context.TODO(), sig, &fosite.Request{ -					Client:         requester.Client, -					GrantedScope:   fosite.Arguments{"foo", "bar", "baz", oidc.ScopeOffline}, -					RequestedScope: fosite.Arguments{"foo", "bar", "baz", oidc.ScopeOffline}, -					Session:        sess, -					Form:           url.Values{"foo": []string{"bar"}}, -					RequestedAt:    time.Now().UTC().Add(-time.Hour).Round(time.Hour), -				}) -				require.NoError(t, err) -			}, -			texpected: func(t *testing.T, requester *fosite.AccessRequest) { -				assert.Equal(t, fosite.Arguments{"foo", "bar", oidc.ScopeOffline}, requester.GrantedScope) -				assert.Equal(t, fosite.Arguments{"foo", "bar", oidc.ScopeOffline}, requester.RequestedScope) -			}, -		}, -		{ -			name: "ShouldFailWithScopeBroadening", -			setup: func(config *fosite.Config, strategy oauth2.RefreshTokenStrategy, store *storage.MemoryStore, requester *fosite.AccessRequest) { -				requester.GrantTypes = fosite.Arguments{oidc.GrantTypeRefreshToken} -				requester.Client = &fosite.DefaultClient{ -					ID:         "foo", -					GrantTypes: fosite.Arguments{oidc.GrantTypeRefreshToken}, -					Scopes:     []string{"foo", "bar", "baz", oidc.ScopeOffline}, -				} - -				token, sig, err := strategy.GenerateRefreshToken(context.TODO(), nil) -				require.NoError(t, err) - -				requester.Form.Add(oidc.FormParameterRefreshToken, token) -				requester.Form.Add(oidc.FormParameterScope, "foo bar offline") -				requester.SetRequestedScopes(fosite.Arguments{"foo", "bar", oidc.ScopeOffline}) - -				err = store.CreateRefreshTokenSession(context.TODO(), sig, &fosite.Request{ -					Client:         requester.Client, -					GrantedScope:   fosite.Arguments{"foo", "baz", oidc.ScopeOffline}, -					RequestedScope: fosite.Arguments{"foo", "baz", oidc.ScopeOffline}, -					Session:        sess, -					Form:           url.Values{"foo": []string{"bar"}}, -					RequestedAt:    time.Now().UTC().Add(-time.Hour).Round(time.Hour), -				}) -				require.NoError(t, err) -			}, -			err:      fosite.ErrInvalidScope, -			expected: "The requested scope is invalid, unknown, or malformed. The requested scope 'bar' was not originally granted by the resource owner.", -		}, -		{ -			name: "ShouldPassWithScopeBroadeningOnRefreshFlowScopeClientTrue", -			setup: func(config *fosite.Config, strategy oauth2.RefreshTokenStrategy, store *storage.MemoryStore, requester *fosite.AccessRequest) { -				requester.GrantTypes = fosite.Arguments{oidc.GrantTypeRefreshToken} -				requester.Client = &oidc.RegisteredClient{ -					ID:                                     "foo", -					GrantTypes:                             fosite.Arguments{oidc.GrantTypeRefreshToken}, -					Scopes:                                 []string{"foo", "bar", "baz", oidc.ScopeOffline}, -					RefreshFlowIgnoreOriginalGrantedScopes: true, -				} - -				token, sig, err := strategy.GenerateRefreshToken(context.TODO(), nil) -				require.NoError(t, err) - -				requester.Form.Add(oidc.FormParameterRefreshToken, token) -				requester.Form.Add(oidc.FormParameterScope, "foo bar offline") -				requester.SetRequestedScopes(fosite.Arguments{"foo", "bar", oidc.ScopeOffline}) - -				err = store.CreateRefreshTokenSession(context.TODO(), sig, &fosite.Request{ -					Client:         requester.Client, -					GrantedScope:   fosite.Arguments{"foo", "baz", oidc.ScopeOffline}, -					RequestedScope: fosite.Arguments{"foo", "baz", oidc.ScopeOffline}, -					Session:        sess, -					Form:           url.Values{"foo": []string{"bar"}}, -					RequestedAt:    time.Now().UTC().Add(-time.Hour).Round(time.Hour), -				}) -				require.NoError(t, err) -			}, -			texpected: func(t *testing.T, requester *fosite.AccessRequest) { -				assert.Equal(t, fosite.Arguments{"foo", oidc.ScopeOffline}, requester.GrantedScope) -				assert.Equal(t, fosite.Arguments{"foo", "bar", oidc.ScopeOffline}, requester.RequestedScope) -			}, -		}, -		{ -			name: "ShouldFailWithScopeBroadeningOnRefreshFlowScopeClientFalse", -			setup: func(config *fosite.Config, strategy oauth2.RefreshTokenStrategy, store *storage.MemoryStore, requester *fosite.AccessRequest) { -				requester.GrantTypes = fosite.Arguments{oidc.GrantTypeRefreshToken} -				requester.Client = &oidc.RegisteredClient{ -					ID:                                     "foo", -					GrantTypes:                             fosite.Arguments{oidc.GrantTypeRefreshToken}, -					Scopes:                                 []string{"foo", "bar", "baz", oidc.ScopeOffline}, -					RefreshFlowIgnoreOriginalGrantedScopes: false, -				} - -				token, sig, err := strategy.GenerateRefreshToken(context.TODO(), nil) -				require.NoError(t, err) - -				requester.Form.Add(oidc.FormParameterRefreshToken, token) -				requester.Form.Add(oidc.FormParameterScope, "foo bar offline") -				requester.SetRequestedScopes(fosite.Arguments{"foo", "bar", oidc.ScopeOffline}) - -				err = store.CreateRefreshTokenSession(context.TODO(), sig, &fosite.Request{ -					Client:         requester.Client, -					GrantedScope:   fosite.Arguments{"foo", "baz", oidc.ScopeOffline}, -					RequestedScope: fosite.Arguments{"foo", "baz", oidc.ScopeOffline}, -					Session:        sess, -					Form:           url.Values{"foo": []string{"bar"}}, -					RequestedAt:    time.Now().UTC().Add(-time.Hour).Round(time.Hour), -				}) -				require.NoError(t, err) -			}, -			err:      fosite.ErrInvalidScope, -			expected: "The requested scope is invalid, unknown, or malformed. The requested scope 'bar' was not originally granted by the resource owner.", -		}, -		{ -			name: "ShouldPassWithCustomLifespans", -			setup: func(config *fosite.Config, strategy oauth2.RefreshTokenStrategy, store *storage.MemoryStore, requester *fosite.AccessRequest) { -				requester.GrantTypes = fosite.Arguments{oidc.GrantTypeRefreshToken} -				requester.Client = &fosite.DefaultClientWithCustomTokenLifespans{ -					DefaultClient: &fosite.DefaultClient{ -						ID:         "foo", -						GrantTypes: fosite.Arguments{oidc.GrantTypeRefreshToken}, -						Scopes:     []string{"foo", "bar", oidc.ScopeOffline}, -					}, -				} - -				requester.Client.(*fosite.DefaultClientWithCustomTokenLifespans).SetTokenLifespans(&TestLifespans) - -				token, sig, err := strategy.GenerateRefreshToken(context.TODO(), nil) -				require.NoError(t, err) - -				requester.Form.Add(oidc.FormParameterRefreshToken, token) -				err = store.CreateRefreshTokenSession(context.TODO(), sig, &fosite.Request{ -					Client:         requester.Client, -					GrantedScope:   fosite.Arguments{"foo", oidc.ScopeOffline}, -					RequestedScope: fosite.Arguments{"foo", "bar", oidc.ScopeOffline}, -					Session:        sess, -					Form:           url.Values{"foo": []string{"bar"}}, -					RequestedAt:    time.Now().UTC().Add(-time.Hour).Round(time.Hour), -				}) -				require.NoError(t, err) -			}, -			texpected: func(t *testing.T, requester *fosite.AccessRequest) { -				assert.NotEqual(t, sess, requester.Session) -				assert.NotEqual(t, time.Now().UTC().Add(-time.Hour).Round(time.Hour), requester.RequestedAt) -				assert.Equal(t, fosite.Arguments{"foo", oidc.ScopeOffline}, requester.GrantedScope) -				assert.Equal(t, fosite.Arguments{"foo", oidc.ScopeOffline}, requester.RequestedScope) -				assert.NotEqual(t, url.Values{"foo": []string{"bar"}}, requester.Form) - -				require.WithinDuration(t, time.Now().Add(*TestLifespans.RefreshTokenGrantAccessTokenLifespan).UTC(), requester.GetSession().GetExpiresAt(fosite.AccessToken).UTC(), time.Minute) -				require.WithinDuration(t, time.Now().Add(*TestLifespans.RefreshTokenGrantRefreshTokenLifespan).UTC(), requester.GetSession().GetExpiresAt(fosite.RefreshToken).UTC(), time.Minute) -			}, -		}, -		{ -			name: "ShouldFailWithoutOfflineScope", -			setup: func(config *fosite.Config, strategy oauth2.RefreshTokenStrategy, store *storage.MemoryStore, requester *fosite.AccessRequest) { -				requester.GrantTypes = fosite.Arguments{oidc.GrantTypeRefreshToken} -				requester.Client = &fosite.DefaultClient{ -					ID:         "foo", -					GrantTypes: fosite.Arguments{oidc.GrantTypeRefreshToken}, -					Scopes:     []string{"foo", "bar"}, -				} - -				token, sig, err := strategy.GenerateRefreshToken(context.TODO(), nil) -				require.NoError(t, err) - -				requester.Form.Add(oidc.FormParameterRefreshToken, token) -				err = store.CreateRefreshTokenSession(context.TODO(), sig, &fosite.Request{ -					Client:         requester.Client, -					GrantedScope:   fosite.Arguments{"foo"}, -					RequestedScope: fosite.Arguments{"foo", "bar"}, -					Session:        sess, -					Form:           url.Values{"foo": []string{"bar"}}, -					RequestedAt:    time.Now().UTC().Add(-time.Hour).Round(time.Hour), -				}) -				require.NoError(t, err) -			}, -			err:      fosite.ErrScopeNotGranted, -			expected: "The token was not granted the requested scope. The OAuth 2.0 Client was not granted scope offline and may thus not perform the 'refresh_token' authorization grant.", -		}, -		{ -			name: "ShouldPassWithoutOfflineScopeWhenConfigured", -			setup: func(config *fosite.Config, strategy oauth2.RefreshTokenStrategy, store *storage.MemoryStore, requester *fosite.AccessRequest) { -				config.RefreshTokenScopes = []string{} -				requester.GrantTypes = fosite.Arguments{oidc.GrantTypeRefreshToken} -				requester.Client = &fosite.DefaultClient{ -					ID:         "foo", -					GrantTypes: fosite.Arguments{oidc.GrantTypeRefreshToken}, -					Scopes:     []string{"foo", "bar"}, -				} - -				token, sig, err := strategy.GenerateRefreshToken(context.TODO(), nil) -				require.NoError(t, err) - -				requester.Form.Add(oidc.FormParameterRefreshToken, token) -				err = store.CreateRefreshTokenSession(context.TODO(), sig, &fosite.Request{ -					Client:         requester.Client, -					GrantedScope:   fosite.Arguments{"foo"}, -					RequestedScope: fosite.Arguments{"foo", "bar"}, -					Session:        sess, -					Form:           url.Values{"foo": []string{"bar"}}, -					RequestedAt:    time.Now().UTC().Add(-time.Hour).Round(time.Hour), -				}) -				require.NoError(t, err) -			}, -			texpected: func(t *testing.T, requester *fosite.AccessRequest) { -				assert.NotEqual(t, sess, requester.Session) -				assert.NotEqual(t, time.Now().UTC().Add(-time.Hour).Round(time.Hour), requester.RequestedAt) -				assert.Equal(t, fosite.Arguments{"foo"}, requester.GrantedScope) -				assert.Equal(t, fosite.Arguments{"foo"}, requester.RequestedScope) -				assert.NotEqual(t, url.Values{"foo": []string{"bar"}}, requester.Form) -				assert.Equal(t, time.Now().Add(time.Hour).UTC().Round(time.Second), requester.GetSession().GetExpiresAt(fosite.AccessToken)) -				assert.Equal(t, time.Now().Add(time.Hour).UTC().Round(time.Second), requester.GetSession().GetExpiresAt(fosite.RefreshToken)) -			}, -		}, -		{ -			name: "ShouldFailOnReuse", -			setup: func(config *fosite.Config, strategy oauth2.RefreshTokenStrategy, store *storage.MemoryStore, requester *fosite.AccessRequest) { -				requester.GrantTypes = fosite.Arguments{oidc.GrantTypeRefreshToken} -				requester.Client = &fosite.DefaultClient{ -					ID:         "foo", -					GrantTypes: fosite.Arguments{oidc.GrantTypeRefreshToken}, -					Scopes:     []string{"foo", "bar", oidc.ScopeOffline}, -				} - -				token, sig, err := strategy.GenerateRefreshToken(context.TODO(), nil) -				require.NoError(t, err) - -				requester.Form.Add(oidc.FormParameterRefreshToken, token) -				req := &fosite.Request{ -					Client:         requester.Client, -					GrantedScope:   fosite.Arguments{"foo", oidc.ScopeOffline}, -					RequestedScope: fosite.Arguments{"foo", "bar", oidc.ScopeOffline}, -					Session:        sess, -					Form:           url.Values{"foo": []string{"bar"}}, -					RequestedAt:    time.Now().UTC().Add(-time.Hour).Round(time.Hour), -				} -				err = store.CreateRefreshTokenSession(context.TODO(), sig, req) -				require.NoError(t, err) - -				err = store.RevokeRefreshToken(context.TODO(), req.ID) -				require.NoError(t, err) -			}, -			err:      fosite.ErrInactiveToken, -			expected: "Token is inactive because it is malformed, expired or otherwise invalid. Token validation failed. Token is inactive because it is malformed, expired or otherwise invalid. Token validation failed.", -		}, -		{ -			name: "ShouldFailOnMalformedAudience", -			setup: func(config *fosite.Config, strategy oauth2.RefreshTokenStrategy, store *storage.MemoryStore, requester *fosite.AccessRequest) { -				requester.GrantTypes = fosite.Arguments{oidc.GrantTypeRefreshToken} -				requester.Client = &fosite.DefaultClient{ -					ID:         "foo", -					GrantTypes: fosite.Arguments{oidc.GrantTypeRefreshToken}, -					Scopes:     []string{"foo", "bar", oidc.ScopeOffline}, -				} - -				token, sig, err := strategy.GenerateRefreshToken(context.TODO(), nil) -				require.NoError(t, err) - -				requester.Form.Add(oidc.FormParameterRefreshToken, token) -				err = store.CreateRefreshTokenSession(context.TODO(), sig, &fosite.Request{ -					Client:          requester.Client, -					GrantedScope:    fosite.Arguments{"foo", oidc.ScopeOffline}, -					RequestedScope:  fosite.Arguments{"foo", "bar", oidc.ScopeOffline}, -					GrantedAudience: fosite.Arguments{string([]byte{0x00, 0x01, 0x02})}, -					Session:         sess, -					Form:            url.Values{"foo": []string{"bar"}}, -					RequestedAt:     time.Now().UTC().Add(-time.Hour).Round(time.Hour), -				}) -				require.NoError(t, err) -			}, -			err:      fosite.ErrInvalidRequest, -			expected: "The request is missing a required parameter, includes an invalid parameter value, includes a parameter more than once, or is otherwise malformed. Unable to parse requested audience '\x00\x01\x02'. parse '\\x00\\x01\\x02': net/url: invalid control character in URL", -		}, -		{ -			name: "ShouldFailOnUnauthorizedAudience", -			setup: func(config *fosite.Config, strategy oauth2.RefreshTokenStrategy, store *storage.MemoryStore, requester *fosite.AccessRequest) { -				requester.GrantTypes = fosite.Arguments{oidc.GrantTypeRefreshToken} -				requester.Client = &fosite.DefaultClient{ -					ID:         "foo", -					GrantTypes: fosite.Arguments{oidc.GrantTypeRefreshToken}, -					Scopes:     []string{"foo", "bar", oidc.ScopeOffline}, -				} - -				token, sig, err := strategy.GenerateRefreshToken(context.TODO(), nil) -				require.NoError(t, err) - -				requester.Form.Add(oidc.FormParameterRefreshToken, token) -				err = store.CreateRefreshTokenSession(context.TODO(), sig, &fosite.Request{ -					Client:          requester.Client, -					GrantedScope:    fosite.Arguments{"foo", oidc.ScopeOffline}, -					RequestedScope:  fosite.Arguments{"foo", "bar", oidc.ScopeOffline}, -					GrantedAudience: fosite.Arguments{"https://foo.com"}, -					Session:         sess, -					Form:            url.Values{"foo": []string{"bar"}}, -					RequestedAt:     time.Now().UTC().Add(-time.Hour).Round(time.Hour), -				}) -				require.NoError(t, err) -			}, -			err:      fosite.ErrInvalidRequest, -			expected: "The request is missing a required parameter, includes an invalid parameter value, includes a parameter more than once, or is otherwise malformed. Requested audience 'https://foo.com' has not been whitelisted by the OAuth 2.0 Client.", -		}, -		{ -			name: "ShouldPassOnPermittedAudienceAndGrantPreviousAudiences", -			setup: func(config *fosite.Config, strategy oauth2.RefreshTokenStrategy, store *storage.MemoryStore, requester *fosite.AccessRequest) { -				requester.GrantTypes = fosite.Arguments{oidc.GrantTypeRefreshToken} -				requester.Client = &fosite.DefaultClient{ -					ID:         "foo", -					GrantTypes: fosite.Arguments{oidc.GrantTypeRefreshToken}, -					Audience:   []string{"https://foo.com"}, -					Scopes:     []string{"foo", "bar", oidc.ScopeOffline}, -				} - -				token, sig, err := strategy.GenerateRefreshToken(context.TODO(), nil) -				require.NoError(t, err) - -				requester.Form.Add(oidc.FormParameterRefreshToken, token) -				err = store.CreateRefreshTokenSession(context.TODO(), sig, &fosite.Request{ -					Client:          requester.Client, -					GrantedScope:    fosite.Arguments{"foo", oidc.ScopeOffline}, -					RequestedScope:  fosite.Arguments{"foo", "bar", oidc.ScopeOffline}, -					GrantedAudience: fosite.Arguments{"https://foo.com"}, -					Session:         sess, -					Form:            url.Values{"foo": []string{"bar"}}, -					RequestedAt:     time.Now().UTC().Add(-time.Hour).Round(time.Hour), -				}) -				require.NoError(t, err) -			}, -			texpected: func(t *testing.T, requester *fosite.AccessRequest) { -				assert.NotEqual(t, sess, requester.Session) -				assert.NotEqual(t, time.Now().UTC().Add(-time.Hour).Round(time.Hour), requester.RequestedAt) -				assert.Equal(t, fosite.Arguments{"foo", oidc.ScopeOffline}, requester.GrantedScope) -				assert.Equal(t, fosite.Arguments{"foo", oidc.ScopeOffline}, requester.RequestedScope) -				assert.Equal(t, fosite.Arguments{"https://foo.com"}, requester.GrantedAudience) -				assert.Equal(t, fosite.Arguments(nil), requester.RequestedAudience) -				assert.NotEqual(t, url.Values{"foo": []string{"bar"}}, requester.Form) -				assert.Equal(t, time.Now().Add(time.Hour).UTC().Round(time.Second), requester.GetSession().GetExpiresAt(fosite.AccessToken)) -				assert.Equal(t, time.Now().Add(time.Hour).UTC().Round(time.Second), requester.GetSession().GetExpiresAt(fosite.RefreshToken)) -			}, -		}, -	} - -	factory := func() (oauth2.RefreshTokenStrategy, *storage.MemoryStore, *fosite.AccessRequest) { -		return &oauth2.HMACSHAStrategy{ -			Enigma: &hmac.HMACStrategy{Config: &fosite.Config{GlobalSecret: []byte("foobarfoobarfoobarfoobarfoobarfoobarfoobarfoobar")}}, -			Config: &fosite.Config{ -				AccessTokenLifespan:   time.Hour * 24, -				AuthorizeCodeLifespan: time.Hour * 24, -			}, -		}, storage.NewMemoryStore(), fosite.NewAccessRequest(&fosite.DefaultSession{}) -	} - -	for _, tc := range testCases { -		t.Run(tc.name, func(t *testing.T) { -			strategy, store, requester := factory() - -			config := &fosite.Config{ -				AccessTokenLifespan:      time.Hour, -				RefreshTokenLifespan:     time.Hour, -				ScopeStrategy:            fosite.HierarchicScopeStrategy, -				AudienceMatchingStrategy: fosite.DefaultAudienceMatchingStrategy, -				RefreshTokenScopes:       []string{oidc.ScopeOffline}, -			} - -			handler := &oidc.RefreshTokenGrantHandler{ -				TokenRevocationStorage: store, -				RefreshTokenStrategy:   strategy, -				Config:                 config, -			} - -			requester.Form = url.Values{} - -			tc.setup(config, strategy, store, requester) - -			err := handler.HandleTokenEndpointRequest(context.TODO(), requester) -			if tc.err != nil { -				assert.Equal(t, tc.err.Error(), err.Error()) - -				if tc.rexpected != nil { -					assert.Regexp(t, tc.rexpected, oidc.ErrorToDebugRFC6749Error(err)) -				} else { -					assert.EqualError(t, oidc.ErrorToDebugRFC6749Error(err), tc.expected) -				} -			} else { -				assert.NoError(t, oidc.ErrorToDebugRFC6749Error(err)) -			} - -			if tc.texpected != nil { -				tc.texpected(t, requester) -			} -		}) -	} -} - -func TestRefreshTokenGrantHandler_PopulateTokenEndpointResponse(t *testing.T) { -	testCases := []struct { -		name     string -		setup    func(config *fosite.Config, strategy oauth2.CoreStrategy, store *storage.MemoryStore, requester *fosite.AccessRequest) -		check    func(t *testing.T, strategy oauth2.CoreStrategy, store *storage.MemoryStore, requester *fosite.AccessRequest, responder *fosite.AccessResponse) -		err      error -		expected string -	}{ -		{ -			name: "ShouldPass", -			setup: func(config *fosite.Config, strategy oauth2.CoreStrategy, store *storage.MemoryStore, requester *fosite.AccessRequest) { -				requester.ID = "req-id" -				requester.GrantTypes = fosite.Arguments{oidc.GrantTypeRefreshToken} -				requester.RequestedScope = fosite.Arguments{"foo", "bar"} -				requester.GrantedScope = fosite.Arguments{"foo", "bar"} - -				token, signature, err := strategy.GenerateRefreshToken(context.TODO(), nil) -				require.NoError(t, err) -				require.NoError(t, store.CreateRefreshTokenSession(context.TODO(), signature, requester)) -				requester.Form.Add(oidc.FormParameterRefreshToken, token) -			}, -			check: func(t *testing.T, strategy oauth2.CoreStrategy, store *storage.MemoryStore, requester *fosite.AccessRequest, responder *fosite.AccessResponse) { -				signature := strategy.RefreshTokenSignature(context.Background(), requester.Form.Get(oidc.FormParameterRefreshToken)) - -				// The old refresh token should be deleted. -				_, err := store.GetRefreshTokenSession(context.TODO(), signature, nil) -				require.Error(t, err) - -				assert.Equal(t, "req-id", requester.ID) -				require.NoError(t, strategy.ValidateAccessToken(context.TODO(), requester, responder.GetAccessToken())) -				require.NoError(t, strategy.ValidateRefreshToken(context.TODO(), requester, responder.ToMap()[oidc.FormParameterRefreshToken].(string))) -				assert.Equal(t, fosite.BearerAccessToken, responder.GetTokenType()) -				assert.NotEmpty(t, responder.ToMap()["expires_in"]) -				assert.Equal(t, "foo bar", responder.ToMap()[oidc.FormParameterScope]) -			}, -		}, -		{ -			name: "ShouldFailNotResponsible", -			setup: func(config *fosite.Config, strategy oauth2.CoreStrategy, store *storage.MemoryStore, requester *fosite.AccessRequest) { -				requester.GrantTypes = fosite.Arguments{"313"} -			}, -			err:      fosite.ErrUnknownRequest, -			expected: "The handler is not responsible for this request.", -		}, -	} - -	factory := func() (oauth2.CoreStrategy, *storage.MemoryStore, *fosite.AccessRequest, *fosite.AccessResponse) { -		return &oauth2.HMACSHAStrategy{ -			Enigma: &hmac.HMACStrategy{Config: &fosite.Config{GlobalSecret: []byte("foobarfoobarfoobarfoobarfoobarfoobarfoobarfoobar")}}, -			Config: &fosite.Config{ -				AccessTokenLifespan:   time.Hour * 24, -				AuthorizeCodeLifespan: time.Hour * 24, -			}, -		}, storage.NewMemoryStore(), fosite.NewAccessRequest(&fosite.DefaultSession{}), fosite.NewAccessResponse() -	} - -	for _, tc := range testCases { -		t.Run(tc.name, func(t *testing.T) { -			strategy, store, requester, responder := factory() - -			config := &fosite.Config{ -				AccessTokenLifespan:      time.Hour, -				ScopeStrategy:            fosite.HierarchicScopeStrategy, -				AudienceMatchingStrategy: fosite.DefaultAudienceMatchingStrategy, -			} - -			h := oidc.RefreshTokenGrantHandler{ -				TokenRevocationStorage: store, -				RefreshTokenStrategy:   strategy, -				AccessTokenStrategy:    strategy, -				Config:                 config, -			} - -			requester.Client = &fosite.DefaultClient{} -			requester.Form = url.Values{} - -			tc.setup(config, strategy, store, requester) - -			err := h.PopulateTokenEndpointResponse(context.TODO(), requester, responder) -			if tc.err != nil { -				assert.EqualError(t, err, tc.err.Error()) -				assert.EqualError(t, oidc.ErrorToDebugRFC6749Error(err), tc.expected) -			} else { -				assert.NoError(t, oidc.ErrorToDebugRFC6749Error(err)) -			} - -			if tc.check != nil { -				tc.check(t, strategy, store, requester, responder) -			} -		}) -	} -} - -func TestRefreshFlowSanitizeRestoreOriginalRequest(t *testing.T) { -	testCases := []struct { -		name      string -		requester fosite.Requester -		original  fosite.Requester -		expected  fosite.Arguments -	}{ -		{ -			"ShouldRestoreIDAndScopeWhenAccessRequest", -			&fosite.AccessRequest{ -				Request: fosite.Request{ -					ID: "test", -				}, -			}, -			&fosite.AccessRequest{ -				Request: fosite.Request{ -					ID:           "test2", -					GrantedScope: fosite.Arguments{abc, "123"}, -				}, -			}, -			fosite.Arguments{abc, "123"}, -		}, -		{ -			"ShouldRestoreIDAndNotRestoreScopeWhenRequest", -			&fosite.Request{ -				ID: "test", -			}, -			&fosite.AccessRequest{ -				Request: fosite.Request{ -					ID:           "test2", -					GrantedScope: fosite.Arguments{abc, "123"}, -				}, -			}, -			fosite.Arguments(nil), -		}, -	} - -	for _, tc := range testCases { -		t.Run(tc.name, func(t *testing.T) { -			actual := oidc.RefreshFlowSanitizeRestoreOriginalRequest(tc.requester, tc.original) - -			assert.Equal(t, tc.original.GetID(), actual.GetID()) -			assert.Equal(t, tc.expected, actual.GetGrantedScopes()) -		}) -	} -} - -func TestRefreshTokenGrantHandler_HandleTokenEndpointRequest_Tx(t *testing.T) { -	type store struct { -		storage.Transactional -		oauth2.TokenRevocationStorage -	} - -	testCases := []struct { -		name      string -		setup     func(ctx context.Context, request *fosite.AccessRequest, transactional *mocks.MockTransactional, revocation *mocks.MockTokenRevocationStorage) -		err       error -		expected  string -		texpected func(t *testing.T, request *fosite.AccessRequest) -	}{ -		{ -			name: "ShouldRevokeSessionOnTokenReuse", -			setup: func(ctx context.Context, request *fosite.AccessRequest, transactional *mocks.MockTransactional, revocation *mocks.MockTokenRevocationStorage) { -				request.GrantTypes = fosite.Arguments{oidc.GrantTypeRefreshToken} -				request.Client = &fosite.DefaultClient{ -					ID:         "foo", -					GrantTypes: fosite.Arguments{oidc.GrantTypeRefreshToken}, -				} -				revocation. -					EXPECT(). -					GetRefreshTokenSession(ctx, gomock.Any(), gomock.Any()). -					Return(request, fosite.ErrInactiveToken). -					Times(1) -				transactional. -					EXPECT(). -					BeginTX(ctx). -					Return(ctx, nil). -					Times(1) -				revocation. -					EXPECT(). -					DeleteRefreshTokenSession(ctx, gomock.Any()). -					Return(nil). -					Times(1) -				revocation. -					EXPECT(). -					RevokeRefreshToken(ctx, gomock.Any()). -					Return(nil). -					Times(1) -				revocation. -					EXPECT(). -					RevokeAccessToken(ctx, gomock.Any()). -					Return(nil). -					Times(1) -				transactional. -					EXPECT(). -					Commit(ctx). -					Return(nil). -					Times(1) -			}, -			err:      fosite.ErrInactiveToken, -			expected: "Token is inactive because it is malformed, expired or otherwise invalid. Token validation failed. Token is inactive because it is malformed, expired or otherwise invalid. Token validation failed.", -		}, -	} - -	for _, tc := range testCases { -		t.Run(tc.name, func(t *testing.T) { -			ctx := context.Background() -			request := fosite.NewAccessRequest(&fosite.DefaultSession{}) - -			ctrl := gomock.NewController(t) -			defer ctrl.Finish() - -			transactional := mocks.NewMockTransactional(ctrl) -			revocation := mocks.NewMockTokenRevocationStorage(ctrl) -			tc.setup(ctx, request, transactional, revocation) - -			strategy := &oauth2.HMACSHAStrategy{ -				Enigma: &hmac.HMACStrategy{Config: &fosite.Config{GlobalSecret: []byte("foobarfoobarfoobarfoobarfoobarfoobarfoobarfoobar")}}, -				Config: &fosite.Config{ -					AccessTokenLifespan:   time.Hour * 24, -					AuthorizeCodeLifespan: time.Hour * 24, -				}, -			} - -			handler := oidc.RefreshTokenGrantHandler{ -				TokenRevocationStorage: store{ -					transactional, -					revocation, -				}, -				AccessTokenStrategy:  strategy, -				RefreshTokenStrategy: strategy, -				Config: &fosite.Config{ -					AccessTokenLifespan:      time.Hour, -					ScopeStrategy:            fosite.HierarchicScopeStrategy, -					AudienceMatchingStrategy: fosite.DefaultAudienceMatchingStrategy, -				}, -			} - -			err := handler.HandleTokenEndpointRequest(ctx, request) - -			if tc.err != nil { -				assert.EqualError(t, err, tc.err.Error()) -				assert.EqualError(t, oidc.ErrorToDebugRFC6749Error(err), tc.expected) -			} else { -				assert.NoError(t, oidc.ErrorToDebugRFC6749Error(err)) -			} - -			if tc.texpected != nil { -				tc.texpected(t, request) -			} -		}) -	} -} - -func TestRefreshTokenGrantHandler_PopulateTokenEndpointResponse_Tx(t *testing.T) { -	type store struct { -		storage.Transactional -		oauth2.TokenRevocationStorage -	} - -	testCases := []struct { -		name      string -		setup     func(ctx context.Context, request *fosite.AccessRequest, transactional *mocks.MockTransactional, revocation *mocks.MockTokenRevocationStorage) -		err       error -		expected  string -		texpected func(t *testing.T, request *fosite.AccessRequest, response *fosite.AccessResponse) -	}{ -		{ -			name: "ShouldSuccessfullyCommitTxWhenNoErrors", -			setup: func(ctx context.Context, request *fosite.AccessRequest, transactional *mocks.MockTransactional, revocation *mocks.MockTokenRevocationStorage) { -				request.GrantTypes = fosite.Arguments{oidc.GrantTypeRefreshToken} -				transactional. -					EXPECT(). -					BeginTX(ctx). -					Return(ctx, nil). -					Times(1) -				revocation. -					EXPECT(). -					GetRefreshTokenSession(ctx, gomock.Any(), nil). -					Return(request, nil). -					Times(1) -				revocation. -					EXPECT(). -					RevokeAccessToken(ctx, gomock.Any()). -					Return(nil). -					Times(1) -				revocation. -					EXPECT(). -					RevokeRefreshTokenMaybeGracePeriod(ctx, gomock.Any(), gomock.Any()). -					Return(nil). -					Times(1) -				revocation. -					EXPECT(). -					CreateAccessTokenSession(ctx, gomock.Any(), gomock.Any()). -					Return(nil). -					Times(1) -				revocation. -					EXPECT(). -					CreateRefreshTokenSession(ctx, gomock.Any(), gomock.Any()). -					Return(nil). -					Times(1) -				transactional. -					EXPECT(). -					Commit(ctx). -					Return(nil). -					Times(1) -			}, -		}, -		{ -			name: "ShouldSuccessfullyRollbackTxWhenErrorsFromGetRefreshTokenSession", -			setup: func(ctx context.Context, request *fosite.AccessRequest, transactional *mocks.MockTransactional, revocation *mocks.MockTokenRevocationStorage) { -				request.GrantTypes = fosite.Arguments{oidc.GrantTypeRefreshToken} -				transactional. -					EXPECT(). -					BeginTX(ctx). -					Return(ctx, nil). -					Times(1) -				revocation. -					EXPECT(). -					GetRefreshTokenSession(ctx, gomock.Any(), nil). -					Return(nil, errors.New("Whoops, a nasty database error occurred!")). -					Times(1) -				transactional. -					EXPECT(). -					Rollback(ctx). -					Return(nil). -					Times(1) -			}, -			err:      fosite.ErrServerError, -			expected: "The authorization server encountered an unexpected condition that prevented it from fulfilling the request. Whoops, a nasty database error occurred!", -		}, -		{ -			name: "ShouldErrorErrInvalidRequestWhenGetRefreshTokenSessionErrNotFound", -			setup: func(ctx context.Context, request *fosite.AccessRequest, transactional *mocks.MockTransactional, revocation *mocks.MockTokenRevocationStorage) { -				request.GrantTypes = fosite.Arguments{oidc.GrantTypeRefreshToken} -				transactional. -					EXPECT(). -					BeginTX(ctx). -					Return(ctx, nil). -					Times(1) -				revocation. -					EXPECT(). -					GetRefreshTokenSession(ctx, gomock.Any(), nil). -					Return(nil, fosite.ErrNotFound). -					Times(1) -				transactional. -					EXPECT(). -					Rollback(ctx). -					Return(nil). -					Times(1) -			}, -			err:      fosite.ErrInvalidRequest, -			expected: "The request is missing a required parameter, includes an invalid parameter value, includes a parameter more than once, or is otherwise malformed. Failed to refresh token because of multiple concurrent requests using the same token which is not allowed. Could not find the requested resource(s).", -		}, -		{ -			name: "ShouldSuccessfullyRollbackTxWhenErrorsFromRevokeAccessToken", -			setup: func(ctx context.Context, request *fosite.AccessRequest, transactional *mocks.MockTransactional, revocation *mocks.MockTokenRevocationStorage) { -				request.GrantTypes = fosite.Arguments{oidc.GrantTypeRefreshToken} -				transactional. -					EXPECT(). -					BeginTX(ctx). -					Return(ctx, nil). -					Times(1) -				revocation. -					EXPECT(). -					GetRefreshTokenSession(ctx, gomock.Any(), nil). -					Return(request, nil). -					Times(1) -				revocation. -					EXPECT(). -					RevokeAccessToken(ctx, gomock.Any()). -					Return(errors.New("Whoops, a nasty database error occurred!")). -					Times(1) -				transactional. -					EXPECT(). -					Rollback(ctx). -					Return(nil). -					Times(1) -			}, -			err:      fosite.ErrServerError, -			expected: "The authorization server encountered an unexpected condition that prevented it from fulfilling the request. Whoops, a nasty database error occurred!", -		}, -		{ -			name: "ShouldErrorErrInvalidRequestWhenRevokeAccessTokenErrSerializationFailure", -			setup: func(ctx context.Context, request *fosite.AccessRequest, transactional *mocks.MockTransactional, revocation *mocks.MockTokenRevocationStorage) { -				request.GrantTypes = fosite.Arguments{oidc.GrantTypeRefreshToken} -				transactional. -					EXPECT(). -					BeginTX(ctx). -					Return(ctx, nil). -					Times(1) -				revocation. -					EXPECT(). -					GetRefreshTokenSession(ctx, gomock.Any(), nil). -					Return(request, nil). -					Times(1) -				revocation. -					EXPECT(). -					RevokeAccessToken(ctx, gomock.Any()). -					Return(fosite.ErrSerializationFailure). -					Times(1) -				transactional. -					EXPECT(). -					Rollback(ctx). -					Return(nil). -					Times(1) -			}, -			err:      fosite.ErrInvalidRequest, -			expected: "The request is missing a required parameter, includes an invalid parameter value, includes a parameter more than once, or is otherwise malformed. Failed to refresh token because of multiple concurrent requests using the same token which is not allowed. The request could not be completed due to concurrent access", -		}, -		{ -			name: "ShouldErrorErrInactiveTokenWhenRevokeAccessTokenErrInvalidRequest", -			setup: func(ctx context.Context, request *fosite.AccessRequest, transactional *mocks.MockTransactional, revocation *mocks.MockTokenRevocationStorage) { -				request.GrantTypes = fosite.Arguments{oidc.GrantTypeRefreshToken} -				transactional. -					EXPECT(). -					BeginTX(ctx). -					Return(ctx, nil). -					Times(1) -				revocation. -					EXPECT(). -					GetRefreshTokenSession(ctx, gomock.Any(), nil). -					Return(nil, fosite.ErrInactiveToken). -					Times(1) -				transactional. -					EXPECT(). -					Rollback(ctx). -					Return(nil). -					Times(1) -			}, -			err:      fosite.ErrInvalidRequest, -			expected: "The request is missing a required parameter, includes an invalid parameter value, includes a parameter more than once, or is otherwise malformed. Failed to refresh token because of multiple concurrent requests using the same token which is not allowed. Token is inactive because it is malformed, expired or otherwise invalid. Token validation failed.", -		}, -		{ -			name: "ShouldSuccessfullyRollbackTxWhenErrorsFromRevokeRefreshTokenMaybeGracePeriod", -			setup: func(ctx context.Context, request *fosite.AccessRequest, transactional *mocks.MockTransactional, revocation *mocks.MockTokenRevocationStorage) { -				request.GrantTypes = fosite.Arguments{oidc.GrantTypeRefreshToken} -				transactional. -					EXPECT(). -					BeginTX(ctx). -					Return(ctx, nil). -					Times(1) -				revocation. -					EXPECT(). -					GetRefreshTokenSession(ctx, gomock.Any(), nil). -					Return(request, nil). -					Times(1) -				revocation. -					EXPECT(). -					RevokeAccessToken(ctx, gomock.Any()). -					Return(nil). -					Times(1) -				revocation. -					EXPECT(). -					RevokeRefreshTokenMaybeGracePeriod(ctx, gomock.Any(), gomock.Any()). -					Return(errors.New("Whoops, a nasty database error occurred!")). -					Times(1) -				transactional. -					EXPECT(). -					Rollback(ctx). -					Return(nil). -					Times(1) -			}, -			err:      fosite.ErrServerError, -			expected: "The authorization server encountered an unexpected condition that prevented it from fulfilling the request. Whoops, a nasty database error occurred!", -		}, -		{ -			name: "ShouldErrorErrInvalidRequestWhenRevokeRefreshTokenMaybeGracePeriodErrSerializationFailure", -			setup: func(ctx context.Context, request *fosite.AccessRequest, transactional *mocks.MockTransactional, revocation *mocks.MockTokenRevocationStorage) { -				request.GrantTypes = fosite.Arguments{oidc.GrantTypeRefreshToken} -				transactional. -					EXPECT(). -					BeginTX(ctx). -					Return(ctx, nil). -					Times(1) -				revocation. -					EXPECT(). -					GetRefreshTokenSession(ctx, gomock.Any(), nil). -					Return(request, nil). -					Times(1) -				revocation. -					EXPECT(). -					RevokeAccessToken(ctx, gomock.Any()). -					Return(nil). -					Times(1) -				revocation. -					EXPECT(). -					RevokeRefreshTokenMaybeGracePeriod(ctx, gomock.Any(), gomock.Any()). -					Return(fosite.ErrSerializationFailure). -					Times(1) -				transactional. -					EXPECT(). -					Rollback(ctx). -					Return(nil). -					Times(1) -			}, -			err:      fosite.ErrInvalidRequest, -			expected: "The request is missing a required parameter, includes an invalid parameter value, includes a parameter more than once, or is otherwise malformed. Failed to refresh token because of multiple concurrent requests using the same token which is not allowed. The request could not be completed due to concurrent access", -		}, -		{ -			name: "ShouldErrorErrInvalidRequestWhenCreateAccessTokenSessionErrSerializationFailure", -			setup: func(ctx context.Context, request *fosite.AccessRequest, transactional *mocks.MockTransactional, revocation *mocks.MockTokenRevocationStorage) { -				request.GrantTypes = fosite.Arguments{oidc.GrantTypeRefreshToken} -				transactional. -					EXPECT(). -					BeginTX(ctx). -					Return(ctx, nil). -					Times(1) -				revocation. -					EXPECT(). -					GetRefreshTokenSession(ctx, gomock.Any(), nil). -					Return(request, nil). -					Times(1) -				revocation. -					EXPECT(). -					RevokeAccessToken(ctx, gomock.Any()). -					Return(nil). -					Times(1) -				revocation. -					EXPECT(). -					RevokeRefreshTokenMaybeGracePeriod(ctx, gomock.Any(), gomock.Any()). -					Return(nil). -					Times(1) -				revocation. -					EXPECT(). -					CreateAccessTokenSession(ctx, gomock.Any(), gomock.Any()). -					Return(fosite.ErrSerializationFailure). -					Times(1) -				transactional. -					EXPECT(). -					Rollback(ctx). -					Return(nil). -					Times(1) -			}, -			err:      fosite.ErrInvalidRequest, -			expected: "The request is missing a required parameter, includes an invalid parameter value, includes a parameter more than once, or is otherwise malformed. Failed to refresh token because of multiple concurrent requests using the same token which is not allowed. The request could not be completed due to concurrent access", -		}, -		{ -			name: "ShouldSuccessfullyRollbackTxWhenErrorsFromCreateAccessTokenSession", -			setup: func(ctx context.Context, request *fosite.AccessRequest, transactional *mocks.MockTransactional, revocation *mocks.MockTokenRevocationStorage) { -				request.GrantTypes = fosite.Arguments{oidc.GrantTypeRefreshToken} -				transactional. -					EXPECT(). -					BeginTX(ctx). -					Return(ctx, nil). -					Times(1) -				revocation. -					EXPECT(). -					GetRefreshTokenSession(ctx, gomock.Any(), nil). -					Return(request, nil). -					Times(1) -				revocation. -					EXPECT(). -					RevokeAccessToken(ctx, gomock.Any()). -					Return(nil). -					Times(1) -				revocation. -					EXPECT(). -					RevokeRefreshTokenMaybeGracePeriod(ctx, gomock.Any(), gomock.Any()). -					Return(nil). -					Times(1) -				revocation. -					EXPECT(). -					CreateAccessTokenSession(ctx, gomock.Any(), gomock.Any()). -					Return(errors.New("Whoops, a nasty database error occurred!")). -					Times(1) -				transactional. -					EXPECT(). -					Rollback(ctx). -					Return(nil). -					Times(1) -			}, -			err:      fosite.ErrServerError, -			expected: "The authorization server encountered an unexpected condition that prevented it from fulfilling the request. Whoops, a nasty database error occurred!", -		}, -		{ -			name: "ShouldSuccessfullyRollbackTxWhenErrorsFromCreateRefreshTokenSession", -			setup: func(ctx context.Context, request *fosite.AccessRequest, transactional *mocks.MockTransactional, revocation *mocks.MockTokenRevocationStorage) { -				request.GrantTypes = fosite.Arguments{oidc.GrantTypeRefreshToken} -				transactional. -					EXPECT(). -					BeginTX(ctx). -					Return(ctx, nil). -					Times(1) -				revocation. -					EXPECT(). -					GetRefreshTokenSession(ctx, gomock.Any(), nil). -					Return(request, nil). -					Times(1) -				revocation. -					EXPECT(). -					RevokeAccessToken(ctx, gomock.Any()). -					Return(nil). -					Times(1) -				revocation. -					EXPECT(). -					RevokeRefreshTokenMaybeGracePeriod(ctx, gomock.Any(), gomock.Any()). -					Return(nil). -					Times(1) -				revocation. -					EXPECT(). -					CreateAccessTokenSession(ctx, gomock.Any(), gomock.Any()). -					Return(nil). -					Times(1) -				revocation. -					EXPECT(). -					CreateRefreshTokenSession(ctx, gomock.Any(), gomock.Any()). -					Return(errors.New("Whoops, a nasty database error occurred!")). -					Times(1) -				transactional. -					EXPECT(). -					Rollback(ctx). -					Return(nil). -					Times(1) -			}, -			err:      fosite.ErrServerError, -			expected: "The authorization server encountered an unexpected condition that prevented it from fulfilling the request. Whoops, a nasty database error occurred!", -		}, -		{ -			name: "ShouldErrorErrInvalidRequestWhenCreateRefreshTokenSessionErrSerializationFailure", -			setup: func(ctx context.Context, request *fosite.AccessRequest, transactional *mocks.MockTransactional, revocation *mocks.MockTokenRevocationStorage) { -				request.GrantTypes = fosite.Arguments{oidc.GrantTypeRefreshToken} -				transactional. -					EXPECT(). -					BeginTX(ctx). -					Return(ctx, nil). -					Times(1) -				revocation. -					EXPECT(). -					GetRefreshTokenSession(ctx, gomock.Any(), nil). -					Return(request, nil). -					Times(1) -				revocation. -					EXPECT(). -					RevokeAccessToken(ctx, gomock.Any()). -					Return(nil). -					Times(1) -				revocation. -					EXPECT(). -					RevokeRefreshTokenMaybeGracePeriod(ctx, gomock.Any(), gomock.Any()). -					Return(nil). -					Times(1) -				revocation. -					EXPECT(). -					CreateAccessTokenSession(ctx, gomock.Any(), gomock.Any()). -					Return(nil). -					Times(1) -				revocation. -					EXPECT(). -					CreateRefreshTokenSession(ctx, gomock.Any(), gomock.Any()). -					Return(fosite.ErrSerializationFailure). -					Times(1) -				transactional. -					EXPECT(). -					Rollback(ctx). -					Return(nil). -					Times(1) -			}, -			err:      fosite.ErrInvalidRequest, -			expected: "The request is missing a required parameter, includes an invalid parameter value, includes a parameter more than once, or is otherwise malformed. Failed to refresh token because of multiple concurrent requests using the same token which is not allowed. The request could not be completed due to concurrent access", -		}, -		{ -			name: "ShouldErrorErrServerErrorWhenBeginTxFailure", -			setup: func(ctx context.Context, request *fosite.AccessRequest, transactional *mocks.MockTransactional, revocation *mocks.MockTokenRevocationStorage) { -				request.GrantTypes = fosite.Arguments{oidc.GrantTypeRefreshToken} -				transactional. -					EXPECT(). -					BeginTX(ctx). -					Return(nil, errors.New("Could not create transaction!")). -					Times(1) -			}, -			err:      fosite.ErrServerError, -			expected: "The authorization server encountered an unexpected condition that prevented it from fulfilling the request. Could not create transaction!", -		}, -		{ -			name: "ShouldErrorErrServerErrorWhenRollbackTxFailure", -			setup: func(ctx context.Context, request *fosite.AccessRequest, transactional *mocks.MockTransactional, revocation *mocks.MockTokenRevocationStorage) { -				request.GrantTypes = fosite.Arguments{oidc.GrantTypeRefreshToken} -				transactional. -					EXPECT(). -					BeginTX(ctx). -					Return(ctx, nil). -					Times(1) -				revocation. -					EXPECT(). -					GetRefreshTokenSession(ctx, gomock.Any(), nil). -					Return(nil, fosite.ErrNotFound). -					Times(1) -				transactional. -					EXPECT(). -					Rollback(ctx). -					Return(errors.New("Could not rollback transaction!")). -					Times(1) -			}, -			err:      fosite.ErrServerError, -			expected: "The authorization server encountered an unexpected condition that prevented it from fulfilling the request. error: invalid_request; rollback error: Could not rollback transaction!", -		}, -		{ -			name: "ShouldErrorErrServerErrorWhenCommitTxFailure", -			setup: func(ctx context.Context, request *fosite.AccessRequest, transactional *mocks.MockTransactional, revocation *mocks.MockTokenRevocationStorage) { -				request.GrantTypes = fosite.Arguments{oidc.GrantTypeRefreshToken} -				transactional. -					EXPECT(). -					BeginTX(ctx). -					Return(ctx, nil). -					Times(1) -				revocation. -					EXPECT(). -					GetRefreshTokenSession(ctx, gomock.Any(), nil). -					Return(request, nil). -					Times(1) -				revocation. -					EXPECT(). -					RevokeAccessToken(ctx, gomock.Any()). -					Return(nil). -					Times(1) -				revocation. -					EXPECT(). -					RevokeRefreshTokenMaybeGracePeriod(ctx, gomock.Any(), gomock.Any()). -					Return(nil). -					Times(1) -				revocation. -					EXPECT(). -					CreateAccessTokenSession(ctx, gomock.Any(), gomock.Any()). -					Return(nil). -					Times(1) -				revocation. -					EXPECT(). -					CreateRefreshTokenSession(ctx, gomock.Any(), gomock.Any()). -					Return(nil). -					Times(1) -				transactional. -					EXPECT(). -					Commit(ctx). -					Return(errors.New("Could not commit transaction!")). -					Times(1) -				transactional. -					EXPECT(). -					Rollback(ctx). -					Return(nil). -					Times(1) -			}, -			err:      fosite.ErrServerError, -			expected: "The authorization server encountered an unexpected condition that prevented it from fulfilling the request. Could not commit transaction!", -		}, -		{ -			name: "ShouldErrorErrInvalidRequestWhenCommitTxErrSerializationFailure", -			setup: func(ctx context.Context, request *fosite.AccessRequest, transactional *mocks.MockTransactional, revocation *mocks.MockTokenRevocationStorage) { -				request.GrantTypes = fosite.Arguments{oidc.GrantTypeRefreshToken} -				transactional. -					EXPECT(). -					BeginTX(ctx). -					Return(ctx, nil). -					Times(1) -				revocation. -					EXPECT(). -					GetRefreshTokenSession(ctx, gomock.Any(), nil). -					Return(request, nil). -					Times(1) -				revocation. -					EXPECT(). -					RevokeAccessToken(ctx, gomock.Any()). -					Return(nil). -					Times(1) -				revocation. -					EXPECT(). -					RevokeRefreshTokenMaybeGracePeriod(ctx, gomock.Any(), gomock.Any()). -					Return(nil). -					Times(1) -				revocation. -					EXPECT(). -					CreateAccessTokenSession(ctx, gomock.Any(), gomock.Any()). -					Return(nil). -					Times(1) -				revocation. -					EXPECT(). -					CreateRefreshTokenSession(ctx, gomock.Any(), gomock.Any()). -					Return(nil). -					Times(1) -				transactional. -					EXPECT(). -					Commit(ctx). -					Return(fosite.ErrSerializationFailure). -					Times(1) -				transactional. -					EXPECT(). -					Rollback(ctx). -					Return(nil). -					Times(1) -			}, -			err:      fosite.ErrInvalidRequest, -			expected: "The request is missing a required parameter, includes an invalid parameter value, includes a parameter more than once, or is otherwise malformed. Failed to refresh token because of multiple concurrent requests using the same token which is not allowed. The request could not be completed due to concurrent access", -		}, -	} - -	for _, tc := range testCases { -		t.Run(tc.name, func(t *testing.T) { -			ctrl := gomock.NewController(t) -			defer ctrl.Finish() - -			transactional := mocks.NewMockTransactional(ctrl) -			revocation := mocks.NewMockTokenRevocationStorage(ctrl) - -			ctx := context.Background() - -			request := fosite.NewAccessRequest(&fosite.DefaultSession{}) - -			tc.setup(ctx, request, transactional, revocation) - -			strategy := &oauth2.HMACSHAStrategy{ -				Enigma: &hmac.HMACStrategy{Config: &fosite.Config{GlobalSecret: []byte("foobarfoobarfoobarfoobarfoobarfoobarfoobarfoobar")}}, -				Config: &fosite.Config{ -					AccessTokenLifespan:   time.Hour * 24, -					AuthorizeCodeLifespan: time.Hour * 24, -				}, -			} - -			handler := oidc.RefreshTokenGrantHandler{ -				// Notice how we are passing in a store that has support for transactions! -				TokenRevocationStorage: store{ -					transactional, -					revocation, -				}, -				AccessTokenStrategy:  strategy, -				RefreshTokenStrategy: strategy, -				Config: &fosite.Config{ -					AccessTokenLifespan:      time.Hour, -					ScopeStrategy:            fosite.HierarchicScopeStrategy, -					AudienceMatchingStrategy: fosite.DefaultAudienceMatchingStrategy, -				}, -			} - -			response := fosite.NewAccessResponse() - -			err := handler.PopulateTokenEndpointResponse(ctx, request, response) - -			if tc.err != nil { -				assert.EqualError(t, err, tc.err.Error()) -				assert.EqualError(t, oidc.ErrorToDebugRFC6749Error(err), tc.expected) -			} else { -				assert.NoError(t, oidc.ErrorToDebugRFC6749Error(err)) -			} - -			if tc.texpected != nil { -				tc.texpected(t, request, response) -			} -		}) -	} -} - -var TestLifespans = fosite.ClientLifespanConfig{ -	AuthorizationCodeGrantAccessTokenLifespan:  ptr(31 * time.Hour), -	AuthorizationCodeGrantIDTokenLifespan:      ptr(32 * time.Hour), -	AuthorizationCodeGrantRefreshTokenLifespan: ptr(33 * time.Hour), -	ClientCredentialsGrantAccessTokenLifespan:  ptr(34 * time.Hour), -	ImplicitGrantAccessTokenLifespan:           ptr(35 * time.Hour), -	ImplicitGrantIDTokenLifespan:               ptr(36 * time.Hour), -	JwtBearerGrantAccessTokenLifespan:          ptr(37 * time.Hour), -	PasswordGrantAccessTokenLifespan:           ptr(38 * time.Hour), -	PasswordGrantRefreshTokenLifespan:          ptr(39 * time.Hour), -	RefreshTokenGrantIDTokenLifespan:           ptr(40 * time.Hour), -	RefreshTokenGrantAccessTokenLifespan:       ptr(41 * time.Hour), -	RefreshTokenGrantRefreshTokenLifespan:      ptr(42 * time.Hour), -} - -func ptr(d time.Duration) *time.Duration { -	return &d -} diff --git a/internal/oidc/handler_introspection.go b/internal/oidc/handler_introspection.go deleted file mode 100644 index b5fc9ae10..000000000 --- a/internal/oidc/handler_introspection.go +++ /dev/null @@ -1,146 +0,0 @@ -package oidc - -import ( -	"context" -	"net/http" -	"strings" - -	"github.com/ory/fosite" -	"github.com/ory/x/errorsx" -	"github.com/valyala/fasthttp" -	"golang.org/x/text/language" -) - -// NewIntrospectionRequest shadows the fosite version of this function. -func (p *OpenIDConnectProvider) NewIntrospectionRequest(ctx context.Context, r *http.Request, session fosite.Session) (_ fosite.IntrospectionResponder, err error) { -	ctx = context.WithValue(ctx, fosite.RequestContextKey, r) - -	if r.Method != fasthttp.MethodPost { -		return &IntrospectionResponse{Active: false}, errorsx.WithStack(fosite.ErrInvalidRequest.WithHintf("HTTP method is '%s' but expected 'POST'.", r.Method)) -	} else if err := r.ParseMultipartForm(1 << 20); err != nil && err != http.ErrNotMultipart { -		return &IntrospectionResponse{Active: false}, errorsx.WithStack(fosite.ErrInvalidRequest.WithHint("Unable to parse HTTP body, make sure to send a properly formatted form request body.").WithWrap(err).WithDebug(ErrorToDebugRFC6749Error(err).Error())) -	} else if len(r.PostForm) == 0 { -		return &IntrospectionResponse{Active: false}, errorsx.WithStack(fosite.ErrInvalidRequest.WithHint("The POST body can not be empty.")) -	} - -	token := r.PostForm.Get(FormParameterToken) -	tokenTypeHint := r.PostForm.Get(FormParameterTokenTypeHint) - -	var client fosite.Client - -	if client, err = p.handleNewIntrospectionRequestClientAuthentication(ctx, r, session, token); err != nil { -		return &IntrospectionResponse{Active: false}, err -	} - -	var ( -		ar  fosite.AccessRequester -		use fosite.TokenUse -	) - -	if use, ar, err = p.IntrospectToken(ctx, token, fosite.TokenUse(tokenTypeHint), session, fosite.RemoveEmpty(strings.Split(r.PostForm.Get(FormParameterScope), " "))...); err != nil { -		return &IntrospectionResponse{Active: false}, errorsx.WithStack(fosite.ErrInactiveToken.WithHint("An introspection strategy indicated that the token is inactive.").WithWrap(err).WithDebug(ErrorToDebugRFC6749Error(err).Error())) -	} - -	accessTokenType := "" - -	if use == fosite.AccessToken { -		accessTokenType = fosite.BearerAccessToken -	} - -	return &IntrospectionResponse{ -		Client:          client, -		Active:          true, -		AccessRequester: ar, -		TokenUse:        use, -		AccessTokenType: accessTokenType, -	}, nil -} - -func (p *OpenIDConnectProvider) handleNewIntrospectionRequestClientAuthentication(ctx context.Context, r *http.Request, session fosite.Session, token string) (c fosite.Client, err error) { -	if clientToken := fosite.AccessTokenFromRequest(r); clientToken != "" { -		if token == clientToken { -			return nil, errorsx.WithStack(fosite.ErrRequestUnauthorized.WithHint("Bearer and introspection token are identical.")) -		} - -		var ( -			ar  fosite.AccessRequester -			use fosite.TokenUse -		) - -		if use, ar, err = p.IntrospectToken(ctx, clientToken, fosite.AccessToken, session.Clone()); err != nil { -			return nil, errorsx.WithStack(fosite.ErrRequestUnauthorized.WithHint("HTTP Authorization header missing, malformed, or credentials used are invalid.").WithWrap(err).WithDebug(ErrorToDebugRFC6749Error(err).Error())) -		} else if use != "" && use != fosite.AccessToken { -			return nil, errorsx.WithStack(fosite.ErrRequestUnauthorized.WithHintf("HTTP Authorization header did not provide a token of type 'access_token', got type '%s'.", use)) -		} - -		c = ar.GetClient() -	} else { -		var ( -			clientID, clientSecret string -			ok                     bool -		) - -		switch clientID, clientSecret, ok, err = clientCredentialsFromBasicAuth(r.Header); { -		case err != nil: -			return nil, errorsx.WithStack(fosite.ErrRequestUnauthorized.WithWrap(err).WithHint("HTTP Authorization header malformed.")) -		case !ok: -			return nil, errorsx.WithStack(fosite.ErrRequestUnauthorized.WithHint("HTTP Authorization header missing.")) -		} - -		var client Client - -		if client, err = p.Store.GetFullClient(ctx, clientID); err != nil { -			return nil, errorsx.WithStack(fosite.ErrRequestUnauthorized.WithHint("Unable to find OAuth 2.0 Client from HTTP basic authorization header.").WithWrap(err).WithDebug(ErrorToDebugRFC6749Error(err).Error())) -		} - -		// Enforce client authentication. -		if err = p.checkClientSecret(ctx, client, []byte(clientSecret)); err != nil { -			return nil, errorsx.WithStack(fosite.ErrRequestUnauthorized.WithHint("OAuth 2.0 Client credentials are invalid.")) -		} - -		c = client -	} - -	return c, nil -} - -// IntrospectionResponse is a copy of the fosite.IntrospectionResponse which also includes a fosite.Client to satisfy -// the ClientRequesterResponder interface so we can perform the JWT Response for OAuth 2.0 Token Introspection with the -// correct audience. -type IntrospectionResponse struct { -	Client          fosite.Client          `json:"-"` -	Active          bool                   `json:"active"` -	AccessRequester fosite.AccessRequester `json:"extra"` -	TokenUse        fosite.TokenUse        `json:"token_use,omitempty"` -	AccessTokenType string                 `json:"token_type,omitempty"` -	Lang            language.Tag           `json:"-"` -} - -// IsActive returns if the introspected token is active. -func (r *IntrospectionResponse) IsActive() bool { -	return r.Active -} - -// GetClient returns the client related to the introspected token. -func (r *IntrospectionResponse) GetClient() fosite.Client { -	return r.Client -} - -// GetAccessRequester returns the attached fosite.AccessRequester for the introspected token. -func (r *IntrospectionResponse) GetAccessRequester() fosite.AccessRequester { -	return r.AccessRequester -} - -// GetTokenUse returns the fosite.TokenUse for the introspected token. -func (r *IntrospectionResponse) GetTokenUse() fosite.TokenUse { -	return r.TokenUse -} - -// GetAccessTokenType returns the type for the introspected token. -func (r *IntrospectionResponse) GetAccessTokenType() string { -	return r.AccessTokenType -} - -var ( -	_ fosite.IntrospectionResponder = (*IntrospectionResponse)(nil) -) diff --git a/internal/oidc/handler_introspection_test.go b/internal/oidc/handler_introspection_test.go deleted file mode 100644 index 1a2eb8bc7..000000000 --- a/internal/oidc/handler_introspection_test.go +++ /dev/null @@ -1,397 +0,0 @@ -package oidc_test - -import ( -	"bytes" -	"context" -	"io" -	"net/http" -	"net/url" -	"testing" - -	"github.com/ory/fosite" -	"github.com/stretchr/testify/assert" -	"github.com/stretchr/testify/require" -	"github.com/valyala/fasthttp" -	"go.uber.org/mock/gomock" - -	"github.com/authelia/authelia/v4/internal/configuration/schema" -	"github.com/authelia/authelia/v4/internal/mocks" -	"github.com/authelia/authelia/v4/internal/oidc" -	"github.com/authelia/authelia/v4/internal/templates" -) - -func TestOpenIDConnectProvider_NewIntrospectionRequest(t *testing.T) { -	testCases := []struct { -		name       string -		clients    []schema.IdentityProvidersOpenIDConnectClient -		setup      func(ctx gomock.Matcher, provider *oidc.OpenIDConnectProvider, mock *mocks.MockTokenIntrospector) -		req        *http.Request -		expected   fosite.TokenUse -		expectedtt string -		err        string -	}{ -		{ -			"ShouldNotIntrospectTokenWithoutCredentials", -			nil, -			nil, -			&http.Request{ -				Method: fasthttp.MethodPost, -				Header: http.Header{}, -				PostForm: url.Values{ -					oidc.FormParameterToken: []string{"arbitrary-introspection-token"}, -				}, -			}, -			fosite.AccessToken, -			fosite.BearerAccessToken, -			"The request could not be authorized. HTTP Authorization header missing.", -		}, -		{ -			"ShouldIntrospectAccessTokenWithBearerCredentials", -			nil, -			func(ctx gomock.Matcher, provider *oidc.OpenIDConnectProvider, mock *mocks.MockTokenIntrospector) { -				mock.EXPECT().IntrospectToken(ctx, "arbitrary-bearer-token", gomock.Any(), gomock.Any(), gomock.Any()).Return(fosite.AccessToken, nil) -				mock.EXPECT().IntrospectToken(ctx, "arbitrary-introspection-token", gomock.Any(), gomock.Any(), gomock.Any()).Return(fosite.AccessToken, nil) -			}, -			&http.Request{ -				Method: fasthttp.MethodPost, -				Header: http.Header{ -					fasthttp.HeaderAuthorization: []string{"Bearer arbitrary-bearer-token"}, -				}, -				PostForm: url.Values{ -					oidc.FormParameterToken: []string{"arbitrary-introspection-token"}, -				}, -			}, -			fosite.AccessToken, -			fosite.BearerAccessToken, -			"", -		}, -		{ -			"ShouldIntrospectAccessTokenWithBasicCredentials", -			[]schema.IdentityProvidersOpenIDConnectClient{ -				{ -					ID:     "client_id", -					Secret: MustDecodeSecret("$plaintext$client_secret"), -				}, -			}, -			func(ctx gomock.Matcher, provider *oidc.OpenIDConnectProvider, mock *mocks.MockTokenIntrospector) { -				mock.EXPECT().IntrospectToken(ctx, "arbitrary-introspection-token", gomock.Any(), gomock.Any(), gomock.Any()).Return(fosite.AccessToken, nil) -			}, -			&http.Request{ -				Method: fasthttp.MethodPost, -				Header: http.Header{ -					fasthttp.HeaderAuthorization: []string{"Basic Y2xpZW50X2lkOmNsaWVudF9zZWNyZXQ="}, -				}, -				PostForm: url.Values{ -					oidc.FormParameterToken: []string{"arbitrary-introspection-token"}, -				}, -			}, -			fosite.AccessToken, -			fosite.BearerAccessToken, -			"", -		}, -		{ -			"ShouldNotIntrospectAccessTokenWithBasicCredentialsInvalidClientSecret", -			[]schema.IdentityProvidersOpenIDConnectClient{ -				{ -					ID:     "client_id", -					Secret: MustDecodeSecret("$plaintext$client_secret2"), -				}, -			}, -			nil, -			&http.Request{ -				Method: fasthttp.MethodPost, -				Header: http.Header{ -					fasthttp.HeaderAuthorization: []string{"Basic Y2xpZW50X2lkOmNsaWVudF9zZWNyZXQ="}, -				}, -				PostForm: url.Values{ -					oidc.FormParameterToken: []string{"arbitrary-introspection-token"}, -				}, -			}, -			fosite.AccessToken, -			fosite.BearerAccessToken, -			"The request could not be authorized. OAuth 2.0 Client credentials are invalid.", -		}, -		{ -			"ShouldNotIntrospectAccessTokenWithBasicCredentialsInvalidClientID", -			[]schema.IdentityProvidersOpenIDConnectClient{ -				{ -					ID:     "client_id2", -					Secret: MustDecodeSecret("$plaintext$client_secret"), -				}, -			}, -			nil, -			&http.Request{ -				Method: fasthttp.MethodPost, -				Header: http.Header{ -					fasthttp.HeaderAuthorization: []string{"Basic Y2xpZW50X2lkOmNsaWVudF9zZWNyZXQ="}, -				}, -				PostForm: url.Values{ -					oidc.FormParameterToken: []string{"arbitrary-introspection-token"}, -				}, -			}, -			fosite.AccessToken, -			fosite.BearerAccessToken, -			"The request could not be authorized. Unable to find OAuth 2.0 Client from HTTP basic authorization header. Client authentication failed (e.g., unknown client, no client authentication included, or unsupported authentication method). Client with id 'client_id' does not appear to be a registered client.", -		}, -		{ -			"ShouldNotIntrospectAccessTokenWithBasicCredentialsInvalidAuthorizationHeader", -			[]schema.IdentityProvidersOpenIDConnectClient{ -				{ -					ID:     "client_id2", -					Secret: MustDecodeSecret("$plaintext$client_secret"), -				}, -			}, -			nil, -			&http.Request{ -				Method: fasthttp.MethodPost, -				Header: http.Header{ -					fasthttp.HeaderAuthorization: []string{"x"}, -				}, -				PostForm: url.Values{ -					oidc.FormParameterToken: []string{"arbitrary-introspection-token"}, -				}, -			}, -			fosite.AccessToken, -			fosite.BearerAccessToken, -			"The request could not be authorized. HTTP Authorization header malformed.", -		}, -		{ -			"ShouldNotIntrospectAccessTokenWithBearerCredentialsHasError", -			nil, -			func(ctx gomock.Matcher, provider *oidc.OpenIDConnectProvider, mock *mocks.MockTokenIntrospector) { -				mock.EXPECT().IntrospectToken(ctx, "arbitrary-bearer-token", gomock.Any(), gomock.Any(), gomock.Any()).Return(fosite.TokenType(""), fosite.ErrNotFound) -			}, -			&http.Request{ -				Method: fasthttp.MethodPost, -				Header: http.Header{ -					fasthttp.HeaderAuthorization: []string{"Bearer arbitrary-bearer-token"}, -				}, -				PostForm: url.Values{ -					oidc.FormParameterToken: []string{"arbitrary-introspection-token"}, -				}, -			}, -			fosite.AccessToken, -			fosite.BearerAccessToken, -			"The request could not be authorized. HTTP Authorization header missing, malformed, or credentials used are invalid. Could not find the requested resource(s).", -		}, -		{ -			"ShouldIntrospectRefreshToken", -			nil, -			func(ctx gomock.Matcher, provider *oidc.OpenIDConnectProvider, mock *mocks.MockTokenIntrospector) { -				mock.EXPECT().IntrospectToken(ctx, "arbitrary-bearer-token", gomock.Any(), gomock.Any(), gomock.Any()).Return(fosite.AccessToken, nil) -				mock.EXPECT().IntrospectToken(ctx, "arbitrary-introspection-token", gomock.Any(), gomock.Any(), gomock.Any()).Return(fosite.RefreshToken, nil) -			}, -			&http.Request{ -				Method: fasthttp.MethodPost, -				Header: http.Header{ -					fasthttp.HeaderAuthorization: []string{"Bearer arbitrary-bearer-token"}, -				}, -				PostForm: url.Values{ -					oidc.FormParameterToken: []string{"arbitrary-introspection-token"}, -				}, -			}, -			fosite.RefreshToken, -			"", -			"", -		}, -		{ -			"ShouldNotIntrospectWhenBearerRefreshToken", -			nil, -			func(ctx gomock.Matcher, provider *oidc.OpenIDConnectProvider, mock *mocks.MockTokenIntrospector) { -				mock.EXPECT().IntrospectToken(ctx, "arbitrary-bearer-token", gomock.Any(), gomock.Any(), gomock.Any()).Return(fosite.RefreshToken, nil) -			}, -			&http.Request{ -				Method: fasthttp.MethodPost, -				Header: http.Header{ -					fasthttp.HeaderAuthorization: []string{"Bearer arbitrary-bearer-token"}, -				}, -				PostForm: url.Values{ -					oidc.FormParameterToken: []string{"arbitrary-introspection-token"}, -				}, -			}, -			fosite.RefreshToken, -			"", -			"The request could not be authorized. HTTP Authorization header did not provide a token of type 'access_token', got type 'refresh_token'.", -		}, -		{ -			"ShouldNotIntrospectWhenIdenticalTokens", -			nil, -			nil, -			&http.Request{ -				Method: fasthttp.MethodPost, -				Header: http.Header{ -					fasthttp.HeaderAuthorization: []string{"Bearer arbitrary-bearer-token"}, -				}, -				PostForm: url.Values{ -					oidc.FormParameterToken: []string{"arbitrary-bearer-token"}, -				}, -			}, -			fosite.RefreshToken, -			"", -			"The request could not be authorized. Bearer and introspection token are identical.", -		}, -		{ -			"ShouldNotIntrospectInvalidMethodVerb", -			nil, -			nil, -			&http.Request{ -				Method: fasthttp.MethodGet, -				Header: http.Header{ -					fasthttp.HeaderAuthorization: []string{"Bearer arbitrary-bearer-token"}, -				}, -				PostForm: url.Values{ -					oidc.FormParameterToken: []string{"arbitrary-bearer-token"}, -				}, -			}, -			fosite.RefreshToken, -			"", -			"The request is missing a required parameter, includes an invalid parameter value, includes a parameter more than once, or is otherwise malformed. HTTP method is 'GET' but expected 'POST'.", -		}, -		{ -			"ShouldNotIntrospectEmptyPost", -			nil, -			nil, -			&http.Request{ -				Method: fasthttp.MethodPost, -				Header: http.Header{ -					fasthttp.HeaderAuthorization: []string{"Bearer arbitrary-bearer-token"}, -				}, -				PostForm: nil, -			}, -			fosite.RefreshToken, -			"", -			"The request is missing a required parameter, includes an invalid parameter value, includes a parameter more than once, or is otherwise malformed. The POST body can not be empty.", -		}, -		{ -			"ShouldNotIntrospectCorruptMultiPartFormDataPost", -			nil, -			nil, -			&http.Request{ -				Method: fasthttp.MethodPost, -				Header: http.Header{ -					fasthttp.HeaderAuthorization: []string{"Bearer arbitrary-bearer-token"}, -					fasthttp.HeaderContentType:   []string{"multipart/form-data"}, -					fasthttp.HeaderContentLength: []string{"3"}, -				}, -				Body:     io.NopCloser(bytes.NewReader([]byte{0x01, 0x00, 0x02})), -				PostForm: nil, -			}, -			fosite.RefreshToken, -			"", -			"The request is missing a required parameter, includes an invalid parameter value, includes a parameter more than once, or is otherwise malformed. Unable to parse HTTP body, make sure to send a properly formatted form request body. no multipart boundary param in Content-Type", -		}, -		{ -			"ShouldReturnIntrospectionTokenError", -			nil, -			func(ctx gomock.Matcher, provider *oidc.OpenIDConnectProvider, mock *mocks.MockTokenIntrospector) { -				mock.EXPECT().IntrospectToken(ctx, "arbitrary-bearer-token", gomock.Any(), gomock.Any(), gomock.Any()).Return(fosite.AccessToken, nil) -				mock.EXPECT().IntrospectToken(ctx, "arbitrary-introspection-token", gomock.Any(), gomock.Any(), gomock.Any()).Return(fosite.TokenType(""), fosite.ErrNotFound) -			}, -			&http.Request{ -				Method: fasthttp.MethodPost, -				Header: http.Header{ -					fasthttp.HeaderAuthorization: []string{"Bearer arbitrary-bearer-token"}, -				}, -				PostForm: url.Values{ -					oidc.FormParameterToken: []string{"arbitrary-introspection-token"}, -				}, -			}, -			fosite.AccessToken, -			fosite.BearerAccessToken, -			"Token is inactive because it is malformed, expired or otherwise invalid. An introspection strategy indicated that the token is inactive. Could not find the requested resource(s).", -		}, -	} - -	tp, err := templates.New(templates.Config{}) - -	require.NoError(t, err) - -	for _, tc := range testCases { -		t.Run(tc.name, func(t *testing.T) { -			ctrl := gomock.NewController(t) - -			defer ctrl.Finish() - -			mock := mocks.NewMockTokenIntrospector(ctrl) - -			store := mocks.NewMockStorage(ctrl) - -			ctx := gomock.AssignableToTypeOf(context.WithValue(context.TODO(), fosite.ContextKey("test"), nil)) - -			provider := oidc.NewOpenIDConnectProvider(&schema.IdentityProvidersOpenIDConnect{HMACSecret: badhmac, Clients: tc.clients}, store, tp) - -			provider.Config.Handlers.TokenIntrospection = fosite.TokenIntrospectionHandlers{mock} - -			if tc.setup != nil { -				tc.setup(ctx, provider, mock) -			} - -			responder, err := provider.NewIntrospectionRequest(context.TODO(), tc.req, oidc.NewSession()) - -			if len(tc.err) == 0 { -				assert.NoError(t, oidc.ErrorToDebugRFC6749Error(err)) -				require.NotNil(t, responder) - -				assert.Equal(t, tc.expected, responder.GetTokenUse()) -				assert.Equal(t, tc.expectedtt, responder.GetAccessTokenType()) -			} else { -				assert.EqualError(t, oidc.ErrorToDebugRFC6749Error(err), tc.err) -			} -		}) -	} -} - -func TestIntrospectionResponse(t *testing.T) { -	testCases := []struct { -		name            string -		have            *oidc.IntrospectionResponse -		clientID        string -		active          bool -		accessRequestID string -	}{ -		{ -			"ShouldTestActiveToken", -			&oidc.IntrospectionResponse{ -				Client: &oidc.RegisteredClient{ID: "client1"}, -				Active: true, -				AccessRequester: &fosite.AccessRequest{ -					Request: fosite.Request{ -						ID: abc, -					}, -				}, -			}, -			"client1", -			true, -			abc, -		}, -		{ -			"ShouldReturnNilBadResponse", -			&oidc.IntrospectionResponse{ -				Client:          nil, -				Active:          false, -				AccessRequester: nil, -			}, -			"", -			false, -			"", -		}, -	} - -	for _, tc := range testCases { -		t.Run(tc.name, func(t *testing.T) { -			if tc.clientID == "" { -				assert.Nil(t, tc.have.GetClient()) -			} else { -				assert.Equal(t, tc.clientID, tc.have.GetClient().GetID()) -			} - -			if tc.accessRequestID == "" { -				assert.Nil(t, tc.have.GetAccessRequester()) -			} else { -				assert.Equal(t, tc.accessRequestID, tc.have.GetAccessRequester().GetID()) -			} - -			assert.Equal(t, tc.active, tc.have.IsActive()) -		}) -	} -} diff --git a/internal/oidc/handlers.go b/internal/oidc/handlers.go deleted file mode 100644 index d5cf814b5..000000000 --- a/internal/oidc/handlers.go +++ /dev/null @@ -1,33 +0,0 @@ -package oidc - -import ( -	"context" - -	"github.com/ory/fosite" -) - -// AuthorizationServerIssuerIdentificationHandler handles RFC9207: OAuth 2.0 Authorization Server Issuer Identification -// response parameters as per the https://datatracker.ietf.org/doc/html/rfc9207 specification document. -type AuthorizationServerIssuerIdentificationHandler struct { -	Config interface { -		AuthorizationServerIssuerIdentificationProvider -	} -} - -// HandleAuthorizeEndpointRequest implements the fosite.AuthorizeEndpointHandler for RFC9207. -func (h *AuthorizationServerIssuerIdentificationHandler) HandleAuthorizeEndpointRequest(ctx context.Context, requester fosite.AuthorizeRequester, responder fosite.AuthorizeResponder) (err error) { -	switch requester.GetResponseMode() { -	case ResponseModeJWT, ResponseModeFormPostJWT, ResponseModeQueryJWT, ResponseModeFragmentJWT: -		break -	default: -		if issuer := h.Config.GetAuthorizationServerIdentificationIssuer(ctx); len(issuer) != 0 { -			responder.GetParameters().Set(FormParameterIssuer, issuer) -		} -	} - -	return nil -} - -var ( -	_ fosite.AuthorizeEndpointHandler = (*AuthorizationServerIssuerIdentificationHandler)(nil) -) diff --git a/internal/oidc/handlers_test.go b/internal/oidc/handlers_test.go deleted file mode 100644 index f7b690d92..000000000 --- a/internal/oidc/handlers_test.go +++ /dev/null @@ -1,94 +0,0 @@ -package oidc_test - -import ( -	"context" -	"net/url" -	"testing" - -	"github.com/ory/fosite" -	"github.com/stretchr/testify/assert" - -	"github.com/authelia/authelia/v4/internal/oidc" -) - -func TestAuthorizationServerIssuerIdentificationHandler_HandleAuthorizeEndpointRequest(t *testing.T) { -	testCases := []struct { -		name     string -		issuer   string -		have     fosite.AuthorizeRequester -		expected url.Values -	}{ -		{ -			"ShouldAddIssuer", -			"https://auth.example.com", -			&fosite.AuthorizeRequest{}, -			url.Values{"iss": []string{"https://auth.example.com"}}, -		}, -		{ -			"ShouldAddIssuerResponseModeFormPost", -			"https://auth.example.com", -			&fosite.AuthorizeRequest{ResponseMode: fosite.ResponseModeFormPost}, -			url.Values{"iss": []string{"https://auth.example.com"}}, -		}, -		{ -			"ShouldAddIssuerResponseModeQuery", -			"https://auth.example.com", -			&fosite.AuthorizeRequest{ResponseMode: fosite.ResponseModeQuery}, -			url.Values{"iss": []string{"https://auth.example.com"}}, -		}, -		{ -			"ShouldAddIssuerResponseModeFragment", -			"https://auth.example.com", -			&fosite.AuthorizeRequest{ResponseMode: fosite.ResponseModeFragment}, -			url.Values{"iss": []string{"https://auth.example.com"}}, -		}, -		{ -			"ShouldAddIssuerResponseModeDefault", -			"https://auth.example.com", -			&fosite.AuthorizeRequest{ResponseMode: fosite.ResponseModeDefault}, -			url.Values{"iss": []string{"https://auth.example.com"}}, -		}, -		{ -			"ShouldNotAddIssuerResponseModeFormPostJWT", -			"https://auth.example.com", -			&fosite.AuthorizeRequest{ResponseMode: oidc.ResponseModeFormPostJWT}, -			url.Values{}, -		}, -		{ -			"ShouldNotAddIssuerResponseModeQueryJWT", -			"https://auth.example.com", -			&fosite.AuthorizeRequest{ResponseMode: oidc.ResponseModeQueryJWT}, -			url.Values{}, -		}, -		{ -			"ShouldNotAddIssuerResponseModeFragmentJWT", -			"https://auth.example.com", -			&fosite.AuthorizeRequest{ResponseMode: oidc.ResponseModeFragmentJWT}, -			url.Values{}, -		}, -		{ -			"ShouldNotAddIssuerResponseModeJWT", -			"https://auth.example.com", -			&fosite.AuthorizeRequest{ResponseMode: oidc.ResponseModeJWT}, -			url.Values{}, -		}, -	} - -	for _, tc := range testCases { -		t.Run(tc.name, func(t *testing.T) { -			config := &oidc.Config{Issuers: oidc.IssuersConfig{ -				AuthorizationServerIssuerIdentification: tc.issuer, -			}} - -			handler := &oidc.AuthorizationServerIssuerIdentificationHandler{Config: config} - -			responder := fosite.NewAuthorizeResponse() - -			ctx := context.TODO() - -			assert.NoError(t, handler.HandleAuthorizeEndpointRequest(ctx, tc.have, responder)) - -			assert.Equal(t, tc.expected, responder.GetParameters()) -		}) -	} -} diff --git a/internal/oidc/introspector_jwt.go b/internal/oidc/introspector_jwt.go deleted file mode 100644 index fa07c8816..000000000 --- a/internal/oidc/introspector_jwt.go +++ /dev/null @@ -1,48 +0,0 @@ -package oidc - -import ( -	"context" - -	"github.com/ory/fosite" -	"github.com/ory/fosite/handler/oauth2" -	"github.com/ory/fosite/token/jwt" -	"github.com/ory/x/errorsx" -) - -// StatelessJWTValidator is a stateless introspect for the JWT Access Tokens. -type StatelessJWTValidator struct { -	jwt.Signer -	Config interface { -		fosite.ScopeStrategyProvider -	} -} - -// IntrospectToken handles stateless token introspection if the token is a JWT Access Token. -func (v *StatelessJWTValidator) IntrospectToken(ctx context.Context, token string, tokenUse fosite.TokenUse, accessRequest fosite.AccessRequester, scopes []string) (fosite.TokenUse, error) { -	if ok, _ := isAccessTokenJWT(token); !ok { -		return "", fosite.ErrUnknownRequest.WithDebug("The provided token appears to be an opaque token not a JWT.") -	} - -	t, err := jwtValidate(ctx, v.Signer, token) -	if err != nil { -		return "", err -	} - -	if !IsJWTProfileAccessToken(t) { -		return "", errorsx.WithStack(fosite.ErrRequestUnauthorized.WithDebug("The provided token is not a valid RFC9068 JWT Profile Access Token as it is missing the header 'typ' value of 'at+jwt'.")) -	} - -	requester := oauth2.AccessTokenJWTToRequest(t) - -	if err = MatchScopes(v.Config.GetScopeStrategy(ctx), requester.GetGrantedScopes(), scopes); err != nil { -		return fosite.AccessToken, err -	} - -	accessRequest.Merge(requester) - -	return fosite.AccessToken, nil -} - -var ( -	_ fosite.TokenIntrospector = (*StatelessJWTValidator)(nil) -) diff --git a/internal/oidc/introspector_jwt_test.go b/internal/oidc/introspector_jwt_test.go deleted file mode 100644 index 8fb0594ee..000000000 --- a/internal/oidc/introspector_jwt_test.go +++ /dev/null @@ -1,130 +0,0 @@ -package oidc_test - -import ( -	"context" -	"testing" - -	"github.com/golang-jwt/jwt/v5" -	"github.com/ory/fosite" -	fjwt "github.com/ory/fosite/token/jwt" -	"github.com/stretchr/testify/assert" - -	"github.com/authelia/authelia/v4/internal/oidc" -) - -func TestStatelessJWTValidator_IntrospectToken(t *testing.T) { -	signer := &fjwt.DefaultSigner{ -		GetPrivateKey: func(ctx context.Context) (any, error) { -			return x509PrivateKeyRSA2048, nil -		}, -	} - -	maketoken := func(method jwt.SigningMethod, claims jwt.MapClaims, header map[string]any) string { -		j := &jwt.Token{ -			Header: header, -			Claims: claims, -			Method: method, -		} - -		if _, ok := j.Header[oidc.JWTHeaderKeyAlgorithm]; !ok { -			j.Header[oidc.JWTHeaderKeyAlgorithm] = method.Alg() -		} - -		token, err := j.SignedString(x509PrivateKeyRSA2048) - -		if err != nil { -			panic(err) -		} - -		return token -	} - -	handler := oidc.StatelessJWTValidator{ -		Signer: signer, -		Config: &ScopeStrategyProvider{ -			value: fosite.ExactScopeStrategy, -		}, -	} - -	testCases := []struct { -		name     string -		have     string -		scopes   []string -		expected fosite.TokenUse -		err      string -	}{ -		{ -			"ShouldHandleAccessTokenJWT", -			maketoken(jwt.SigningMethodRS256, jwt.MapClaims{}, map[string]any{oidc.JWTHeaderKeyType: oidc.JWTHeaderTypeValueAccessTokenJWT}), -			nil, -			fosite.AccessToken, -			"", -		}, -		{ -			"ShouldHandleAccessTokenJWTWithScopes", -			maketoken(jwt.SigningMethodRS256, jwt.MapClaims{oidc.ClaimScope: "example"}, map[string]any{oidc.JWTHeaderKeyType: oidc.JWTHeaderTypeValueAccessTokenJWT}), -			[]string{"example"}, -			fosite.AccessToken, -			"", -		}, -		{ -			"ShouldHandleAccessTokenJWTWithScopes", -			maketoken(jwt.SigningMethodRS256, jwt.MapClaims{oidc.ClaimScope: "example2"}, map[string]any{oidc.JWTHeaderKeyType: oidc.JWTHeaderTypeValueAccessTokenJWT}), -			[]string{"example"}, -			fosite.AccessToken, -			"The requested scope is invalid, unknown, or malformed. The request scope 'example' has not been granted or is not allowed to be requested.", -		}, -		{ -			"ShouldRejectStandardJWT", -			maketoken(jwt.SigningMethodRS256, jwt.MapClaims{}, map[string]any{oidc.JWTHeaderKeyType: "JWT"}), -			nil, -			fosite.TokenUse(""), -			"The request could not be authorized. Check that you provided valid credentials in the right format. The provided token is not a valid RFC9068 JWT Profile Access Token as it is missing the header 'typ' value of 'at+jwt'.", -		}, -		{ -			"ShouldRejectNonJWT", -			"authelia_at_example", -			nil, -			fosite.TokenUse(""), -			"The handler is not responsible for this request. The provided token appears to be an opaque token not a JWT.", -		}, -		{ -			"ShouldRejectTokenWithOpaquePrefix", -			"authelia_at_example.another.example", -			nil, -			fosite.TokenUse(""), -			"The handler is not responsible for this request. The provided token appears to be an opaque token not a JWT.", -		}, -		{ -			"ShouldRecjectInvalidToken", -			"example.another.example", -			nil, -			fosite.TokenUse(""), -			"Invalid token format. Check that you provided a valid token in the right format. invalid character '\\x16' looking for beginning of object key string", -		}, -	} - -	for _, tc := range testCases { -		t.Run(tc.name, func(t *testing.T) { -			ar := fosite.NewAccessRequest(oidc.NewSession()) - -			actual, err := handler.IntrospectToken(context.TODO(), tc.have, fosite.AccessToken, ar, tc.scopes) - -			assert.Equal(t, tc.expected, actual) - -			if len(tc.err) == 0 { -				assert.NoError(t, oidc.ErrorToDebugRFC6749Error(err)) -			} else { -				assert.EqualError(t, oidc.ErrorToDebugRFC6749Error(err), tc.err) -			} -		}) -	} -} - -type ScopeStrategyProvider struct { -	value fosite.ScopeStrategy -} - -func (p *ScopeStrategyProvider) GetScopeStrategy(ctx context.Context) fosite.ScopeStrategy { -	return p.value -} diff --git a/internal/oidc/jarm.go b/internal/oidc/jarm.go deleted file mode 100644 index d9f506093..000000000 --- a/internal/oidc/jarm.go +++ /dev/null @@ -1,179 +0,0 @@ -package oidc - -import ( -	"context" -	"errors" -	"net/url" -	"time" - -	"github.com/google/uuid" -	"github.com/ory/fosite/handler/oauth2" -	"github.com/ory/fosite/token/jwt" -) - -// EncodeJWTSecuredResponseParameters takes the result from GenerateJWTSecuredResponse and turns it into parameters in the form of url.Values. -func EncodeJWTSecuredResponseParameters(token, _ string, tErr error) (parameters url.Values, err error) { -	if tErr != nil { -		return nil, tErr -	} - -	return url.Values{FormParameterResponse: []string{token}}, nil -} - -// GenerateJWTSecuredResponse generates the token and signature for a JARM response. -func GenerateJWTSecuredResponse(ctx context.Context, config JWTSecuredResponseModeProvider, client Client, session any, in url.Values) (token, signature string, err error) { -	headers := map[string]any{} - -	if alg := client.GetAuthorizationSignedResponseAlg(); len(alg) > 0 { -		headers[JWTHeaderKeyAlgorithm] = alg -	} - -	if kid := client.GetAuthorizationSignedResponseKeyID(); len(kid) > 0 { -		headers[JWTHeaderKeyIdentifier] = kid -	} - -	var issuer string - -	issuer = config.GetJWTSecuredAuthorizeResponseModeIssuer(ctx) - -	if len(issuer) == 0 { -		var ( -			src   jwt.MapClaims -			value any -			ok    bool -		) - -		switch s := session.(type) { -		case nil: -			return "", "", errors.New("The JARM response modes require the Authorize Requester session to be set but it wasn't.") -		case IDTokenSessionContainer: -			src = s.IDTokenClaims().ToMapClaims() -		case oauth2.JWTSessionContainer: -			src = s.GetJWTClaims().ToMapClaims() -		default: -			return "", "", errors.New("The JARM response modes require the Authorize Requester session to implement either the IDTokenSessionContainer or oauth2.JWTSessionContainer interfaces but it doesn't.") -		} - -		if value, ok = src[ClaimIssuer]; ok { -			issuer, _ = value.(string) -		} -	} - -	claims := &JWTSecuredAuthorizationResponseModeClaims{ -		JTI:       uuid.New().String(), -		Issuer:    issuer, -		IssuedAt:  time.Now().UTC(), -		ExpiresAt: time.Now().UTC().Add(config.GetJWTSecuredAuthorizeResponseModeLifespan(ctx)), -		Audience:  []string{client.GetID()}, -		Extra:     map[string]any{}, -	} - -	for param := range in { -		claims.Extra[param] = in.Get(param) -	} - -	var signer jwt.Signer - -	if signer = config.GetJWTSecuredAuthorizeResponseModeSigner(ctx); signer == nil { -		return "", "", errors.New("The JARM response modes require the JWTSecuredAuthorizeResponseModeSignerProvider to return a jwt.Signer but it didn't.") -	} - -	return signer.Generate(ctx, claims.ToMapClaims(), &jwt.Headers{Extra: headers}) -} - -// JWTSecuredAuthorizationResponseModeClaims represent the JWT claims for JARM. -type JWTSecuredAuthorizationResponseModeClaims struct { -	JTI       string -	Issuer    string -	IssuedAt  time.Time -	ExpiresAt time.Time -	Audience  []string -	Extra     map[string]any -} - -// ToMap will transform the headers to a map structure. -func (c *JWTSecuredAuthorizationResponseModeClaims) ToMap() map[string]any { -	var ret = mapCopy(c.Extra) - -	if c.Issuer != "" { -		ret[ClaimIssuer] = c.Issuer -	} else { -		delete(ret, ClaimIssuer) -	} - -	if c.JTI != "" { -		ret[ClaimJWTID] = c.JTI -	} else { -		ret[ClaimJWTID] = uuid.New().String() -	} - -	if len(c.Audience) > 0 { -		ret[ClaimAudience] = c.Audience -	} else { -		ret[ClaimAudience] = []string{} -	} - -	if !c.IssuedAt.IsZero() { -		ret[ClaimIssuedAt] = c.IssuedAt.Unix() -	} else { -		delete(ret, ClaimIssuedAt) -	} - -	if !c.ExpiresAt.IsZero() { -		ret[ClaimExpirationTime] = c.ExpiresAt.Unix() -	} else { -		delete(ret, ClaimExpirationTime) -	} - -	return ret -} - -// FromMap will set the claims based on a mapping. -func (c *JWTSecuredAuthorizationResponseModeClaims) FromMap(m map[string]any) { -	c.Extra = make(map[string]any) - -	for k, v := range m { -		switch k { -		case ClaimJWTID: -			if s, ok := v.(string); ok { -				c.JTI = s -			} -		case ClaimIssuer: -			if s, ok := v.(string); ok { -				c.Issuer = s -			} -		case ClaimAudience: -			c.Audience = toStringSlice(v) -		case ClaimIssuedAt: -			c.IssuedAt = toTime(v, c.IssuedAt) -		case ClaimExpirationTime: -			c.ExpiresAt = toTime(v, c.ExpiresAt) -		default: -			c.Extra[k] = v -		} -	} -} - -// Add will add a key-value pair to the extra field. -func (c *JWTSecuredAuthorizationResponseModeClaims) Add(key string, value any) { -	if c.Extra == nil { -		c.Extra = make(map[string]any) -	} - -	c.Extra[key] = value -} - -// Get will get a value from the extra field based on a given key. -func (c *JWTSecuredAuthorizationResponseModeClaims) Get(key string) any { -	return c.ToMap()[key] -} - -// ToMapClaims will return a jwt-go MapClaims representation. -func (c *JWTSecuredAuthorizationResponseModeClaims) ToMapClaims() jwt.MapClaims { -	return c.ToMap() -} - -// FromMapClaims will populate claims from a jwt-go MapClaims representation. -func (c *JWTSecuredAuthorizationResponseModeClaims) FromMapClaims(mc jwt.MapClaims) { -	c.FromMap(mc) -} diff --git a/internal/oidc/jarm_test.go b/internal/oidc/jarm_test.go deleted file mode 100644 index e66c50807..000000000 --- a/internal/oidc/jarm_test.go +++ /dev/null @@ -1,322 +0,0 @@ -package oidc_test - -import ( -	"context" -	"net/url" -	"testing" -	"time" - -	"github.com/golang-jwt/jwt/v5" -	"github.com/ory/fosite/handler/oauth2" -	"github.com/ory/fosite/handler/openid" -	fjwt "github.com/ory/fosite/token/jwt" -	"github.com/stretchr/testify/assert" -	"github.com/stretchr/testify/require" - -	"github.com/authelia/authelia/v4/internal/oidc" -) - -func TestEncodeJWTSecuredResponseParameters(t *testing.T) { -	testCases := []struct { -		name     string -		issuer   string -		signer   *fjwt.DefaultSigner -		client   oidc.Client -		session  any -		in       url.Values -		expected jwt.MapClaims -		err      string -	}{ -		{ -			"ShouldErrorOnNilSession", -			"", -			&fjwt.DefaultSigner{ -				GetPrivateKey: func(ctx context.Context) (any, error) { -					return nil, nil -				}, -			}, -			&oidc.RegisteredClient{}, -			nil, -			nil, -			jwt.MapClaims{}, -			"The JARM response modes require the Authorize Requester session to be set but it wasn't.", -		}, -		{ -			"ShouldErrorOnBadTypeSession", -			"", -			&fjwt.DefaultSigner{ -				GetPrivateKey: func(ctx context.Context) (any, error) { -					return nil, nil -				}, -			}, -			&oidc.RegisteredClient{}, -			1, -			nil, -			jwt.MapClaims{}, -			"The JARM response modes require the Authorize Requester session to implement either the IDTokenSessionContainer or oauth2.JWTSessionContainer interfaces but it doesn't.", -		}, -		{ -			"ShouldErrorOnNilKey", -			"https://auth.example.com", -			&fjwt.DefaultSigner{ -				GetPrivateKey: func(ctx context.Context) (any, error) { -					return nil, nil -				}, -			}, -			&oidc.RegisteredClient{}, -			nil, -			nil, -			jwt.MapClaims{}, -			"unsupported private key type: <nil>", -		}, -		{ -			"ShouldErrorOnNilKey", -			"https://auth.example.com", -			&fjwt.DefaultSigner{ -				GetPrivateKey: func(ctx context.Context) (any, error) { -					return x509PrivateKeyRSA2048, nil -				}, -			}, -			&oidc.RegisteredClient{ -				ID:                               "example", -				AuthorizationSignedResponseAlg:   oidc.SigningAlgRSAUsingSHA256, -				AuthorizationSignedResponseKeyID: "12345", -			}, -			nil, -			nil, -			jwt.MapClaims{ -				oidc.ClaimAudience: []any{"example"}, -				oidc.ClaimIssuer:   "https://auth.example.com", -			}, -			"", -		}, -		{ -			"ShouldErrorOnNilSigner", -			"https://auth.example.com", -			nil, -			&oidc.RegisteredClient{ -				ID:                               "example", -				AuthorizationSignedResponseAlg:   oidc.SigningAlgRSAUsingSHA256, -				AuthorizationSignedResponseKeyID: "12345", -			}, -			nil, -			nil, -			jwt.MapClaims{ -				oidc.ClaimAudience: []any{"example"}, -				oidc.ClaimIssuer:   "https://auth.example.com", -			}, -			"The JARM response modes require the JWTSecuredAuthorizeResponseModeSignerProvider to return a jwt.Signer but it didn't.", -		}, -		{ -			"ShouldEncodeParameters", -			"https://auth.example.com", -			&fjwt.DefaultSigner{ -				GetPrivateKey: func(ctx context.Context) (any, error) { -					return x509PrivateKeyRSA2048, nil -				}, -			}, -			&oidc.RegisteredClient{ -				ID:                               "example", -				AuthorizationSignedResponseAlg:   oidc.SigningAlgRSAUsingSHA256, -				AuthorizationSignedResponseKeyID: "12345", -			}, -			nil, -			url.Values{oidc.FormParameterAuthorizationCode: []string{"123"}}, -			jwt.MapClaims{ -				oidc.ClaimAudience:                  []any{"example"}, -				oidc.ClaimIssuer:                    "https://auth.example.com", -				oidc.FormParameterAuthorizationCode: "123", -			}, -			"", -		}, -		{ -			"ShouldEncodeParametersAndRestoreIssuerFromSession", -			"", -			&fjwt.DefaultSigner{ -				GetPrivateKey: func(ctx context.Context) (any, error) { -					return x509PrivateKeyRSA2048, nil -				}, -			}, -			&oidc.RegisteredClient{ -				ID:                               "example", -				AuthorizationSignedResponseAlg:   oidc.SigningAlgRSAUsingSHA256, -				AuthorizationSignedResponseKeyID: "12345", -			}, -			&oauth2.JWTSession{ -				JWTClaims: &fjwt.JWTClaims{ -					Issuer: "https://original.example.com", -				}, -			}, -			url.Values{oidc.FormParameterAuthorizationCode: []string{"123"}}, -			jwt.MapClaims{ -				oidc.ClaimAudience:                  []any{"example"}, -				oidc.ClaimIssuer:                    "https://original.example.com", -				oidc.FormParameterAuthorizationCode: "123", -			}, -			"", -		}, -		{ -			"ShouldEncodeParametersAndRestoreIssuerFromSessionOpenID", -			"", -			&fjwt.DefaultSigner{ -				GetPrivateKey: func(ctx context.Context) (any, error) { -					return x509PrivateKeyRSA2048, nil -				}, -			}, -			&oidc.RegisteredClient{ -				ID:                               "example", -				AuthorizationSignedResponseAlg:   oidc.SigningAlgRSAUsingSHA256, -				AuthorizationSignedResponseKeyID: "12345", -			}, -			&openid.DefaultSession{ -				Claims: &fjwt.IDTokenClaims{ -					Issuer: "https://original.example.com", -				}, -			}, -			url.Values{oidc.FormParameterAuthorizationCode: []string{"123"}}, -			jwt.MapClaims{ -				oidc.ClaimAudience:                  []any{"example"}, -				oidc.ClaimIssuer:                    "https://original.example.com", -				oidc.FormParameterAuthorizationCode: "123", -			}, -			"", -		}, -	} - -	for _, tc := range testCases { -		t.Run(tc.name, func(t *testing.T) { -			config := &oidc.Config{ -				Issuers: oidc.IssuersConfig{JWTSecuredResponseMode: tc.issuer}, -			} - -			if tc.signer != nil { -				config.Signer = tc.signer -			} - -			actual, err := oidc.EncodeJWTSecuredResponseParameters(oidc.GenerateJWTSecuredResponse(context.TODO(), config, tc.client, tc.session, tc.in)) - -			if tc.err != "" { -				assert.Nil(t, actual) -				assert.EqualError(t, err, tc.err) -			} else { -				assert.NoError(t, err) - -				require.NotNil(t, actual) - -				jarm := actual.Get(oidc.FormParameterResponse) - -				require.NotEmpty(t, jarm) - -				token, _, err := jwt.NewParser().ParseUnverified(jarm, jwt.MapClaims{}) - -				assert.NoError(t, err) -				require.NotNil(t, token) - -				claims, ok := token.Claims.(jwt.MapClaims) - -				require.True(t, ok) - -				assert.NotEmpty(t, claims[oidc.ClaimJWTID]) -				assert.NotEmpty(t, claims[oidc.ClaimExpirationTime]) -				assert.NotEmpty(t, claims[oidc.ClaimIssuedAt]) - -				for claim, value := range tc.expected { -					switch claim { -					case oidc.ClaimJWTID, oidc.ClaimExpirationTime, oidc.ClaimIssuedAt: -						continue -					default: -						assert.Equal(t, value, claims[claim]) -					} -				} -			} -		}) -	} -} - -func TestJWTSecuredAuthorizationResponseModeClaims_ToFrom(t *testing.T) { -	testCases := []struct { -		name      string -		have      oidc.JWTSecuredAuthorizationResponseModeClaims -		expected  map[string]any -		expectedx fjwt.MapClaims -	}{ -		{ -			"ShouldReturnMinimal", -			oidc.JWTSecuredAuthorizationResponseModeClaims{ -				JTI:      "example", -				Audience: []string{}, -				Extra:    map[string]any{}, -			}, -			map[string]any{ -				oidc.ClaimAudience: []string{}, -				oidc.ClaimJWTID:    "example", -			}, -			fjwt.MapClaims{ -				oidc.ClaimAudience: []string{}, -				oidc.ClaimJWTID:    "example", -			}, -		}, -		{ -			"ShouldReturnComprehensive", -			oidc.JWTSecuredAuthorizationResponseModeClaims{ -				JTI:       "example", -				Issuer:    "https://auth.example.com", -				IssuedAt:  time.Unix(10000, 0).UTC(), -				ExpiresAt: time.Unix(10000+10, 0).UTC(), -				Audience:  []string{"example"}, -				Extra: map[string]any{ -					abc: 123, -				}, -			}, -			map[string]any{ -				oidc.ClaimJWTID:          "example", -				oidc.ClaimIssuer:         "https://auth.example.com", -				oidc.ClaimIssuedAt:       int64(10000), -				oidc.ClaimExpirationTime: int64(10010), -				oidc.ClaimAudience:       []string{"example"}, -				abc:                      123, -			}, -			fjwt.MapClaims{ -				oidc.ClaimJWTID:          "example", -				oidc.ClaimIssuer:         "https://auth.example.com", -				oidc.ClaimIssuedAt:       int64(10000), -				oidc.ClaimExpirationTime: int64(10010), -				oidc.ClaimAudience:       []string{"example"}, -				abc:                      123, -			}, -		}, -	} - -	for _, tc := range testCases { -		t.Run(tc.name, func(t *testing.T) { -			havemap := tc.have.ToMap() -			havemapclaims := tc.have.ToMapClaims() - -			assert.Equal(t, tc.expected, havemap) -			assert.Equal(t, tc.expectedx, havemapclaims) - -			frommap := oidc.JWTSecuredAuthorizationResponseModeClaims{} - -			frommap.FromMap(havemap) - -			frommampclaims := oidc.JWTSecuredAuthorizationResponseModeClaims{} - -			frommampclaims.FromMapClaims(havemapclaims) - -			assert.Equal(t, tc.have, frommap) -			assert.Equal(t, tc.have, frommampclaims) -		}) -	} - -	claims := oidc.JWTSecuredAuthorizationResponseModeClaims{} - -	mapclaims := claims.ToMap() - -	assert.NotEmpty(t, mapclaims[oidc.ClaimJWTID]) - -	assert.Nil(t, claims.Get(oidc.ClaimUsername)) - -	claims.Add(oidc.ClaimUsername, "example") - -	assert.Equal(t, "example", claims.Get(oidc.ClaimUsername)) -} diff --git a/internal/oidc/keys.go b/internal/oidc/keys.go index afd685e95..608ee9010 100644 --- a/internal/oidc/keys.go +++ b/internal/oidc/keys.go @@ -10,9 +10,9 @@ import (  	"sort"  	"strings" -	"github.com/go-jose/go-jose/v3" +	fjwt "authelia.com/provider/oauth2/token/jwt" +	"github.com/go-jose/go-jose/v4"  	"github.com/golang-jwt/jwt/v5" -	fjwt "github.com/ory/fosite/token/jwt"  	"github.com/ory/x/errorsx"  	"github.com/authelia/authelia/v4/internal/configuration/schema" diff --git a/internal/oidc/keys_blackbox_test.go b/internal/oidc/keys_blackbox_test.go index 60459884a..8ce91bb02 100644 --- a/internal/oidc/keys_blackbox_test.go +++ b/internal/oidc/keys_blackbox_test.go @@ -7,8 +7,8 @@ import (  	"fmt"  	"testing" -	"github.com/go-jose/go-jose/v3" -	fjwt "github.com/ory/fosite/token/jwt" +	fjwt "authelia.com/provider/oauth2/token/jwt" +	"github.com/go-jose/go-jose/v4"  	"github.com/stretchr/testify/assert"  	"github.com/stretchr/testify/require" diff --git a/internal/oidc/provider.go b/internal/oidc/provider.go index 186707015..23544f1f2 100644 --- a/internal/oidc/provider.go +++ b/internal/oidc/provider.go @@ -3,7 +3,7 @@ package oidc  import (  	"fmt" -	"github.com/ory/fosite" +	oauthelia2 "authelia.com/provider/oauth2"  	"github.com/ory/herodot"  	"github.com/authelia/authelia/v4/internal/configuration/schema" @@ -26,10 +26,9 @@ func NewOpenIDConnectProvider(config *schema.IdentityProvidersOpenIDConnect, sto  		Config:     NewConfig(config, signer, templates),  	} -	provider.OAuth2Provider = fosite.NewOAuth2Provider(provider.Store, provider.Config) +	provider.Provider = oauthelia2.New(provider.Store, provider.Config)  	provider.Config.LoadHandlers(provider.Store) -	provider.Config.Strategy.ClientAuthentication = provider.DefaultClientAuthenticationStrategy  	provider.discovery = NewOpenIDConnectWellKnownConfiguration(config) diff --git a/internal/oidc/response_mode.go b/internal/oidc/response_mode.go deleted file mode 100644 index 4eb9ae4fb..000000000 --- a/internal/oidc/response_mode.go +++ /dev/null @@ -1,204 +0,0 @@ -package oidc - -import ( -	"context" -	"encoding/json" -	"fmt" -	"net/http" -	"net/url" - -	"github.com/ory/fosite" -	"github.com/valyala/fasthttp" -) - -// ResponseModeHandler returns the response mode handler. -func (p *OpenIDConnectProvider) ResponseModeHandler(ctx context.Context) fosite.ResponseModeHandler { -	if ext := p.Config.GetResponseModeHandlerExtension(ctx); ext != nil { -		return ext -	} - -	return handlerDefaultResponseMode -} - -// WriteAuthorizeResponse decorates the fosite.WriteAuthorizeResponse so that we can ensure our response mode handler is used first. -func (p *OpenIDConnectProvider) WriteAuthorizeResponse(ctx context.Context, rw http.ResponseWriter, requester fosite.AuthorizeRequester, responder fosite.AuthorizeResponder) { -	if handler := p.ResponseModeHandler(ctx); handler.ResponseModes().Has(requester.GetResponseMode()) { -		handler.WriteAuthorizeResponse(ctx, rw, requester, responder) - -		return -	} - -	p.OAuth2Provider.WriteAuthorizeResponse(ctx, rw, requester, responder) -} - -// ResponseModeHandler is the custom response mode handler for Authelia. -// Implements the fosite.ResponseModeHandler interface. -type ResponseModeHandler struct { -	Config Configurator -} - -// ResponseModes returns the response modes this fosite.ResponseModeHandler is responsible for. -func (h *ResponseModeHandler) ResponseModes() fosite.ResponseModeTypes { -	return fosite.ResponseModeTypes{ -		fosite.ResponseModeDefault, -		ResponseModeQuery, -		ResponseModeFragment, -		ResponseModeFormPost, -		ResponseModeJWT, -		ResponseModeQueryJWT, -		ResponseModeFragmentJWT, -		ResponseModeFormPostJWT, -	} -} - -// EncodeResponseForm encodes the response form if necessary. -func (h *ResponseModeHandler) EncodeResponseForm(ctx context.Context, rm fosite.ResponseModeType, client Client, session any, parameters url.Values) (form url.Values, err error) { -	switch rm { -	case ResponseModeFormPostJWT, ResponseModeQueryJWT, ResponseModeFragmentJWT: -		return EncodeJWTSecuredResponseParameters(GenerateJWTSecuredResponse(ctx, h.Config, client, session, parameters)) -	default: -		return parameters, nil -	} -} - -// WriteAuthorizeResponse writes authorization responses. -func (h *ResponseModeHandler) WriteAuthorizeResponse(ctx context.Context, rw http.ResponseWriter, requester fosite.AuthorizeRequester, responder fosite.AuthorizeResponder) { -	wh := rw.Header() -	rh := responder.GetHeader() - -	for k := range rh { -		wh.Set(k, rh.Get(k)) -	} - -	h.doWriteAuthorizeResponse(ctx, rw, requester, responder.GetParameters()) -} - -// WriteAuthorizeError writes authorization errors. -func (h *ResponseModeHandler) WriteAuthorizeError(ctx context.Context, rw http.ResponseWriter, requester fosite.AuthorizeRequester, e error) { -	rfc := fosite.ErrorToRFC6749Error(e). -		WithLegacyFormat(h.Config.GetUseLegacyErrorFormat(ctx)). -		WithExposeDebug(h.Config.GetSendDebugMessagesToClients(ctx)). -		WithLocalizer(h.Config.GetMessageCatalog(ctx), GetLangFromRequester(requester)) - -	if !requester.IsRedirectURIValid() { -		h.doWriteAuthorizeErrorJSON(ctx, rw, rfc) - -		return -	} - -	parameters := rfc.ToValues() - -	if state := requester.GetState(); len(state) != 0 { -		parameters.Set(FormParameterState, state) -	} - -	switch requester.GetResponseMode() { -	case fosite.ResponseModeFormPost, fosite.ResponseModeQuery, fosite.ResponseModeFragment, fosite.ResponseModeDefault: -		if issuer := h.Config.GetAuthorizationServerIdentificationIssuer(ctx); len(issuer) != 0 { -			parameters.Set(FormParameterIssuer, issuer) -		} -	} - -	h.doWriteAuthorizeResponse(ctx, rw, requester, parameters) -} - -func (h *ResponseModeHandler) doWriteAuthorizeResponse(ctx context.Context, rw http.ResponseWriter, requester fosite.AuthorizeRequester, parameters url.Values) { -	redirectURI := requester.GetRedirectURI() -	redirectURI.Fragment = "" - -	var ( -		client Client -		ok     bool -	) - -	if client, ok = requester.GetClient().(Client); !ok { -		h.doWriteAuthorizeErrorJSON(ctx, rw, fosite.ErrServerError.WithDebug("The client had an unexpected type.")) - -		return -	} - -	rm := requester.GetResponseMode() - -	if rm == ResponseModeJWT { -		if requester.GetResponseTypes().ExactOne(ResponseTypeAuthorizationCodeFlow) { -			rm = ResponseModeQueryJWT -		} else { -			rm = ResponseModeFragmentJWT -		} -	} - -	var ( -		form     url.Values -		err      error -		location string -	) - -	switch rm { -	case fosite.ResponseModeFormPost, ResponseModeFormPostJWT: -		if form, err = h.EncodeResponseForm(ctx, rm, client, requester.GetSession(), parameters); err != nil { -			h.doWriteAuthorizeErrorJSON(ctx, rw, fosite.ErrServerError.WithWrap(err).WithDebug(err.Error())) - -			return -		} - -		rw.Header().Set(fasthttp.HeaderContentType, headerContentTypeTextHTML) -		fosite.WriteAuthorizeFormPostResponse(redirectURI.String(), form, h.Config.GetFormPostHTMLTemplate(ctx), rw) - -		return -	case fosite.ResponseModeQuery, fosite.ResponseModeDefault, ResponseModeQueryJWT, ResponseModeJWT: -		for key, values := range redirectURI.Query() { -			for _, value := range values { -				parameters.Add(key, value) -			} -		} - -		if form, err = h.EncodeResponseForm(ctx, rm, client, requester.GetSession(), parameters); err != nil { -			h.doWriteAuthorizeErrorJSON(ctx, rw, fosite.ErrServerError.WithWrap(err).WithDebug(err.Error())) - -			return -		} - -		redirectURI.RawQuery = form.Encode() - -		location = redirectURI.String() -	case fosite.ResponseModeFragment, ResponseModeFragmentJWT: -		if form, err = h.EncodeResponseForm(ctx, rm, client, requester.GetSession(), parameters); err != nil { -			h.doWriteAuthorizeErrorJSON(ctx, rw, fosite.ErrServerError.WithWrap(err).WithDebug(err.Error())) - -			return -		} - -		location = redirectURI.String() + "#" + form.Encode() -	} - -	rw.Header().Set(fasthttp.HeaderLocation, location) -	rw.WriteHeader(http.StatusSeeOther) -} - -func (h *ResponseModeHandler) doWriteAuthorizeErrorJSON(ctx context.Context, rw http.ResponseWriter, rfc *fosite.RFC6749Error) { -	rw.Header().Set(fasthttp.HeaderContentType, headerContentTypeApplicationJSON) - -	var ( -		data []byte -		err  error -	) - -	if data, err = json.Marshal(rfc); err != nil { -		if h.Config.GetSendDebugMessagesToClients(ctx) { -			http.Error(rw, fmt.Sprintf(`{"error":"server_error","error_description":"%s"}`, fosite.EscapeJSONString(err.Error())), http.StatusInternalServerError) -		} else { -			http.Error(rw, `{"error":"server_error"}`, http.StatusInternalServerError) -		} - -		return -	} - -	rw.WriteHeader(rfc.CodeField) -	_, _ = rw.Write(data) -} - -var ( -	_ fosite.ResponseModeHandler = (*ResponseModeHandler)(nil) - -	handlerDefaultResponseMode = &fosite.DefaultResponseModeHandler{} -) diff --git a/internal/oidc/response_mode_test.go b/internal/oidc/response_mode_test.go deleted file mode 100644 index 5353cd4a7..000000000 --- a/internal/oidc/response_mode_test.go +++ /dev/null @@ -1,637 +0,0 @@ -package oidc_test - -import ( -	"context" -	"io" -	"net/http" -	"net/http/httptest" -	"net/url" -	"regexp" -	"testing" - -	"github.com/ory/fosite" -	"github.com/ory/fosite/token/jwt" -	"github.com/stretchr/testify/assert" -	"github.com/stretchr/testify/require" -	"github.com/valyala/fasthttp" - -	"github.com/authelia/authelia/v4/internal/oidc" -	"github.com/authelia/authelia/v4/internal/templates" -) - -func TestOpenIDConnectProvider_ResponseModeHandler(t *testing.T) { -	testCases := []struct { -		name     string -		have     fosite.ResponseModeHandler -		expected any -	}{ -		{ -			"ShouldReturnDefaultFosite", -			nil, -			&fosite.DefaultResponseModeHandler{}, -		}, -		{ -			"ShouldReturnInternal", -			&oidc.ResponseModeHandler{}, -			&oidc.ResponseModeHandler{}, -		}, -	} - -	for _, tc := range testCases { -		t.Run(tc.name, func(t *testing.T) { -			config := &oidc.Config{Handlers: oidc.HandlersConfig{ResponseMode: tc.have}} - -			provider := &oidc.OpenIDConnectProvider{Config: config} - -			actual := provider.ResponseModeHandler(context.TODO()) - -			assert.IsType(t, tc.expected, actual) -		}) -	} -} - -func TestOpenIDConnectProvider_WriteAuthorizeResponse(t *testing.T) { -	testCases := []struct { -		name       string -		requester  fosite.AuthorizeRequester -		responder  fosite.AuthorizeResponder -		setup      func(t *testing.T, config *oidc.Config) -		code       int -		header     http.Header -		headerFunc func(t *testing.T, header http.Header) -		body       string -		bodyRegexp *regexp.Regexp -	}{ -		{ -			"ShouldHandleResponseModeQuery", -			&fosite.AuthorizeRequest{ -				ResponseMode: oidc.ResponseModeQuery, -				Request: fosite.Request{ -					Client: &oidc.RegisteredClient{ -						ID: "example", -						RedirectURIs: []string{ -							"https://app.example.com/callback", -						}, -					}, -				}, -				RedirectURI: &url.URL{Scheme: "https", Host: "app.example.com", Path: "/callback"}, -			}, -			&fosite.AuthorizeResponse{ -				Parameters: url.Values{ -					oidc.FormParameterAuthorizationCode: []string{"1234"}, -					oidc.FormParameterIssuer:            []string{"https://auth.example.com"}, -				}, -			}, -			nil, -			fasthttp.StatusSeeOther, -			http.Header{fasthttp.HeaderLocation: []string{"https://app.example.com/callback?code=1234&iss=https%3A%2F%2Fauth.example.com"}}, -			nil, -			"", -			nil, -		}, -		{ -			"ShouldWriteHeaders", -			&fosite.AuthorizeRequest{ -				ResponseMode: oidc.ResponseModeQuery, -				Request: fosite.Request{ -					Client: &oidc.RegisteredClient{ -						ID: "example", -						RedirectURIs: []string{ -							"https://app.example.com/callback", -						}, -					}, -				}, -				RedirectURI: &url.URL{Scheme: "https", Host: "app.example.com", Path: "/callback"}, -			}, -			&fosite.AuthorizeResponse{ -				Header: http.Header{ -					fasthttp.HeaderAccept: []string{"123"}, -				}, -				Parameters: url.Values{ -					oidc.FormParameterAuthorizationCode: []string{"1234"}, -					oidc.FormParameterIssuer:            []string{"https://auth.example.com"}, -				}, -			}, -			nil, -			fasthttp.StatusSeeOther, -			http.Header{ -				fasthttp.HeaderLocation: []string{"https://app.example.com/callback?code=1234&iss=https%3A%2F%2Fauth.example.com"}, -				fasthttp.HeaderAccept:   []string{"123"}, -			}, -			nil, -			"", -			nil, -		}, -		{ -			"ShouldHandleBadClient", -			&fosite.AuthorizeRequest{ -				ResponseMode: oidc.ResponseModeQuery, -				Request: fosite.Request{ -					Client: &fosite.DefaultClient{ -						ID: "example", -						RedirectURIs: []string{ -							"https://app.example.com/callback", -						}, -					}, -				}, -				RedirectURI: &url.URL{Scheme: "https", Host: "app.example.com", Path: "/callback"}, -			}, -			&fosite.AuthorizeResponse{ -				Header: http.Header{ -					fasthttp.HeaderAccept: []string{"123"}, -				}, -				Parameters: url.Values{ -					oidc.FormParameterAuthorizationCode: []string{"1234"}, -					oidc.FormParameterIssuer:            []string{"https://auth.example.com"}, -				}, -			}, -			nil, -			fasthttp.StatusInternalServerError, -			http.Header{ -				fasthttp.HeaderContentType: []string{"application/json; charset=utf-8"}, -				fasthttp.HeaderAccept:      []string{"123"}, -			}, -			nil, -			"{\"error\":\"server_error\",\"error_description\":\"The authorization server encountered an unexpected condition that prevented it from fulfilling the request.\"}", -			nil, -		}, -		{ -			"ShouldHandleResponseModeQueryWithExistingQuery", -			&fosite.AuthorizeRequest{ -				ResponseMode: oidc.ResponseModeQuery, -				Request: fosite.Request{ -					Client: &oidc.RegisteredClient{ -						ID: "example", -						RedirectURIs: []string{ -							"https://app.example.com/callback?abc=true", -						}, -					}, -				}, -				RedirectURI: &url.URL{Scheme: "https", Host: "app.example.com", Path: "/callback", RawQuery: "abc=true"}, -			}, -			&fosite.AuthorizeResponse{ -				Parameters: url.Values{ -					oidc.FormParameterAuthorizationCode: []string{"1234"}, -					oidc.FormParameterIssuer:            []string{"https://auth.example.com"}, -				}, -			}, -			nil, -			fasthttp.StatusSeeOther, -			http.Header{fasthttp.HeaderLocation: []string{"https://app.example.com/callback?abc=true&code=1234&iss=https%3A%2F%2Fauth.example.com"}}, -			nil, -			"", -			nil, -		}, -		{ -			"ShouldHandleResponseModeFragment", -			&fosite.AuthorizeRequest{ -				ResponseMode: oidc.ResponseModeFragment, -				Request: fosite.Request{ -					Client: &oidc.RegisteredClient{ -						ID: "example", -						RedirectURIs: []string{ -							"https://app.example.com/callback", -						}, -					}, -				}, -				RedirectURI: &url.URL{Scheme: "https", Host: "app.example.com", Path: "/callback"}, -			}, -			&fosite.AuthorizeResponse{ -				Parameters: url.Values{ -					oidc.FormParameterAuthorizationCode: []string{"1234"}, -					oidc.FormParameterIssuer:            []string{"https://auth.example.com"}, -				}, -			}, -			nil, -			fasthttp.StatusSeeOther, -			http.Header{fasthttp.HeaderLocation: []string{"https://app.example.com/callback#code=1234&iss=https%3A%2F%2Fauth.example.com"}}, -			nil, -			"", -			nil, -		}, -		{ -			"ShouldHandleResponseModeFormPost", -			&fosite.AuthorizeRequest{ -				ResponseMode: oidc.ResponseModeFormPost, -				Request: fosite.Request{ -					Client: &oidc.RegisteredClient{ -						ID: "example", -						RedirectURIs: []string{ -							"https://app.example.com/callback", -						}, -					}, -				}, -				RedirectURI: &url.URL{Scheme: "https", Host: "app.example.com", Path: "/callback"}, -			}, -			&fosite.AuthorizeResponse{ -				Parameters: url.Values{ -					oidc.FormParameterAuthorizationCode: []string{"1234"}, -					oidc.FormParameterIssuer:            []string{"https://auth.example.com"}, -				}, -			}, -			nil, -			fasthttp.StatusOK, -			http.Header{fasthttp.HeaderContentType: []string{"text/html; charset=utf-8"}}, -			nil, -			"<!DOCTYPE html>\n<html lang=\"en\">\n\t<head>\n\t\t<title>Submit This Form</title>\n\t\t<script type=\"text/javascript\">\n\t\t\twindow.onload = function() {\n\t\t\t\tdocument.forms[0].submit();\n\t\t\t};\n\t\t</script>\n\t</head>\n\t<body>\n\t\t<form method=\"post\" action=\"https://app.example.com/callback\">\n\t\t\t\n\t\t\t\n\t\t\t<input type=\"hidden\" name=\"code\" value=\"1234\"/>\n\t\t\t\n\t\t\t\n\t\t\t\n\t\t\t<input type=\"hidden\" name=\"iss\" value=\"https://auth.example.com\"/>\n\t\t\t\n\t\t\t\n\t\t</form>\n\t</body>\n</html>\n", -			nil, -		}, -		{ -			"ShouldReturnEncoderErrorResponseModeJWT", -			&fosite.AuthorizeRequest{ -				ResponseMode:  oidc.ResponseModeJWT, -				ResponseTypes: fosite.Arguments{oidc.ResponseTypeAuthorizationCodeFlow}, -				Request: fosite.Request{ -					Client: &oidc.RegisteredClient{ -						ID: "example", -						RedirectURIs: []string{ -							"https://app.example.com/callback", -						}, -					}, -				}, -				RedirectURI: &url.URL{Scheme: "https", Host: "app.example.com", Path: "/callback"}, -			}, -			&fosite.AuthorizeResponse{ -				Parameters: url.Values{ -					oidc.FormParameterAuthorizationCode: []string{"1234"}, -					oidc.FormParameterIssuer:            []string{"https://auth.example.com"}, -				}, -			}, -			func(t *testing.T, config *oidc.Config) { -				config.Signer = nil -			}, -			fasthttp.StatusInternalServerError, -			http.Header{fasthttp.HeaderContentType: []string{"application/json; charset=utf-8"}}, -			nil, -			"{\"error\":\"server_error\",\"error_description\":\"The authorization server encountered an unexpected condition that prevented it from fulfilling the request.\"}", -			nil, -		}, -		{ -			"ShouldReturnEncoderErrorResponseModeFormPostJWT", -			&fosite.AuthorizeRequest{ -				ResponseMode:  oidc.ResponseModeFormPostJWT, -				ResponseTypes: fosite.Arguments{oidc.ResponseTypeAuthorizationCodeFlow}, -				Request: fosite.Request{ -					Client: &oidc.RegisteredClient{ -						ID: "example", -						RedirectURIs: []string{ -							"https://app.example.com/callback", -						}, -					}, -				}, -				RedirectURI: &url.URL{Scheme: "https", Host: "app.example.com", Path: "/callback"}, -			}, -			&fosite.AuthorizeResponse{ -				Parameters: url.Values{ -					oidc.FormParameterAuthorizationCode: []string{"1234"}, -					oidc.FormParameterIssuer:            []string{"https://auth.example.com"}, -				}, -			}, -			func(t *testing.T, config *oidc.Config) { -				config.Signer = nil -			}, -			fasthttp.StatusInternalServerError, -			http.Header{fasthttp.HeaderContentType: []string{"application/json; charset=utf-8"}}, -			nil, -			"{\"error\":\"server_error\",\"error_description\":\"The authorization server encountered an unexpected condition that prevented it from fulfilling the request.\"}", -			nil, -		}, -		{ -			"ShouldReturnEncoderErrorResponseModeFragmentJWT", -			&fosite.AuthorizeRequest{ -				ResponseMode:  oidc.ResponseModeFragmentJWT, -				ResponseTypes: fosite.Arguments{oidc.ResponseTypeAuthorizationCodeFlow}, -				Request: fosite.Request{ -					Client: &oidc.RegisteredClient{ -						ID: "example", -						RedirectURIs: []string{ -							"https://app.example.com/callback", -						}, -					}, -				}, -				RedirectURI: &url.URL{Scheme: "https", Host: "app.example.com", Path: "/callback"}, -			}, -			&fosite.AuthorizeResponse{ -				Parameters: url.Values{ -					oidc.FormParameterAuthorizationCode: []string{"1234"}, -					oidc.FormParameterIssuer:            []string{"https://auth.example.com"}, -				}, -			}, -			func(t *testing.T, config *oidc.Config) { -				config.Signer = nil -			}, -			fasthttp.StatusInternalServerError, -			http.Header{fasthttp.HeaderContentType: []string{"application/json; charset=utf-8"}}, -			nil, -			"{\"error\":\"server_error\",\"error_description\":\"The authorization server encountered an unexpected condition that prevented it from fulfilling the request.\"}", -			nil, -		}, -		{ -			"ShouldEncodeJWTResponseModeJWTResponseTypesCode", -			&fosite.AuthorizeRequest{ -				ResponseMode:  oidc.ResponseModeJWT, -				ResponseTypes: fosite.Arguments{oidc.ResponseTypeAuthorizationCodeFlow}, -				Request: fosite.Request{ -					Client: &oidc.RegisteredClient{ -						ID: "example", -						RedirectURIs: []string{ -							"https://app.example.com/callback", -						}, -					}, -				}, -				RedirectURI: &url.URL{Scheme: "https", Host: "app.example.com", Path: "/callback"}, -			}, -			&fosite.AuthorizeResponse{ -				Parameters: url.Values{ -					oidc.FormParameterAuthorizationCode: []string{"1234"}, -					oidc.FormParameterIssuer:            []string{"https://auth.example.com"}, -				}, -			}, -			nil, -			fasthttp.StatusSeeOther, -			nil, -			func(t *testing.T, header http.Header) { -				uri, err := url.ParseRequestURI(header.Get(fasthttp.HeaderLocation)) -				assert.NoError(t, err) - -				require.NotNil(t, uri) - -				assert.Equal(t, "https", uri.Scheme) -				assert.Equal(t, "app.example.com", uri.Host) -				assert.Equal(t, "/callback", uri.Path) -				assert.Regexp(t, regexp.MustCompile(`^[a-zA-Z0-9_-]+\.[a-zA-Z0-9_-]+\.[a-zA-Z0-9_-]+$`), uri.Query().Get(oidc.FormParameterResponse)) -			}, -			"", -			nil, -		}, -		{ -			"ShouldEncodeJWTResponseModeJWTResponseTypesNotCode", -			&fosite.AuthorizeRequest{ -				ResponseMode:  oidc.ResponseModeJWT, -				ResponseTypes: fosite.Arguments{oidc.ResponseTypeImplicitFlowBoth}, -				Request: fosite.Request{ -					Client: &oidc.RegisteredClient{ -						ID: "example", -						RedirectURIs: []string{ -							"https://app.example.com/callback", -						}, -					}, -				}, -				RedirectURI: &url.URL{Scheme: "https", Host: "app.example.com", Path: "/callback"}, -			}, -			&fosite.AuthorizeResponse{ -				Parameters: url.Values{ -					oidc.FormParameterAuthorizationCode: []string{"1234"}, -					oidc.FormParameterIssuer:            []string{"https://auth.example.com"}, -				}, -			}, -			nil, -			fasthttp.StatusSeeOther, -			nil, -			func(t *testing.T, header http.Header) { -				uri, err := url.Parse(header.Get(fasthttp.HeaderLocation)) -				assert.NoError(t, err) - -				require.NotNil(t, uri) - -				assert.Equal(t, "https", uri.Scheme) -				assert.Equal(t, "app.example.com", uri.Host) -				assert.Equal(t, "/callback", uri.Path) -				assert.Regexp(t, regexp.MustCompile(`^response=[a-zA-Z0-9_-]+\.[a-zA-Z0-9_-]+\.[a-zA-Z0-9_-]+$`), uri.Fragment) -			}, -			"", -			nil, -		}, -		{ -			"ShouldEncodeJWTResponseModeFormPost", -			&fosite.AuthorizeRequest{ -				ResponseMode:  oidc.ResponseModeFormPostJWT, -				ResponseTypes: fosite.Arguments{oidc.ResponseTypeAuthorizationCodeFlow}, -				Request: fosite.Request{ -					Client: &oidc.RegisteredClient{ -						ID: "example", -						RedirectURIs: []string{ -							"https://app.example.com/callback", -						}, -					}, -				}, -				RedirectURI: &url.URL{Scheme: "https", Host: "app.example.com", Path: "/callback"}, -			}, -			&fosite.AuthorizeResponse{ -				Parameters: url.Values{ -					oidc.FormParameterAuthorizationCode: []string{"1234"}, -					oidc.FormParameterIssuer:            []string{"https://auth.example.com"}, -				}, -			}, -			nil, -			fasthttp.StatusOK, -			http.Header{fasthttp.HeaderContentType: []string{"text/html; charset=utf-8"}}, -			nil, -			"", -			regexp.MustCompile(`<input type="hidden" name="response" value="[a-zA-Z0-9_-]+\.[a-zA-Z0-9_-]+\.[a-zA-Z0-9_-]+"/>`), -		}, -	} - -	tp, err := templates.New(templates.Config{}) - -	require.NoError(t, err) - -	for _, tc := range testCases { -		t.Run(tc.name, func(t *testing.T) { -			config := &oidc.Config{ -				Signer: &jwt.DefaultSigner{ -					GetPrivateKey: func(ctx context.Context) (interface{}, error) { -						return x509PrivateKeyRSA2048, nil -					}, -				}, -				Templates: tp, -				Issuers: oidc.IssuersConfig{ -					AuthorizationServerIssuerIdentification: "https://auth.example.com", -					JWTSecuredResponseMode:                  "https://auth.example.com", -				}, -			} - -			mock := httptest.NewRecorder() - -			handler := &oidc.ResponseModeHandler{ -				Config: config, -			} - -			config.Handlers.ResponseMode = handler - -			provider := &oidc.OpenIDConnectProvider{Config: config} - -			if tc.setup != nil { -				tc.setup(t, config) -			} - -			provider.WriteAuthorizeResponse(context.TODO(), mock, tc.requester, tc.responder) - -			result := mock.Result() - -			assert.Equal(t, tc.code, result.StatusCode) - -			if tc.header != nil { -				assert.Equal(t, tc.header, result.Header) -			} - -			if tc.headerFunc != nil { -				tc.headerFunc(t, result.Header) -			} - -			data, err := io.ReadAll(result.Body) -			require.NoError(t, err) - -			if tc.bodyRegexp == nil { -				assert.Equal(t, tc.body, string(data)) -			} else { -				assert.Regexp(t, tc.bodyRegexp, string(data)) -			} -		}) -	} -} - -func TestOpenIDConnectProvider_WriteAuthorizeError(t *testing.T) { -	testCases := []struct { -		name       string -		requester  fosite.AuthorizeRequester -		setup      func(t *testing.T, config *oidc.Config) -		error      error -		code       int -		header     http.Header -		headerFunc func(t *testing.T, header http.Header) -		body       string -		bodyRegexp *regexp.Regexp -	}{ -		{ -			"ShouldHandleErrorResponse", -			&fosite.AuthorizeRequest{ -				ResponseMode: oidc.ResponseModeQuery, -				Request: fosite.Request{ -					Client: &oidc.RegisteredClient{ -						ID: "example", -						RedirectURIs: []string{ -							"https://app.example.com/callback", -						}, -					}, -				}, -				RedirectURI: &url.URL{Scheme: "https", Host: "app.example.com", Path: "/callback"}, -			}, -			nil, -			fosite.ErrServerError.WithDebug("The Debug."), -			fasthttp.StatusSeeOther, -			http.Header{ -				fasthttp.HeaderLocation: []string{"https://app.example.com/callback?error=server_error&error_description=The+authorization+server+encountered+an+unexpected+condition+that+prevented+it+from+fulfilling+the+request.&iss=https%3A%2F%2Fauth.example.com"}, -			}, -			nil, -			"", -			nil, -		}, -		{ -			"ShouldHandleErrorResponseWithState", -			&fosite.AuthorizeRequest{ -				ResponseMode: oidc.ResponseModeQuery, -				State:        "abc123state", -				Request: fosite.Request{ -					Client: &oidc.RegisteredClient{ -						ID: "example", -						RedirectURIs: []string{ -							"https://app.example.com/callback", -						}, -					}, -				}, -				RedirectURI: &url.URL{Scheme: "https", Host: "app.example.com", Path: "/callback"}, -			}, -			nil, -			fosite.ErrServerError.WithDebug("The Debug."), -			fasthttp.StatusSeeOther, -			http.Header{ -				fasthttp.HeaderLocation: []string{"https://app.example.com/callback?error=server_error&error_description=The+authorization+server+encountered+an+unexpected+condition+that+prevented+it+from+fulfilling+the+request.&iss=https%3A%2F%2Fauth.example.com&state=abc123state"}, -			}, -			nil, -			"", -			nil, -		}, -		{ -			"ShouldHandleErrorResponseWithInvalidRedirectURI", -			&fosite.AuthorizeRequest{ -				ResponseMode: oidc.ResponseModeQuery, -				State:        "abc123state", -				Request: fosite.Request{ -					Client: &oidc.RegisteredClient{ -						ID: "example", -						RedirectURIs: []string{ -							"https://app.example.com/callback", -						}, -					}, -				}, -				RedirectURI: &url.URL{Scheme: "https", Host: "app.example.com", Path: "/invalid"}, -			}, -			nil, -			fosite.ErrServerError.WithDebug("The Debug."), -			fasthttp.StatusInternalServerError, -			http.Header{ -				fasthttp.HeaderContentType: []string{"application/json; charset=utf-8"}, -			}, -			nil, -			"{\"error\":\"server_error\",\"error_description\":\"The authorization server encountered an unexpected condition that prevented it from fulfilling the request.\"}", -			nil, -		}, -	} - -	tp, err := templates.New(templates.Config{}) - -	require.NoError(t, err) - -	for _, tc := range testCases { -		t.Run(tc.name, func(t *testing.T) { -			config := &oidc.Config{ -				Signer: &jwt.DefaultSigner{ -					GetPrivateKey: func(ctx context.Context) (interface{}, error) { -						return x509PrivateKeyRSA2048, nil -					}, -				}, -				Templates: tp, -				Issuers: oidc.IssuersConfig{ -					AuthorizationServerIssuerIdentification: "https://auth.example.com", -					JWTSecuredResponseMode:                  "https://auth.example.com", -				}, -			} - -			mock := httptest.NewRecorder() - -			handler := &oidc.ResponseModeHandler{ -				Config: config, -			} - -			config.Handlers.ResponseMode = handler - -			if tc.setup != nil { -				tc.setup(t, config) -			} - -			handler.WriteAuthorizeError(context.TODO(), mock, tc.requester, tc.error) - -			result := mock.Result() - -			assert.Equal(t, tc.code, result.StatusCode) - -			if tc.header != nil { -				assert.Equal(t, tc.header, result.Header) -			} - -			if tc.headerFunc != nil { -				tc.headerFunc(t, result.Header) -			} - -			data, err := io.ReadAll(result.Body) -			require.NoError(t, err) - -			if tc.bodyRegexp == nil { -				assert.Equal(t, tc.body, string(data)) -			} else { -				assert.Regexp(t, tc.bodyRegexp, string(data)) -			} -		}) -	} -} diff --git a/internal/oidc/session.go b/internal/oidc/session.go index 7b283bfd7..2faaa3f3a 100644 --- a/internal/oidc/session.go +++ b/internal/oidc/session.go @@ -4,11 +4,11 @@ import (  	"net/url"  	"time" +	oauthelia2 "authelia.com/provider/oauth2" +	"authelia.com/provider/oauth2/handler/openid" +	"authelia.com/provider/oauth2/token/jwt"  	"github.com/google/uuid"  	"github.com/mohae/deepcopy" -	"github.com/ory/fosite" -	"github.com/ory/fosite/handler/openid" -	"github.com/ory/fosite/token/jwt"  	"github.com/authelia/authelia/v4/internal/model"  ) @@ -30,7 +30,7 @@ func NewSession() (session *Session) {  // NewSessionWithAuthorizeRequest uses details from an AuthorizeRequester to generate an OpenIDSession.  func NewSessionWithAuthorizeRequest(ctx Context, issuer *url.URL, kid, username string, amr []string, extra map[string]any, -	authTime time.Time, consent *model.OAuth2ConsentSession, requester fosite.AuthorizeRequester) (session *Session) { +	authTime time.Time, consent *model.OAuth2ConsentSession, requester oauthelia2.AuthorizeRequester) (session *Session) {  	if extra == nil {  		extra = map[string]any{}  	} @@ -126,7 +126,7 @@ func (s *Session) GetJWTClaims() jwt.JWTClaimsContainer {  	claims := &jwt.JWTClaims{  		Subject:   s.Subject, -		ExpiresAt: s.GetExpiresAt(fosite.AccessToken), +		ExpiresAt: s.GetExpiresAt(oauthelia2.AccessToken),  		IssuedAt:  time.Now().UTC(),  		Extra:     map[string]any{},  	} @@ -174,11 +174,11 @@ func (s *Session) GetExtraClaims() map[string]any {  	return s.Extra  } -// Clone copies the OpenIDSession to a new fosite.Session. -func (s *Session) Clone() fosite.Session { +// Clone copies the OpenIDSession to a new oauthelia2.Session. +func (s *Session) Clone() oauthelia2.Session {  	if s == nil {  		return nil  	} -	return deepcopy.Copy(s).(fosite.Session) +	return deepcopy.Copy(s).(oauthelia2.Session)  } diff --git a/internal/oidc/session_test.go b/internal/oidc/session_test.go index 905e76d1f..088d17953 100644 --- a/internal/oidc/session_test.go +++ b/internal/oidc/session_test.go @@ -4,8 +4,8 @@ import (  	"testing"  	"time" -	"github.com/ory/fosite/handler/openid" -	"github.com/ory/fosite/token/jwt" +	"authelia.com/provider/oauth2/handler/openid" +	"authelia.com/provider/oauth2/token/jwt"  	"github.com/stretchr/testify/assert"  	"github.com/stretchr/testify/require" diff --git a/internal/oidc/store.go b/internal/oidc/store.go index 8d4e6bf45..487e8a47d 100644 --- a/internal/oidc/store.go +++ b/internal/oidc/store.go @@ -8,8 +8,8 @@ import (  	"fmt"  	"time" +	oauthelia2 "authelia.com/provider/oauth2"  	"github.com/google/uuid" -	"github.com/ory/fosite"  	"github.com/authelia/authelia/v4/internal/authorization"  	"github.com/authelia/authelia/v4/internal/configuration/schema" @@ -65,11 +65,11 @@ func (s *Store) GetSubject(ctx context.Context, sectorID, username string) (subj  	return opaqueID.Identifier, nil  } -// GetFullClient returns a fosite.Client asserted as an Client matching the provided id. +// GetFullClient returns a oauthelia2.Client asserted as an Client matching the provided id.  func (s *Store) GetFullClient(_ context.Context, id string) (client Client, err error) {  	client, ok := s.clients[id]  	if !ok { -		return nil, fosite.ErrInvalidClient.WithDebugf("Client with id '%s' does not appear to be a registered client.", id) +		return nil, oauthelia2.ErrInvalidClient.WithDebugf("Client with id '%s' does not appear to be a registered client.", id)  	}  	return client, nil @@ -101,13 +101,13 @@ func (s *Store) Rollback(ctx context.Context) (err error) {  }  // GetClient loads the client by its ID or returns an error if the client does not exist or another error occurred. -// This implements a portion of fosite.ClientManager. -func (s *Store) GetClient(ctx context.Context, id string) (client fosite.Client, err error) { +// This implements a portion of oauthelia2.ClientManager. +func (s *Store) GetClient(ctx context.Context, id string) (client oauthelia2.Client, err error) {  	return s.GetFullClient(ctx, id)  }  // ClientAssertionJWTValid returns an error if the JTI is known or the DB check failed and nil if the JTI is not known. -// This implements a portion of fosite.ClientManager. +// This implements a portion of oauthelia2.ClientManager.  func (s *Store) ClientAssertionJWTValid(ctx context.Context, jti string) (err error) {  	signature := fmt.Sprintf("%x", sha256.Sum256([]byte(jti))) @@ -119,7 +119,7 @@ func (s *Store) ClientAssertionJWTValid(ctx context.Context, jti string) (err er  	case err != nil:  		return err  	case blacklistedJTI.ExpiresAt.After(time.Now()): -		return fosite.ErrJTIKnown +		return oauthelia2.ErrJTIKnown  	default:  		return nil  	} @@ -127,7 +127,7 @@ func (s *Store) ClientAssertionJWTValid(ctx context.Context, jti string) (err er  // SetClientAssertionJWT marks a JTI as known for the given expiry time. Before inserting the new JTI, it will clean  // up any existing JTIs that have expired as those tokens can not be replayed due to the expiry. -// This implements a portion of fosite.ClientManager. +// This implements a portion of oauthelia2.ClientManager.  func (s *Store) SetClientAssertionJWT(ctx context.Context, jti string, exp time.Time) (err error) {  	blacklistedJTI := model.NewOAuth2BlacklistedJTI(jti, exp) @@ -136,7 +136,7 @@ func (s *Store) SetClientAssertionJWT(ctx context.Context, jti string, exp time.  // CreateAuthorizeCodeSession stores the authorization request for a given authorization code.  // This implements a portion of oauth2.AuthorizeCodeStorage. -func (s *Store) CreateAuthorizeCodeSession(ctx context.Context, code string, request fosite.Requester) (err error) { +func (s *Store) CreateAuthorizeCodeSession(ctx context.Context, code string, request oauthelia2.Requester) (err error) {  	return s.saveSession(ctx, storage.OAuth2SessionTypeAuthorizeCode, code, request)  } @@ -151,16 +151,15 @@ func (s *Store) InvalidateAuthorizeCodeSession(ctx context.Context, code string)  // GetAuthorizeCodeSession hydrates the session based on the given code and returns the authorization request.  // If the authorization code has been invalidated with `InvalidateAuthorizeCodeSession`, this  // method should return the ErrInvalidatedAuthorizeCode error. -// Make sure to also return the fosite.Requester value when returning the fosite.ErrInvalidatedAuthorizeCode error! +// Make sure to also return the oauthelia2.Requester value when returning the oauthelia2.ErrInvalidatedAuthorizeCode error!  // This implements a portion of oauth2.AuthorizeCodeStorage. -func (s *Store) GetAuthorizeCodeSession(ctx context.Context, code string, session fosite.Session) (request fosite.Requester, err error) { -	// TODO: Implement the fosite.ErrInvalidatedAuthorizeCode error above. This requires splitting the invalidated sessions and deleted sessions. +func (s *Store) GetAuthorizeCodeSession(ctx context.Context, code string, session oauthelia2.Session) (request oauthelia2.Requester, err error) {  	return s.loadRequesterBySignature(ctx, storage.OAuth2SessionTypeAuthorizeCode, code, session)  }  // CreateAccessTokenSession stores the authorization request for a given access token.  // This implements a portion of oauth2.AccessTokenStorage. -func (s *Store) CreateAccessTokenSession(ctx context.Context, signature string, request fosite.Requester) (err error) { +func (s *Store) CreateAccessTokenSession(ctx context.Context, signature string, request oauthelia2.Requester) (err error) {  	return s.saveSession(ctx, storage.OAuth2SessionTypeAccessToken, signature, request)  } @@ -179,13 +178,13 @@ func (s *Store) RevokeAccessToken(ctx context.Context, requestID string) (err er  // GetAccessTokenSession gets the authorization request for a given access token.  // This implements a portion of oauth2.AccessTokenStorage. -func (s *Store) GetAccessTokenSession(ctx context.Context, signature string, session fosite.Session) (request fosite.Requester, err error) { +func (s *Store) GetAccessTokenSession(ctx context.Context, signature string, session oauthelia2.Session) (request oauthelia2.Requester, err error) {  	return s.loadRequesterBySignature(ctx, storage.OAuth2SessionTypeAccessToken, signature, session)  }  // CreateRefreshTokenSession stores the authorization request for a given refresh token.  // This implements a portion of oauth2.RefreshTokenStorage. -func (s *Store) CreateRefreshTokenSession(ctx context.Context, signature string, request fosite.Requester) (err error) { +func (s *Store) CreateRefreshTokenSession(ctx context.Context, signature string, request oauthelia2.Requester) (err error) {  	return s.saveSession(ctx, storage.OAuth2SessionTypeRefreshToken, signature, request)  } @@ -212,13 +211,13 @@ func (s *Store) RevokeRefreshTokenMaybeGracePeriod(ctx context.Context, requestI  // GetRefreshTokenSession gets the authorization request for a given refresh token.  // This implements a portion of oauth2.RefreshTokenStorage. -func (s *Store) GetRefreshTokenSession(ctx context.Context, signature string, session fosite.Session) (request fosite.Requester, err error) { +func (s *Store) GetRefreshTokenSession(ctx context.Context, signature string, session oauthelia2.Session) (request oauthelia2.Requester, err error) {  	return s.loadRequesterBySignature(ctx, storage.OAuth2SessionTypeRefreshToken, signature, session)  }  // CreatePKCERequestSession stores the authorization request for a given PKCE request.  // This implements a portion of pkce.PKCERequestStorage. -func (s *Store) CreatePKCERequestSession(ctx context.Context, signature string, request fosite.Requester) (err error) { +func (s *Store) CreatePKCERequestSession(ctx context.Context, signature string, request oauthelia2.Requester) (err error) {  	return s.saveSession(ctx, storage.OAuth2SessionTypePKCEChallenge, signature, request)  } @@ -230,14 +229,14 @@ func (s *Store) DeletePKCERequestSession(ctx context.Context, signature string)  // GetPKCERequestSession gets the authorization request for a given PKCE request.  // This implements a portion of pkce.PKCERequestStorage. -func (s *Store) GetPKCERequestSession(ctx context.Context, signature string, session fosite.Session) (requester fosite.Requester, err error) { +func (s *Store) GetPKCERequestSession(ctx context.Context, signature string, session oauthelia2.Session) (requester oauthelia2.Requester, err error) {  	return s.loadRequesterBySignature(ctx, storage.OAuth2SessionTypePKCEChallenge, signature, session)  }  // CreateOpenIDConnectSession creates an OpenID Connect 1.0 connect session for a given authorize code.  // This is relevant for explicit OpenID Connect 1.0 flow.  // This implements a portion of openid.OpenIDConnectRequestStorage. -func (s *Store) CreateOpenIDConnectSession(ctx context.Context, authorizeCode string, request fosite.Requester) (err error) { +func (s *Store) CreateOpenIDConnectSession(ctx context.Context, authorizeCode string, request oauthelia2.Requester) (err error) {  	return s.saveSession(ctx, storage.OAuth2SessionTypeOpenIDConnect, authorizeCode, request)  } @@ -252,13 +251,13 @@ func (s *Store) DeleteOpenIDConnectSession(ctx context.Context, authorizeCode st  // - ErrNoSessionFound if no session was found  // - or an arbitrary error if an error occurred.  // This implements a portion of openid.OpenIDConnectRequestStorage. -func (s *Store) GetOpenIDConnectSession(ctx context.Context, authorizeCode string, request fosite.Requester) (r fosite.Requester, err error) { +func (s *Store) GetOpenIDConnectSession(ctx context.Context, authorizeCode string, request oauthelia2.Requester) (r oauthelia2.Requester, err error) {  	return s.loadRequesterBySignature(ctx, storage.OAuth2SessionTypeOpenIDConnect, authorizeCode, request.GetSession())  }  // CreatePARSession stores the pushed authorization request context. The requestURI is used to derive the key. -// This implements a portion of fosite.PARStorage. -func (s *Store) CreatePARSession(ctx context.Context, requestURI string, request fosite.AuthorizeRequester) (err error) { +// This implements a portion of oauthelia2.PARStorage. +func (s *Store) CreatePARSession(ctx context.Context, requestURI string, request oauthelia2.AuthorizeRequester) (err error) {  	var par *model.OAuth2PARContext  	if par, err = model.NewOAuth2PARContext(requestURI, request); err != nil { @@ -269,8 +268,8 @@ func (s *Store) CreatePARSession(ctx context.Context, requestURI string, request  }  // GetPARSession gets the push authorization request context. The caller is expected to merge the AuthorizeRequest. -// This implements a portion of fosite.PARStorage. -func (s *Store) GetPARSession(ctx context.Context, requestURI string) (request fosite.AuthorizeRequester, err error) { +// This implements a portion of oauthelia2.PARStorage. +func (s *Store) GetPARSession(ctx context.Context, requestURI string) (request oauthelia2.AuthorizeRequester, err error) {  	var par *model.OAuth2PARContext  	if par, err = s.provider.LoadOAuth2PARContext(ctx, requestURI); err != nil { @@ -281,7 +280,7 @@ func (s *Store) GetPARSession(ctx context.Context, requestURI string) (request f  }  // DeletePARSession deletes the context. -// This implements a portion of fosite.PARStorage. +// This implements a portion of oauthelia2.PARStorage.  func (s *Store) DeletePARSession(ctx context.Context, requestURI string) (err error) {  	return s.provider.RevokeOAuth2PARContext(ctx, requestURI)  } @@ -300,7 +299,7 @@ func (s *Store) MarkJWTUsedForTime(ctx context.Context, jti string, exp time.Tim  	return s.SetClientAssertionJWT(ctx, jti, exp)  } -func (s *Store) loadRequesterBySignature(ctx context.Context, sessionType storage.OAuth2SessionType, signature string, session fosite.Session) (r fosite.Requester, err error) { +func (s *Store) loadRequesterBySignature(ctx context.Context, sessionType storage.OAuth2SessionType, signature string, session oauthelia2.Session) (r oauthelia2.Requester, err error) {  	var (  		sessionModel *model.OAuth2Session  	) @@ -309,7 +308,7 @@ func (s *Store) loadRequesterBySignature(ctx context.Context, sessionType storag  	if err != nil {  		switch {  		case errors.Is(err, sql.ErrNoRows): -			return nil, fosite.ErrNotFound +			return nil, oauthelia2.ErrNotFound  		default:  			return nil, err  		} @@ -322,16 +321,16 @@ func (s *Store) loadRequesterBySignature(ctx context.Context, sessionType storag  	if !sessionModel.Active {  		switch sessionType {  		case storage.OAuth2SessionTypeAuthorizeCode: -			return r, fosite.ErrInvalidatedAuthorizeCode +			return r, oauthelia2.ErrInvalidatedAuthorizeCode  		default: -			return r, fosite.ErrInactiveToken +			return r, oauthelia2.ErrInactiveToken  		}  	}  	return r, nil  } -func (s *Store) saveSession(ctx context.Context, sessionType storage.OAuth2SessionType, signature string, r fosite.Requester) (err error) { +func (s *Store) saveSession(ctx context.Context, sessionType storage.OAuth2SessionType, signature string, r oauthelia2.Requester) (err error) {  	var session *model.OAuth2Session  	if session, err = model.NewOAuth2SessionFromRequest(signature, r); err != nil { @@ -349,7 +348,7 @@ func (s *Store) revokeSessionByRequestID(ctx context.Context, sessionType storag  	if err = s.provider.RevokeOAuth2SessionByRequestID(ctx, sessionType, requestID); err != nil {  		switch {  		case errors.Is(err, sql.ErrNoRows): -			return fosite.ErrNotFound +			return oauthelia2.ErrNotFound  		default:  			return err  		} diff --git a/internal/oidc/store_test.go b/internal/oidc/store_test.go index 338995ba8..df370c145 100644 --- a/internal/oidc/store_test.go +++ b/internal/oidc/store_test.go @@ -8,7 +8,7 @@ import (  	"testing"  	"time" -	"github.com/ory/fosite" +	oauthelia2 "authelia.com/provider/oauth2"  	"github.com/stretchr/testify/assert"  	"github.com/stretchr/testify/require"  	"github.com/stretchr/testify/suite" @@ -71,12 +71,12 @@ func TestOpenIDConnectStore_GetInternalClient_ValidClient(t *testing.T) {  	require.NotNil(t, client)  	assert.Equal(t, id, client.GetID())  	assert.Equal(t, myclientdesc, client.GetName()) -	assert.Equal(t, fosite.Arguments(c1.Scopes), client.GetScopes()) -	assert.Equal(t, fosite.Arguments([]string{oidc.GrantTypeAuthorizationCode}), client.GetGrantTypes()) -	assert.Equal(t, fosite.Arguments([]string{oidc.ResponseTypeAuthorizationCodeFlow}), client.GetResponseTypes()) +	assert.Equal(t, oauthelia2.Arguments(c1.Scopes), client.GetScopes()) +	assert.Equal(t, oauthelia2.Arguments([]string{oidc.GrantTypeAuthorizationCode}), client.GetGrantTypes()) +	assert.Equal(t, oauthelia2.Arguments([]string{oidc.ResponseTypeAuthorizationCodeFlow}), client.GetResponseTypes())  	assert.Equal(t, []string(nil), client.GetRedirectURIs())  	assert.Equal(t, authorization.OneFactor, client.GetAuthorizationPolicyRequiredLevel(authorization.Subject{})) -	assert.Equal(t, "$plaintext$client-secret", client.GetSecret().Encode()) +	assert.Equal(t, "$plaintext$client-secret", client.GetClientSecret().(*oidc.ClientSecretDigest).Encode())  }  func TestOpenIDConnectStore_GetInternalClient_InvalidClient(t *testing.T) { @@ -295,7 +295,7 @@ func (s *StoreSuite) TestCreateSessions() {  			Return(nil),  	) -	s.NoError(s.store.CreateAuthorizeCodeSession(s.ctx, abc, &fosite.Request{ +	s.NoError(s.store.CreateAuthorizeCodeSession(s.ctx, abc, &oauthelia2.Request{  		ID: abc,  		Client: &oidc.RegisteredClient{  			ID: "example", @@ -303,7 +303,7 @@ func (s *StoreSuite) TestCreateSessions() {  		Session: session,  	})) -	s.EqualError(s.store.CreateAuthorizeCodeSession(s.ctx, abc, &fosite.Request{ +	s.EqualError(s.store.CreateAuthorizeCodeSession(s.ctx, abc, &oauthelia2.Request{  		ID: abc,  		Client: &oidc.RegisteredClient{  			ID: "example", @@ -311,7 +311,7 @@ func (s *StoreSuite) TestCreateSessions() {  		Session: session,  	}), "duplicate key") -	s.EqualError(s.store.CreateAuthorizeCodeSession(s.ctx, abc, &fosite.Request{ +	s.EqualError(s.store.CreateAuthorizeCodeSession(s.ctx, abc, &oauthelia2.Request{  		ID: abc,  		Client: &oidc.RegisteredClient{  			ID: "example", @@ -319,7 +319,7 @@ func (s *StoreSuite) TestCreateSessions() {  		Session: nil,  	}), "failed to create new *model.OAuth2Session: the session type OpenIDSession was expected but the type '<nil>' was used") -	s.NoError(s.store.CreateAccessTokenSession(s.ctx, abc, &fosite.Request{ +	s.NoError(s.store.CreateAccessTokenSession(s.ctx, abc, &oauthelia2.Request{  		ID: abc,  		Client: &oidc.RegisteredClient{  			ID: "example", @@ -327,7 +327,7 @@ func (s *StoreSuite) TestCreateSessions() {  		Session: session,  	})) -	s.NoError(s.store.CreateRefreshTokenSession(s.ctx, abc, &fosite.Request{ +	s.NoError(s.store.CreateRefreshTokenSession(s.ctx, abc, &oauthelia2.Request{  		ID: abc,  		Client: &oidc.RegisteredClient{  			ID: "example", @@ -335,7 +335,7 @@ func (s *StoreSuite) TestCreateSessions() {  		Session: session,  	})) -	s.NoError(s.store.CreateOpenIDConnectSession(s.ctx, abc, &fosite.Request{ +	s.NoError(s.store.CreateOpenIDConnectSession(s.ctx, abc, &oauthelia2.Request{  		ID: abc,  		Client: &oidc.RegisteredClient{  			ID: "example", @@ -343,7 +343,7 @@ func (s *StoreSuite) TestCreateSessions() {  		Session: session,  	})) -	s.NoError(s.store.CreatePKCERequestSession(s.ctx, abc, &fosite.Request{ +	s.NoError(s.store.CreatePKCERequestSession(s.ctx, abc, &oauthelia2.Request{  		ID: abc,  		Client: &oidc.RegisteredClient{  			ID: "example", @@ -351,8 +351,8 @@ func (s *StoreSuite) TestCreateSessions() {  		Session: session,  	})) -	s.NoError(s.store.CreatePARSession(s.ctx, abc, &fosite.AuthorizeRequest{ -		Request: fosite.Request{ +	s.NoError(s.store.CreatePARSession(s.ctx, abc, &oauthelia2.AuthorizeRequest{ +		Request: oauthelia2.Request{  			ID: abc,  			Client: &oidc.RegisteredClient{  				ID: "example", @@ -360,8 +360,8 @@ func (s *StoreSuite) TestCreateSessions() {  			Session: session,  		}})) -	s.EqualError(s.store.CreatePARSession(s.ctx, abc, &fosite.AuthorizeRequest{ -		Request: fosite.Request{ +	s.EqualError(s.store.CreatePARSession(s.ctx, abc, &oauthelia2.AuthorizeRequest{ +		Request: oauthelia2.Request{  			ID: abc,  			Client: &oidc.RegisteredClient{  				ID: "example", @@ -533,7 +533,7 @@ func (s *StoreSuite) TestGetSessions() {  	)  	var ( -		r   fosite.Requester +		r   oauthelia2.Requester  		err error  	) @@ -569,7 +569,7 @@ func (s *StoreSuite) TestGetSessions() {  	s.NotNil(r)  	s.NoError(err) -	r, err = s.store.GetOpenIDConnectSession(s.ctx, "ot", &fosite.Request{ +	r, err = s.store.GetOpenIDConnectSession(s.ctx, "ot", &oauthelia2.Request{  		ID: abc,  		Client: &oidc.RegisteredClient{  			ID: "example", diff --git a/internal/oidc/types.go b/internal/oidc/types.go index 35e9e74f2..dbdd09860 100644 --- a/internal/oidc/types.go +++ b/internal/oidc/types.go @@ -6,11 +6,10 @@ import (  	"net/url"  	"time" -	"github.com/go-crypt/crypt/algorithm" -	"github.com/go-jose/go-jose/v3" +	oauthelia2 "authelia.com/provider/oauth2" +	fjwt "authelia.com/provider/oauth2/token/jwt" +	"github.com/go-jose/go-jose/v4"  	"github.com/golang-jwt/jwt/v5" -	"github.com/ory/fosite" -	fjwt "github.com/ory/fosite/token/jwt"  	"github.com/ory/herodot"  	"github.com/authelia/authelia/v4/internal/authentication" @@ -23,7 +22,7 @@ import (  // OpenIDConnectProvider for OpenID Connect.  type OpenIDConnectProvider struct { -	fosite.OAuth2Provider +	oauthelia2.Provider  	*herodot.JSONWriter  	*Store  	*Config @@ -33,9 +32,9 @@ type OpenIDConnectProvider struct {  	discovery OpenIDConnectWellKnownConfiguration  } -// Store is Authelia's internal representation of the fosite.Storage interface. It maps the following +// Store is Authelia's internal representation of the oauthelia2.Storage interface. It maps the following  // interfaces to the storage.Provider interface: -// fosite.Storage, fosite.ClientManager, storage.Transactional, oauth2.AuthorizeCodeStorage, oauth2.AccessTokenStorage, +// oauthelia2.Storage, oauthelia2.ClientManager, storage.Transactional, oauth2.AuthorizeCodeStorage, oauth2.AccessTokenStorage,  // oauth2.RefreshTokenStorage, oauth2.TokenRevocationStorage, pkce.PKCERequestStorage,  // openid.OpenIDConnectRequestStorage, and partially implements rfc7523.RFC7523KeyStorage.  type Store struct { @@ -45,11 +44,12 @@ type Store struct {  // RegisteredClient represents a registered client.  type RegisteredClient struct { -	ID                  string -	Name                string -	Secret              *schema.PasswordDigest -	SectorIdentifierURI *url.URL -	Public              bool +	ID                   string +	Name                 string +	ClientSecret         *ClientSecretDigest +	RotatedClientSecrets []*ClientSecretDigest +	SectorIdentifierURI  *url.URL +	Public               bool  	RequirePushedAuthorizationRequests bool @@ -62,45 +62,54 @@ type RegisteredClient struct {  	RedirectURIs  []string  	GrantTypes    []string  	ResponseTypes []string -	ResponseModes []fosite.ResponseModeType +	ResponseModes []oauthelia2.ResponseModeType  	Lifespans schema.IdentityProvidersOpenIDConnectLifespan -	AuthorizationSignedResponseAlg   string -	AuthorizationSignedResponseKeyID string -	IDTokenSignedResponseAlg         string -	IDTokenSignedResponseKeyID       string -	AccessTokenSignedResponseAlg     string -	AccessTokenSignedResponseKeyID   string -	UserinfoSignedResponseAlg        string -	UserinfoSignedResponseKeyID      string +	AuthorizationSignedResponseAlg              string +	AuthorizationSignedResponseKeyID            string +	AuthorizationEncryptedResponseAlg           string +	AuthorizationEncryptedResponseEncryptionAlg string + +	IDTokenSignedResponseAlg   string +	IDTokenSignedResponseKeyID string + +	AccessTokenSignedResponseAlg   string +	AccessTokenSignedResponseKeyID string + +	UserinfoSignedResponseAlg   string +	UserinfoSignedResponseKeyID string +  	IntrospectionSignedResponseAlg   string  	IntrospectionSignedResponseKeyID string -	RefreshFlowIgnoreOriginalGrantedScopes bool +	RequestObjectSigningAlg string + +	TokenEndpointAuthMethod     string +	TokenEndpointAuthSigningAlg string + +	RefreshFlowIgnoreOriginalGrantedScopes  bool +	AllowMultipleAuthenticationMethods      bool +	ClientCredentialsFlowAllowImplicitScope bool  	AuthorizationPolicy ClientAuthorizationPolicy  	ConsentPolicy         ClientConsentPolicy  	RequestedAudienceMode ClientRequestedAudienceMode -	RequestURIs                 []string -	JSONWebKeys                 *jose.JSONWebKeySet -	JSONWebKeysURI              *url.URL -	RequestObjectSigningAlg     string -	TokenEndpointAuthMethod     string -	TokenEndpointAuthSigningAlg string +	RequestURIs    []string +	JSONWebKeys    *jose.JSONWebKeySet +	JSONWebKeysURI *url.URL  }  // Client represents the internal client definitions.  type Client interface { -	fosite.Client -	fosite.ResponseModeClient +	oauthelia2.Client +	oauthelia2.ResponseModeClient  	RefreshFlowScopeClient  	GetName() (name string) -	GetSecret() (secret algorithm.Digest) -	GetSectorIdentifier() (sector string) +	GetSectorIdentifierURI() (sector string)  	GetAuthorizationSignedResponseAlg() (alg string)  	GetAuthorizationSignedResponseKeyID() (kid string) @@ -110,7 +119,7 @@ type Client interface {  	GetAccessTokenSignedResponseAlg() (alg string)  	GetAccessTokenSignedResponseKeyID() (kid string) -	GetJWTProfileOAuthAccessTokensEnabled() bool +	GetEnableJWTProfileOAuthAccessTokens() bool  	GetUserinfoSignedResponseAlg() (alg string)  	GetUserinfoSignedResponseKeyID() (kid string) @@ -119,27 +128,25 @@ type Client interface {  	GetIntrospectionSignedResponseKeyID() (kid string)  	GetRequirePushedAuthorizationRequests() (enforce bool) -	GetPKCEEnforcement() (enforce bool) -	GetPKCEChallengeMethodEnforcement() (enforce bool) + +	GetEnforcePKCE() (enforce bool) +	GetEnforcePKCEChallengeMethod() (enforce bool)  	GetPKCEChallengeMethod() (method string) -	ValidatePKCEPolicy(r fosite.Requester) (err error) -	ValidatePARPolicy(r fosite.Requester, prefix string) (err error) -	ValidateResponseModePolicy(r fosite.AuthorizeRequester) (err error) +	ValidateResponseModePolicy(r oauthelia2.AuthorizeRequester) (err error) -	ApplyRequestedAudiencePolicy(requester fosite.Requester)  	GetConsentResponseBody(consent *model.OAuth2ConsentSession) (body ConsentGetResponseBody)  	GetConsentPolicy() ClientConsentPolicy  	IsAuthenticationLevelSufficient(level authentication.Level, subject authorization.Subject) (sufficient bool)  	GetAuthorizationPolicyRequiredLevel(subject authorization.Subject) (level authorization.Level)  	GetAuthorizationPolicy() (policy ClientAuthorizationPolicy) -	GetEffectiveLifespan(gt fosite.GrantType, tt fosite.TokenType, fallback time.Duration) (lifespan time.Duration) +	GetEffectiveLifespan(gt oauthelia2.GrantType, tt oauthelia2.TokenType, fallback time.Duration) (lifespan time.Duration)  }  // RefreshFlowScopeClient is a client which can be customized to ignore scopes that were not originally granted.  type RefreshFlowScopeClient interface { -	fosite.Client +	oauthelia2.Client  	GetRefreshFlowIgnoreOriginalGrantedScopes(ctx context.Context) (ignoreOriginalGrantedScopes bool)  } @@ -154,9 +161,9 @@ type Context interface {  	GetJWTWithTimeFuncOption() jwt.ParserOption  } -// ClientRequesterResponder is a fosite.Requster or fosite.Responder with a GetClient method. +// ClientRequesterResponder is a oauthelia2.Requster or fosite.Responder with a GetClient method.  type ClientRequesterResponder interface { -	GetClient() fosite.Client +	GetClient() oauthelia2.Client  }  // IDTokenClaimsSession is a session which can return the IDTokenClaims type. @@ -164,12 +171,11 @@ type IDTokenClaimsSession interface {  	GetIDTokenClaims() *fjwt.IDTokenClaims  } -// Configurator is an internal extension to the fosite.Configurator. +// Configurator is an internal extension to the oauthelia2.Configurator.  type Configurator interface { -	fosite.Configurator +	oauthelia2.Configurator  	AuthorizationServerIssuerIdentificationProvider -	JWTSecuredResponseModeProvider  }  // AuthorizationServerIssuerIdentificationProvider provides OAuth 2.0 Authorization Server Issuer Identification related methods. @@ -945,3 +951,20 @@ type OpenIDConnectWellKnownClaims struct {  	jwt.RegisteredClaims  } + +var ( +	_ Client                                                       = (*RegisteredClient)(nil) +	_ oauthelia2.Client                                            = (*RegisteredClient)(nil) +	_ oauthelia2.RotatedClientSecretsClient                        = (*RegisteredClient)(nil) +	_ oauthelia2.ProofKeyCodeExchangeClient                        = (*RegisteredClient)(nil) +	_ oauthelia2.ClientAuthenticationPolicyClient                  = (*RegisteredClient)(nil) +	_ oauthelia2.OpenIDConnectClient                               = (*RegisteredClient)(nil) +	_ oauthelia2.RefreshFlowScopeClient                            = (*RegisteredClient)(nil) +	_ oauthelia2.RevokeFlowRevokeRefreshTokensExplicitClient       = (*RegisteredClient)(nil) +	_ oauthelia2.JARMClient                                        = (*RegisteredClient)(nil) +	_ oauthelia2.PushedAuthorizationRequestClient                  = (*RegisteredClient)(nil) +	_ oauthelia2.ResponseModeClient                                = (*RegisteredClient)(nil) +	_ oauthelia2.ClientCredentialsFlowRequestedScopeImplicitClient = (*RegisteredClient)(nil) +	_ oauthelia2.RequestedAudienceImplicitClient                   = (*RegisteredClient)(nil) +	_ oauthelia2.JWTProfileClient                                  = (*RegisteredClient)(nil) +) diff --git a/internal/oidc/types_test.go b/internal/oidc/types_test.go index f622d9c9e..8598c49dc 100644 --- a/internal/oidc/types_test.go +++ b/internal/oidc/types_test.go @@ -6,9 +6,9 @@ import (  	"testing"  	"time" +	oauthelia2 "authelia.com/provider/oauth2"  	"github.com/golang-jwt/jwt/v5"  	"github.com/google/uuid" -	"github.com/ory/fosite"  	"github.com/stretchr/testify/assert"  	"github.com/stretchr/testify/require" @@ -40,8 +40,8 @@ func TestNewSessionWithAuthorizeRequest(t *testing.T) {  	formValues.Set(oidc.ClaimNonce, "abc123xyzauthelia") -	request := &fosite.AuthorizeRequest{ -		Request: fosite.Request{ +	request := &oauthelia2.AuthorizeRequest{ +		Request: oauthelia2.Request{  			ID:     requestID.String(),  			Form:   formValues,  			Client: &oidc.RegisteredClient{ID: "example"}, @@ -156,10 +156,10 @@ func (m *TestCodeStrategy) AuthorizeCodeSignature(ctx context.Context, token str  	return m.signature  } -func (m *TestCodeStrategy) GenerateAuthorizeCode(ctx context.Context, requester fosite.Requester) (token string, signature string, err error) { +func (m *TestCodeStrategy) GenerateAuthorizeCode(ctx context.Context, requester oauthelia2.Requester) (token string, signature string, err error) {  	return "", "", nil  } -func (m *TestCodeStrategy) ValidateAuthorizeCode(ctx context.Context, requester fosite.Requester, token string) (err error) { +func (m *TestCodeStrategy) ValidateAuthorizeCode(ctx context.Context, requester oauthelia2.Requester, token string) (err error) {  	return nil  } diff --git a/internal/oidc/util.go b/internal/oidc/util.go index 978a8259c..5ab3a14ed 100644 --- a/internal/oidc/util.go +++ b/internal/oidc/util.go @@ -3,32 +3,33 @@ package oidc  import (  	"errors"  	"fmt" +	"net/url"  	"sort"  	"strings"  	"time" -	"github.com/go-jose/go-jose/v3" +	oauthelia2 "authelia.com/provider/oauth2" +	fjwt "authelia.com/provider/oauth2/token/jwt" +	"github.com/go-jose/go-jose/v4"  	"github.com/golang-jwt/jwt/v5" -	"github.com/ory/fosite" -	fjwt "github.com/ory/fosite/token/jwt"  	"github.com/ory/x/errorsx"  	"golang.org/x/text/language"  )  // IsPushedAuthorizedRequest returns true if the requester has a PushedAuthorizationRequest redirect_uri value. -func IsPushedAuthorizedRequest(r fosite.Requester, prefix string) bool { +func IsPushedAuthorizedRequest(r oauthelia2.Requester, prefix string) bool {  	return strings.HasPrefix(r.GetRequestForm().Get(FormParameterRequestURI), prefix)  } -// MatchScopes uses a fosite.ScopeStrategy to check if scopes match. -func MatchScopes(strategy fosite.ScopeStrategy, granted, scopes []string) error { +// MatchScopes uses a oauthelia2.ScopeStrategy to check if scopes match. +func MatchScopes(strategy oauthelia2.ScopeStrategy, granted, scopes []string) error {  	for _, scope := range scopes {  		if scope == "" {  			continue  		}  		if !strategy(granted, scope) { -			return errorsx.WithStack(fosite.ErrInvalidScope.WithHintf("The request scope '%s' has not been granted or is not allowed to be requested.", scope)) +			return errorsx.WithStack(oauthelia2.ErrInvalidScope.WithHintf("The request scope '%s' has not been granted or is not allowed to be requested.", scope))  		}  	} @@ -137,22 +138,14 @@ func JTIFromMapClaims(m jwt.MapClaims) (jti string, err error) {  	return jti, nil  } -func getExpiresIn(r fosite.Requester, key fosite.TokenType, defaultLifespan time.Duration, now time.Time) time.Duration { -	if r.GetSession().GetExpiresAt(key).IsZero() { -		return defaultLifespan -	} - -	return time.Duration(r.GetSession().GetExpiresAt(key).UnixNano() - now.UnixNano()) -} -  // ErrorToDebugRFC6749Error converts the provided error to a *DebugRFC6749Error provided it is not nil and can be -// cast as a *fosite.RFC6749Error. +// cast as a *oauthelia2.RFC6749Error.  func ErrorToDebugRFC6749Error(err error) (rfc error) {  	if err == nil {  		return nil  	} -	var e *fosite.RFC6749Error +	var e *oauthelia2.RFC6749Error  	if errors.As(err, &e) {  		return &DebugRFC6749Error{e} @@ -161,10 +154,10 @@ func ErrorToDebugRFC6749Error(err error) (rfc error) {  	return err  } -// DebugRFC6749Error is a decorator type which makes the underlying *fosite.RFC6749Error expose debug information and +// DebugRFC6749Error is a decorator type which makes the underlying *oauthelia2.RFC6749Error expose debug information and  // show the full error description.  type DebugRFC6749Error struct { -	*fosite.RFC6749Error +	*oauthelia2.RFC6749Error  }  // Error implements the builtin error interface and shows the error with its debug info and description. @@ -173,142 +166,19 @@ func (err *DebugRFC6749Error) Error() string {  }  // GetLangFromRequester gets the expected language for a requester. -func GetLangFromRequester(requester fosite.Requester) language.Tag { +func GetLangFromRequester(requester oauthelia2.Requester) language.Tag {  	var ( -		ctx fosite.G11NContext +		ctx oauthelia2.G11NContext  		ok  bool  	) -	if ctx, ok = requester.(fosite.G11NContext); ok { +	if ctx, ok = requester.(oauthelia2.G11NContext); ok {  		return ctx.GetLang()  	}  	return language.English  } -// IntrospectionResponseToMap converts a fosite.IntrospectionResponder into a map[string]any which is used to either -// respond to the introspection request with JSON or a JWT. -func IntrospectionResponseToMap(response fosite.IntrospectionResponder) (aud []string, introspection map[string]any) { -	introspection = map[string]any{ -		ClaimActive: false, -	} - -	if response == nil { -		return nil, introspection -	} - -	if response.IsActive() { -		introspection[ClaimActive] = true - -		mapIntrospectionAccessRequesterToMap(response.GetAccessRequester(), introspection) -	} - -	return sliceIntrospectionResponseToRequesterAudience(response), introspection -} - -func mapIntrospectionAccessRequesterToMap(ar fosite.AccessRequester, introspection map[string]any) { -	if ar == nil { -		return -	} - -	var ( -		ok  bool -		aud fosite.Arguments -	) - -	if client := ar.GetClient(); client != nil { -		if id := client.GetID(); id != "" { -			introspection[ClaimClientIdentifier] = id -		} -	} - -	if scope := ar.GetGrantedScopes(); len(scope) > 0 { -		introspection[ClaimScope] = strings.Join(scope, " ") -	} - -	if _, ok = introspection[ClaimIssuedAt]; !ok { -		if rat := ar.GetRequestedAt(); !rat.IsZero() { -			introspection[ClaimIssuedAt] = rat.Unix() -		} -	} - -	if aud = ar.GetGrantedAudience(); len(aud) > 0 { -		introspection[ClaimAudience] = []string(aud) -	} - -	mapIntrospectionAccessRequesterSessionToMap(ar, introspection) -} - -func mapIntrospectionAccessRequesterSessionToMap(ar fosite.AccessRequester, introspection map[string]any) { -	session := ar.GetSession() - -	if session == nil { -		return -	} - -	var ( -		ok    bool -		extra fosite.ExtraClaimsSession -	) - -	if extra, ok = session.(fosite.ExtraClaimsSession); ok { -		claims := extra.GetExtraClaims() - -		for name, value := range claims { -			switch name { -			// We do not allow these to be set through extra claims. -			case ClaimExpirationTime, ClaimClientIdentifier, ClaimScope, ClaimIssuedAt, ClaimSubject, ClaimAudience, ClaimUsername: -				continue -			default: -				introspection[name] = value -			} -		} -	} - -	if exp := session.GetExpiresAt(fosite.AccessToken); !exp.IsZero() { -		introspection[ClaimExpirationTime] = exp.Unix() -	} - -	var claimsSession IDTokenClaimsSession - -	if sub := session.GetSubject(); sub != "" { -		introspection[ClaimSubject] = sub -	} else if claimsSession, ok = session.(IDTokenClaimsSession); ok { -		claims := claimsSession.GetIDTokenClaims() - -		if claims != nil && claims.Subject != "" { -			introspection[ClaimSubject] = claims.Subject -		} -	} - -	if username := session.GetUsername(); username != "" { -		introspection[ClaimUsername] = username -	} -} - -func sliceIntrospectionResponseToRequesterAudience(response fosite.IntrospectionResponder) (aud []string) { -	if cr, ok := response.(ClientRequesterResponder); ok { -		var client fosite.Client - -		if client = cr.GetClient(); client == nil { -			return -		} - -		return []string{client.GetID()} -	} - -	return nil -} - -func mapCopy(src map[string]any) (dst map[string]any) { -	dst = make(map[string]any, len(src)) -	for k, v := range src { -		dst[k] = v -	} - -	return dst -} -  func toStringSlice(v any) (result []string) {  	switch s := v.(type) {  	case string: @@ -367,9 +237,9 @@ func IsJWTProfileAccessToken(token *fjwt.Token) bool {  	return ok && (typ == JWTHeaderTypeValueAccessTokenJWT)  } -// RFC6750Header turns a *fosite.RFC6749Error into the values for a RFC6750 format WWW-Authenticate Bearer response +// RFC6750Header turns a *oauthelia2.RFC6749Error into the values for a RFC6750 format WWW-Authenticate Bearer response  // header, excluding the Bearer prefix. -func RFC6750Header(realm, scope string, err *fosite.RFC6749Error) string { +func RFC6750Header(realm, scope string, err *oauthelia2.RFC6749Error) string {  	values := err.ToValues()  	if realm != "" { @@ -432,7 +302,7 @@ func RFC6750Header(realm, scope string, err *fosite.RFC6749Error) string {  }  // AccessResponderToClearMap returns a clear friendly map copy of the responder map values. -func AccessResponderToClearMap(responder fosite.AccessResponder) map[string]any { +func AccessResponderToClearMap(responder oauthelia2.AccessResponder) map[string]any {  	m := responder.ToMap()  	data := make(map[string]any, len(m)) @@ -452,3 +322,57 @@ func AccessResponderToClearMap(responder fosite.AccessResponder) map[string]any  	return data  } + +// PopulateClientCredentialsFlowSessionWithAccessRequest is used to configure a session when performing a client credentials grant. +func PopulateClientCredentialsFlowSessionWithAccessRequest(ctx Context, client oauthelia2.Client, session *Session) (err error) { +	var ( +		issuer *url.URL +	) + +	if issuer, err = ctx.IssuerURL(); err != nil { +		return oauthelia2.ErrServerError.WithWrap(err).WithDebugf("Failed to determine the issuer with error: %s.", err.Error()) +	} + +	if client == nil { +		return oauthelia2.ErrServerError.WithDebug("Failed to get the client for the request.") +	} + +	session.Subject = "" +	session.Claims.Subject = client.GetID() +	session.ClientID = client.GetID() +	session.DefaultSession.Claims.Issuer = issuer.String() +	session.DefaultSession.Claims.IssuedAt = ctx.GetClock().Now().UTC() +	session.DefaultSession.Claims.RequestedAt = ctx.GetClock().Now().UTC() +	session.ClientCredentials = true + +	return nil +} + +// PopulateClientCredentialsFlowRequester is used to grant the authorized scopes and audiences when performing a client +// credentials grant. +func PopulateClientCredentialsFlowRequester(ctx Context, config oauthelia2.Configurator, client oauthelia2.Client, requester oauthelia2.Requester) (err error) { +	if client == nil || config == nil || requester == nil { +		return oauthelia2.ErrServerError.WithDebug("Failed to get the client, configuration, or requester for the request.") +	} + +	scopes := requester.GetRequestedScopes() + +	var authz, nauthz bool + +	for _, scope := range scopes { +		switch scope { +		case ScopeOffline, ScopeOfflineAccess: +			break +		case ScopeAutheliaBearerAuthz: +			authz = true +		default: +			nauthz = true +		} +	} + +	if authz && nauthz { +		return oauthelia2.ErrInvalidScope.WithDebugf("The scope '%s' must only be requested by itself or with the '%s' scope, no other scopes are permitted.", ScopeAutheliaBearerAuthz, ScopeOfflineAccess) +	} + +	return nil +} diff --git a/internal/oidc/util_blackbox_test.go b/internal/oidc/util_blackbox_test.go index fb7bf6c12..5d4f0db19 100644 --- a/internal/oidc/util_blackbox_test.go +++ b/internal/oidc/util_blackbox_test.go @@ -6,10 +6,9 @@ import (  	"testing"  	"time" -	"github.com/go-jose/go-jose/v3" -	"github.com/ory/fosite" -	"github.com/ory/fosite/handler/openid" -	fjwt "github.com/ory/fosite/token/jwt" +	oauthelia2 "authelia.com/provider/oauth2" +	fjwt "authelia.com/provider/oauth2/token/jwt" +	"github.com/go-jose/go-jose/v4"  	"github.com/stretchr/testify/assert"  	"golang.org/x/text/language" @@ -58,14 +57,14 @@ func TestSortedJSONWebKey(t *testing.T) {  func TestRFC6750Header(t *testing.T) {  	testCaes := []struct {  		name     string -		have     *fosite.RFC6749Error +		have     *oauthelia2.RFC6749Error  		realm    string  		scope    string  		expected string  	}{  		{  			"ShouldEncodeAll", -			&fosite.RFC6749Error{ +			&oauthelia2.RFC6749Error{  				ErrorField:       "invalid_example",  				DescriptionField: "A description",  			}, @@ -75,7 +74,7 @@ func TestRFC6750Header(t *testing.T) {  		},  		{  			"ShouldEncodeBasic", -			&fosite.RFC6749Error{ +			&oauthelia2.RFC6749Error{  				ErrorField:       "invalid_example",  				DescriptionField: "A description",  			}, @@ -92,151 +91,6 @@ func TestRFC6750Header(t *testing.T) {  	}  } -func TestIntrospectionResponseToMap(t *testing.T) { -	testCases := []struct { -		name        string -		have        fosite.IntrospectionResponder -		expectedaud []string -		expected    map[string]any -	}{ -		{ -			"ShouldDecodeInactive", -			&oidc.IntrospectionResponse{}, -			nil, -			map[string]any{oidc.ClaimActive: false}, -		}, -		{ -			"ShouldReturnInactiveWhenNil", -			nil, -			nil, -			map[string]any{oidc.ClaimActive: false}, -		}, -		{ -			"ShouldReturnActiveWithoutAccessRequester", -			&oidc.IntrospectionResponse{ -				Active: true, -			}, -			nil, -			map[string]any{oidc.ClaimActive: true}, -		}, -		{ -			"ShouldReturnActiveWithAccessRequester", -			&oidc.IntrospectionResponse{ -				Active: true, -				AccessRequester: &fosite.AccessRequest{ -					Request: fosite.Request{ -						RequestedAt:     time.Unix(100000, 0).UTC(), -						GrantedScope:    fosite.Arguments{oidc.ScopeOpenID, oidc.ScopeProfile}, -						GrantedAudience: fosite.Arguments{"https://example.com", "aclient"}, -						Client:          &oidc.RegisteredClient{ID: "aclient"}, -					}, -				}, -			}, -			nil, -			map[string]any{ -				oidc.ClaimActive:           true, -				oidc.ClaimScope:            "openid profile", -				oidc.ClaimAudience:         []string{"https://example.com", "aclient"}, -				oidc.ClaimIssuedAt:         int64(100000), -				oidc.ClaimClientIdentifier: "aclient", -			}, -		}, -		{ -			"ShouldReturnActiveWithAccessRequesterAndSession", -			&oidc.IntrospectionResponse{ -				Active: true, -				AccessRequester: &fosite.AccessRequest{ -					Request: fosite.Request{ -						RequestedAt:     time.Unix(100000, 0).UTC(), -						GrantedScope:    fosite.Arguments{oidc.ScopeOpenID, oidc.ScopeProfile}, -						GrantedAudience: fosite.Arguments{"https://example.com", "aclient"}, -						Client:          &oidc.RegisteredClient{ID: "aclient"}, -						Session: &oidc.Session{ -							DefaultSession: &openid.DefaultSession{ -								ExpiresAt: map[fosite.TokenType]time.Time{ -									fosite.AccessToken: time.Unix(1000000, 0).UTC(), -								}, -								Subject: "asubj", -								Claims: &fjwt.IDTokenClaims{ -									Extra: map[string]any{ -										"aclaim":                 1, -										oidc.ClaimExpirationTime: 0, -									}, -								}, -							}, -						}, -					}, -				}, -			}, -			nil, -			map[string]any{ -				oidc.ClaimActive:           true, -				oidc.ClaimScope:            "openid profile", -				oidc.ClaimAudience:         []string{"https://example.com", "aclient"}, -				oidc.ClaimIssuedAt:         int64(100000), -				oidc.ClaimClientIdentifier: "aclient", -				"aclaim":                   1, -				oidc.ClaimSubject:          "asubj", -				oidc.ClaimExpirationTime:   int64(1000000), -			}, -		}, -		{ -			"ShouldReturnActiveWithAccessRequesterAndSessionWithIDTokenClaimsAndUsername", -			&oidc.IntrospectionResponse{ -				Client: &oidc.RegisteredClient{ -					ID:       "rclient", -					Audience: []string{"https://rs.example.com"}, -				}, -				Active: true, -				AccessRequester: &fosite.AccessRequest{ -					Request: fosite.Request{ -						RequestedAt:     time.Unix(100000, 0).UTC(), -						GrantedScope:    fosite.Arguments{oidc.ScopeOpenID, oidc.ScopeProfile}, -						GrantedAudience: fosite.Arguments{"https://example.com", "aclient"}, -						Client:          &oidc.RegisteredClient{ID: "aclient"}, -						Session: &oidc.Session{ -							DefaultSession: &openid.DefaultSession{ -								ExpiresAt: map[fosite.TokenType]time.Time{ -									fosite.AccessToken: time.Unix(1000000, 0).UTC(), -								}, -								Username: "auser", -								Claims: &fjwt.IDTokenClaims{ -									Subject: "asubj", -									Extra: map[string]any{ -										"aclaim":                 1, -										oidc.ClaimExpirationTime: 0, -									}, -								}, -							}, -						}, -					}, -				}, -			}, -			[]string{"rclient"}, -			map[string]any{ -				oidc.ClaimActive:           true, -				oidc.ClaimScope:            "openid profile", -				oidc.ClaimAudience:         []string{"https://example.com", "aclient"}, -				oidc.ClaimIssuedAt:         int64(100000), -				oidc.ClaimClientIdentifier: "aclient", -				"aclaim":                   1, -				oidc.ClaimSubject:          "asubj", -				oidc.ClaimExpirationTime:   int64(1000000), -				oidc.ClaimUsername:         "auser", -			}, -		}, -	} - -	for _, tc := range testCases { -		t.Run(tc.name, func(t *testing.T) { -			aud, introspection := oidc.IntrospectionResponseToMap(tc.have) - -			assert.Equal(t, tc.expectedaud, aud) -			assert.Equal(t, tc.expected, introspection) -		}) -	} -} -  func TestIsJWTProfileAccessToken(t *testing.T) {  	testCases := []struct {  		name     string @@ -285,7 +139,7 @@ func TestIsJWTProfileAccessToken(t *testing.T) {  func TestGetLangFromRequester(t *testing.T) {  	testCases := []struct {  		name     string -		have     fosite.Requester +		have     oauthelia2.Requester  		expected language.Tag  	}{  		{ @@ -295,12 +149,12 @@ func TestGetLangFromRequester(t *testing.T) {  		},  		{  			"ShouldReturnEmpty", -			&fosite.Request{}, +			&oauthelia2.Request{},  			language.Tag{},  		},  		{  			"ShouldReturnValueFromRequest", -			&fosite.Request{ +			&oauthelia2.Request{  				Lang: language.French,  			},  			language.French, @@ -327,29 +181,29 @@ func (t TestGetLangRequester) GetRequestedAt() (requestedAt time.Time) {  	return time.Now().UTC()  } -func (t TestGetLangRequester) GetClient() (client fosite.Client) { +func (t TestGetLangRequester) GetClient() (client oauthelia2.Client) {  	return nil  } -func (t TestGetLangRequester) GetRequestedScopes() (scopes fosite.Arguments) { +func (t TestGetLangRequester) GetRequestedScopes() (scopes oauthelia2.Arguments) {  	return nil  } -func (t TestGetLangRequester) GetRequestedAudience() (audience fosite.Arguments) { +func (t TestGetLangRequester) GetRequestedAudience() (audience oauthelia2.Arguments) {  	return nil  } -func (t TestGetLangRequester) SetRequestedScopes(scopes fosite.Arguments) {} +func (t TestGetLangRequester) SetRequestedScopes(scopes oauthelia2.Arguments) {} -func (t TestGetLangRequester) SetRequestedAudience(audience fosite.Arguments) {} +func (t TestGetLangRequester) SetRequestedAudience(audience oauthelia2.Arguments) {}  func (t TestGetLangRequester) AppendRequestedScope(scope string) {} -func (t TestGetLangRequester) GetGrantedScopes() (grantedScopes fosite.Arguments) { +func (t TestGetLangRequester) GetGrantedScopes() (grantedScopes oauthelia2.Arguments) {  	return nil  } -func (t TestGetLangRequester) GetGrantedAudience() (grantedAudience fosite.Arguments) { +func (t TestGetLangRequester) GetGrantedAudience() (grantedAudience oauthelia2.Arguments) {  	return nil  } @@ -357,18 +211,18 @@ func (t TestGetLangRequester) GrantScope(scope string) {}  func (t TestGetLangRequester) GrantAudience(audience string) {} -func (t TestGetLangRequester) GetSession() (session fosite.Session) { +func (t TestGetLangRequester) GetSession() (session oauthelia2.Session) {  	return nil  } -func (t TestGetLangRequester) SetSession(session fosite.Session) {} +func (t TestGetLangRequester) SetSession(session oauthelia2.Session) {}  func (t TestGetLangRequester) GetRequestForm() url.Values {  	return nil  } -func (t TestGetLangRequester) Merge(requester fosite.Requester) {} +func (t TestGetLangRequester) Merge(requester oauthelia2.Requester) {} -func (t TestGetLangRequester) Sanitize(allowedParameters []string) fosite.Requester { +func (t TestGetLangRequester) Sanitize(allowedParameters []string) oauthelia2.Requester {  	return nil  } diff --git a/internal/storage/provider.go b/internal/storage/provider.go index 9ef7ef5aa..0c461ccdc 100644 --- a/internal/storage/provider.go +++ b/internal/storage/provider.go @@ -5,8 +5,8 @@ import (  	"database/sql"  	"time" +	"authelia.com/provider/oauth2/storage"  	"github.com/google/uuid" -	"github.com/ory/fosite/storage"  	"github.com/authelia/authelia/v4/internal/model"  ) diff --git a/internal/suites/kubernetes.go b/internal/suites/kubernetes.go index f91ebbc1f..4d38e9b68 100644 --- a/internal/suites/kubernetes.go +++ b/internal/suites/kubernetes.go @@ -10,8 +10,10 @@ import (  	"github.com/authelia/authelia/v4/internal/utils"  ) -var k3dImageName = "k3d" -var dockerCmdLine = fmt.Sprintf("docker-compose -p authelia -f internal/suites/docker-compose.yml -f internal/suites/example/compose/k3d/docker-compose.yml exec -T %s", k3dImageName) +var ( +	k3dImageName  = "k3d" +	dockerCmdLine = fmt.Sprintf("docker-compose -p authelia -f internal/suites/docker-compose.yml -f internal/suites/example/compose/k3d/docker-compose.yml exec -T %s", k3dImageName) +)  // K3D used for running kind commands.  type K3D struct{}  | 
