feat(oidc): per-client pkce enforcement policy (#4692)
This implements a per-client PKCE enforcement policy with the ability to enforce that it's used, and the specific challenge mode.pull/4694/head
parent
1e86cb9ca8
commit
adaf069eab
|
@ -1394,15 +1394,9 @@ notifier:
|
|||
## Sets the client to public. This should typically not be set, please see the documentation for usage.
|
||||
# public: false
|
||||
|
||||
## The policy to require for this client; one_factor or two_factor.
|
||||
# authorization_policy: two_factor
|
||||
|
||||
## The consent mode controls how consent is obtained.
|
||||
# consent_mode: auto
|
||||
|
||||
## This value controls the duration a consent on this client remains remembered when the consent mode is
|
||||
## configured as 'auto' or 'pre-configured'.
|
||||
# pre_configured_consent_duration: 1w
|
||||
## Redirect URI's specifies a list of valid case-sensitive callbacks for this client.
|
||||
# redirect_uris:
|
||||
# - https://oidc.example.com:8080/oauth2/callback
|
||||
|
||||
## Audience this client is allowed to request.
|
||||
# audience: []
|
||||
|
@ -1414,10 +1408,6 @@ notifier:
|
|||
# - email
|
||||
# - profile
|
||||
|
||||
## Redirect URI's specifies a list of valid case-sensitive callbacks for this client.
|
||||
# redirect_uris:
|
||||
# - https://oidc.example.com:8080/oauth2/callback
|
||||
|
||||
## Grant Types configures which grants this client can obtain.
|
||||
## It's not recommended to define this unless you know what you're doing.
|
||||
# grant_types:
|
||||
|
@ -1435,6 +1425,23 @@ notifier:
|
|||
# - query
|
||||
# - fragment
|
||||
|
||||
## The policy to require for this client; one_factor or two_factor.
|
||||
# authorization_policy: two_factor
|
||||
|
||||
## Enforces the use of PKCE for this client when set to true.
|
||||
# enforce_pkce: false
|
||||
|
||||
## Enforces the use of PKCE for this client when configured, and enforces the specified challenge method.
|
||||
## Options are 'plain' and 'S256'.
|
||||
# pkce_challenge_method: S256
|
||||
|
||||
## The algorithm used to sign userinfo endpoint responses for this client, either none or RS256.
|
||||
# userinfo_signing_algorithm: none
|
||||
|
||||
## The consent mode controls how consent is obtained.
|
||||
# consent_mode: auto
|
||||
|
||||
## This value controls the duration a consent on this client remains remembered when the consent mode is
|
||||
## configured as 'auto' or 'pre-configured'.
|
||||
# pre_configured_consent_duration: 1w
|
||||
...
|
||||
|
|
|
@ -404,12 +404,92 @@ useful for SPA's and CLI tools. This option requires setting the [client secret]
|
|||
In addition to the standard rules for redirect URIs, public clients can use the `urn:ietf:wg:oauth:2.0:oob` redirect
|
||||
URI.
|
||||
|
||||
#### redirect_uris
|
||||
|
||||
{{< confkey type="list(string)" required="yes" >}}
|
||||
|
||||
A list of valid callback URIs this client will redirect to. All other callbacks will be considered unsafe. The URIs are
|
||||
case-sensitive and they differ from application to application - the community has provided
|
||||
[a list of URL´s for common applications](../../integration/openid-connect/introduction.md).
|
||||
|
||||
Some restrictions that have been placed on clients and
|
||||
their redirect URIs are as follows:
|
||||
|
||||
1. If a client attempts to authorize with Authelia and its redirect URI is not listed in the client configuration the
|
||||
attempt to authorize will fail and an error will be generated.
|
||||
2. The redirect URIs are case-sensitive.
|
||||
3. The URI must include a scheme and that scheme must be one of `http` or `https`.
|
||||
4. The client can ignore rule 3 and use `urn:ietf:wg:oauth:2.0:oob` if it is a [public](#public) client type.
|
||||
|
||||
#### audience
|
||||
|
||||
{{< confkey type="list(string)" required="no" >}}
|
||||
|
||||
A list of audiences this client is allowed to request.
|
||||
|
||||
#### scopes
|
||||
|
||||
{{< confkey type="list(string)" default="openid, groups, profile, email" required="no" >}}
|
||||
|
||||
A list of scopes to allow this client to consume. See
|
||||
[scope definitions](../../integration/openid-connect/introduction.md#scope-definitions) for more information. The
|
||||
documentation for the application you want to use with Authelia will most-likely provide you with the scopes to allow.
|
||||
|
||||
#### grant_types
|
||||
|
||||
{{< confkey type="list(string)" default="refresh_token, authorization_code" required="no" >}}
|
||||
|
||||
A list of grant types this client can return. *It is recommended that this isn't configured at this time unless you
|
||||
know what you're doing*. Valid options are: `implicit`, `refresh_token`, `authorization_code`, `password`,
|
||||
`client_credentials`.
|
||||
|
||||
#### response_types
|
||||
|
||||
{{< confkey type="list(string)" default="code" required="no" >}}
|
||||
|
||||
A list of response types this client can return. *It is recommended that this isn't configured at this time unless you
|
||||
know what you're doing*. Valid options are: `code`, `code id_token`, `id_token`, `token id_token`, `token`,
|
||||
`token id_token code`.
|
||||
|
||||
#### response_modes
|
||||
|
||||
{{< confkey type="list(string)" default="form_post, query, fragment" required="no" >}}
|
||||
|
||||
A list of response modes this client can return. It is recommended that this isn't configured at this time unless you
|
||||
know what you're doing. Potential values are `form_post`, `query`, and `fragment`.
|
||||
|
||||
#### authorization_policy
|
||||
|
||||
{{< confkey type="string" default="two_factor" required="no" >}}
|
||||
|
||||
The authorization policy for this client: either `one_factor` or `two_factor`.
|
||||
|
||||
#### enforce_pkce
|
||||
|
||||
{{< confkey type="bool" default="false" required="no" >}}
|
||||
|
||||
This setting enforces the use of [PKCE] for this individual client. To enforce it for all clients see the global
|
||||
[enforce_pkce](#enforcepkce) setting.
|
||||
|
||||
#### pkce_challenge_method
|
||||
|
||||
{{< confkey type="string" default="" required="no" >}}
|
||||
|
||||
This setting enforces the use of the specified [PKCE] challenge method for this individual client. This setting also
|
||||
effectively enables the [enforce_pkce](#enforcepkce-1) option for this client.
|
||||
|
||||
Valid values are an empty string, `plain`, or `S256`. It should be noted that `S256` is strongly recommended if the
|
||||
relying party supports it.
|
||||
|
||||
#### userinfo_signing_algorithm
|
||||
|
||||
{{< confkey type="string" default="none" required="no" >}}
|
||||
|
||||
The algorithm used to sign the userinfo endpoint responses. This can either be `none` or `RS256`.
|
||||
|
||||
See the [integration guide](../../integration/openid-connect/introduction.md#user-information-signing-algorithm) for
|
||||
more information.
|
||||
|
||||
#### consent_mode
|
||||
|
||||
{{< confkey type="string" default="auto" required="no" >}}
|
||||
|
@ -442,69 +522,6 @@ match exactly with the granted scopes/audience.
|
|||
|
||||
[consent_mode]: #consentmode
|
||||
|
||||
#### audience
|
||||
|
||||
{{< confkey type="list(string)" required="no" >}}
|
||||
|
||||
A list of audiences this client is allowed to request.
|
||||
|
||||
#### scopes
|
||||
|
||||
{{< confkey type="list(string)" default="openid, groups, profile, email" required="no" >}}
|
||||
|
||||
A list of scopes to allow this client to consume. See
|
||||
[scope definitions](../../integration/openid-connect/introduction.md#scope-definitions) for more information. The
|
||||
documentation for the application you want to use with Authelia will most-likely provide you with the scopes to allow.
|
||||
|
||||
#### redirect_uris
|
||||
|
||||
{{< confkey type="list(string)" required="yes" >}}
|
||||
|
||||
A list of valid callback URIs this client will redirect to. All other callbacks will be considered unsafe. The URIs are
|
||||
case-sensitive and they differ from application to application - the community has provided
|
||||
[a list of URL´s for common applications](../../integration/openid-connect/introduction.md).
|
||||
|
||||
Some restrictions that have been placed on clients and
|
||||
their redirect URIs are as follows:
|
||||
|
||||
1. If a client attempts to authorize with Authelia and its redirect URI is not listed in the client configuration the
|
||||
attempt to authorize will fail and an error will be generated.
|
||||
2. The redirect URIs are case-sensitive.
|
||||
3. The URI must include a scheme and that scheme must be one of `http` or `https`.
|
||||
4. The client can ignore rule 3 and use `urn:ietf:wg:oauth:2.0:oob` if it is a [public](#public) client type.
|
||||
|
||||
#### grant_types
|
||||
|
||||
{{< confkey type="list(string)" default="refresh_token, authorization_code" required="no" >}}
|
||||
|
||||
A list of grant types this client can return. *It is recommended that this isn't configured at this time unless you
|
||||
know what you're doing*. Valid options are: `implicit`, `refresh_token`, `authorization_code`, `password`,
|
||||
`client_credentials`.
|
||||
|
||||
#### response_types
|
||||
|
||||
{{< confkey type="list(string)" default="code" required="no" >}}
|
||||
|
||||
A list of response types this client can return. *It is recommended that this isn't configured at this time unless you
|
||||
know what you're doing*. Valid options are: `code`, `code id_token`, `id_token`, `token id_token`, `token`,
|
||||
`token id_token code`.
|
||||
|
||||
#### response_modes
|
||||
|
||||
{{< confkey type="list(string)" default="form_post, query, fragment" required="no" >}}
|
||||
|
||||
A list of response modes this client can return. It is recommended that this isn't configured at this time unless you
|
||||
know what you're doing. Potential values are `form_post`, `query`, and `fragment`.
|
||||
|
||||
#### userinfo_signing_algorithm
|
||||
|
||||
{{< confkey type="string" default="none" required="no" >}}
|
||||
|
||||
The algorithm used to sign the userinfo endpoint responses. This can either be `none` or `RS256`.
|
||||
|
||||
See the [integration guide](../../integration/openid-connect/introduction.md#user-information-signing-algorithm) for
|
||||
more information.
|
||||
|
||||
## Integration
|
||||
|
||||
To integrate Authelia's [OpenID Connect] implementation with a relying party please see the
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
title: "Templating"
|
||||
description: "A reference guide on the templates system"
|
||||
lead: "This section contains reference documentation for Authelia's templating capabilities."
|
||||
date: 2022-12-23T18:31:05+11:00
|
||||
date: 2022-12-23T21:58:54+11:00
|
||||
draft: false
|
||||
images: []
|
||||
menu:
|
||||
|
|
|
@ -64,7 +64,7 @@ Feature List:
|
|||
|
||||
Feature List:
|
||||
|
||||
* [Proof Key Code Exchange (PKCE)](https://www.rfc-editor.org/rfc/rfc7636.html) for Authorization Code Flow
|
||||
* [Proof Key Code Exchange (PKCE)] for Authorization Code Flow
|
||||
* Claims:
|
||||
* `preferred_username` - sending the username in this claim instead of the `sub` claim.
|
||||
|
||||
|
@ -115,8 +115,8 @@ Feature List:
|
|||
|
||||
{{< roadmap-status stage="in-progress" version="v4.38.0" >}}
|
||||
|
||||
|
||||
* [OAuth 2.0 Pushed Authorization Requests](https://www.rfc-editor.org/rfc/rfc9126.html)
|
||||
* Per-Client [Proof Key Code Exchange (PKCE)] Policy
|
||||
|
||||
### Beta 7
|
||||
|
||||
|
@ -219,3 +219,4 @@ The `preferred_username` claim was missing and was fixed.
|
|||
[OpenID Connect Core (Subject Identifier Types)]: https://openid.net/specs/openid-connect-core-1_0.html#SubjectIDTypes
|
||||
[OpenID Connect Core (Pairwise Identifier Algorithm)]: https://openid.net/specs/openid-connect-core-1_0.html#PairwiseAlg
|
||||
[OpenID Connect Core (Mandatory to Implement Features for All OpenID Providers)]: https://openid.net/specs/openid-connect-core-1_0.html#ServerMTI
|
||||
[Proof Key Code Exchange (PKCE)]: https://www.rfc-editor.org/rfc/rfc7636.html
|
||||
|
|
|
@ -1394,15 +1394,9 @@ notifier:
|
|||
## Sets the client to public. This should typically not be set, please see the documentation for usage.
|
||||
# public: false
|
||||
|
||||
## The policy to require for this client; one_factor or two_factor.
|
||||
# authorization_policy: two_factor
|
||||
|
||||
## The consent mode controls how consent is obtained.
|
||||
# consent_mode: auto
|
||||
|
||||
## This value controls the duration a consent on this client remains remembered when the consent mode is
|
||||
## configured as 'auto' or 'pre-configured'.
|
||||
# pre_configured_consent_duration: 1w
|
||||
## Redirect URI's specifies a list of valid case-sensitive callbacks for this client.
|
||||
# redirect_uris:
|
||||
# - https://oidc.example.com:8080/oauth2/callback
|
||||
|
||||
## Audience this client is allowed to request.
|
||||
# audience: []
|
||||
|
@ -1414,10 +1408,6 @@ notifier:
|
|||
# - email
|
||||
# - profile
|
||||
|
||||
## Redirect URI's specifies a list of valid case-sensitive callbacks for this client.
|
||||
# redirect_uris:
|
||||
# - https://oidc.example.com:8080/oauth2/callback
|
||||
|
||||
## Grant Types configures which grants this client can obtain.
|
||||
## It's not recommended to define this unless you know what you're doing.
|
||||
# grant_types:
|
||||
|
@ -1435,6 +1425,23 @@ notifier:
|
|||
# - query
|
||||
# - fragment
|
||||
|
||||
## The policy to require for this client; one_factor or two_factor.
|
||||
# authorization_policy: two_factor
|
||||
|
||||
## Enforces the use of PKCE for this client when set to true.
|
||||
# enforce_pkce: false
|
||||
|
||||
## Enforces the use of PKCE for this client when configured, and enforces the specified challenge method.
|
||||
## Options are 'plain' and 'S256'.
|
||||
# pkce_challenge_method: S256
|
||||
|
||||
## The algorithm used to sign userinfo endpoint responses for this client, either none or RS256.
|
||||
# userinfo_signing_algorithm: none
|
||||
|
||||
## The consent mode controls how consent is obtained.
|
||||
# consent_mode: auto
|
||||
|
||||
## This value controls the duration a consent on this client remains remembered when the consent mode is
|
||||
## configured as 'auto' or 'pre-configured'.
|
||||
# pre_configured_consent_duration: 1w
|
||||
...
|
||||
|
|
|
@ -57,10 +57,13 @@ type OpenIDConnectClientConfiguration struct {
|
|||
ResponseTypes []string `koanf:"response_types"`
|
||||
ResponseModes []string `koanf:"response_modes"`
|
||||
|
||||
UserinfoSigningAlgorithm string `koanf:"userinfo_signing_algorithm"`
|
||||
|
||||
Policy string `koanf:"authorization_policy"`
|
||||
|
||||
EnforcePKCE bool `koanf:"enforce_pkce"`
|
||||
|
||||
PKCEChallengeMethod string `koanf:"pkce_challenge_method"`
|
||||
UserinfoSigningAlgorithm string `koanf:"userinfo_signing_algorithm"`
|
||||
|
||||
ConsentMode string `koanf:"consent_mode"`
|
||||
ConsentPreConfiguredDuration *time.Duration `koanf:"pre_configured_consent_duration"`
|
||||
}
|
||||
|
|
|
@ -43,8 +43,10 @@ var Keys = []string{
|
|||
"identity_providers.oidc.clients[].grant_types",
|
||||
"identity_providers.oidc.clients[].response_types",
|
||||
"identity_providers.oidc.clients[].response_modes",
|
||||
"identity_providers.oidc.clients[].userinfo_signing_algorithm",
|
||||
"identity_providers.oidc.clients[].authorization_policy",
|
||||
"identity_providers.oidc.clients[].enforce_pkce",
|
||||
"identity_providers.oidc.clients[].pkce_challenge_method",
|
||||
"identity_providers.oidc.clients[].userinfo_signing_algorithm",
|
||||
"identity_providers.oidc.clients[].consent_mode",
|
||||
"identity_providers.oidc.clients[].pre_configured_consent_duration",
|
||||
"authentication_backend.password_reset.disable",
|
||||
|
|
|
@ -6,7 +6,6 @@ import (
|
|||
"github.com/go-webauthn/webauthn/protocol"
|
||||
|
||||
"github.com/authelia/authelia/v4/internal/configuration/schema"
|
||||
|
||||
"github.com/authelia/authelia/v4/internal/oidc"
|
||||
)
|
||||
|
||||
|
@ -172,6 +171,8 @@ const (
|
|||
"invalid value: redirect uri '%s' must have the scheme but it is absent"
|
||||
errFmtOIDCClientInvalidPolicy = "identity_providers: oidc: client '%s': option 'policy' must be 'one_factor' " +
|
||||
"or 'two_factor' but it is configured as '%s'"
|
||||
errFmtOIDCClientInvalidPKCEChallengeMethod = "identity_providers: oidc: client '%s': option 'pkce_challenge_method' must be 'plain' " +
|
||||
"or 'S256' but it is configured as '%s'"
|
||||
errFmtOIDCClientInvalidConsentMode = "identity_providers: oidc: client '%s': consent: option 'mode' must be one of " +
|
||||
"'%s' but it is configured as '%s'"
|
||||
errFmtOIDCClientInvalidEntry = "identity_providers: oidc: client '%s': option '%s' must only have the values " +
|
||||
|
|
|
@ -175,6 +175,13 @@ func validateOIDCClients(config *schema.OpenIDConnectConfiguration, val *schema.
|
|||
val.Push(fmt.Errorf(errFmtOIDCClientInvalidPolicy, client.ID, client.Policy))
|
||||
}
|
||||
|
||||
switch client.PKCEChallengeMethod {
|
||||
case "", "plain", "S256":
|
||||
break
|
||||
default:
|
||||
val.Push(fmt.Errorf(errFmtOIDCClientInvalidPKCEChallengeMethod, client.ID, client.PKCEChallengeMethod))
|
||||
}
|
||||
|
||||
validateOIDCClientConsentMode(c, config, val)
|
||||
validateOIDCClientSectorIdentifier(client, val)
|
||||
validateOIDCClientScopes(c, config, val)
|
||||
|
|
|
@ -331,6 +331,40 @@ func TestShouldRaiseErrorWhenOIDCServerClientBadValues(t *testing.T) {
|
|||
fmt.Sprintf(errFmtOIDCClientInvalidConsentMode, "client-bad-consent-mode", strings.Join(append(validOIDCClientConsentModes, "auto"), "', '"), "cap"),
|
||||
},
|
||||
},
|
||||
{
|
||||
Name: "InvalidPKCEChallengeMethod",
|
||||
Clients: []schema.OpenIDConnectClientConfiguration{
|
||||
{
|
||||
ID: "client-bad-pkce-mode",
|
||||
Secret: MustDecodeSecret("$plaintext$a-secret"),
|
||||
Policy: policyTwoFactor,
|
||||
RedirectURIs: []string{
|
||||
"https://google.com",
|
||||
},
|
||||
PKCEChallengeMethod: "abc",
|
||||
},
|
||||
},
|
||||
Errors: []string{
|
||||
fmt.Sprintf(errFmtOIDCClientInvalidPKCEChallengeMethod, "client-bad-pkce-mode", "abc"),
|
||||
},
|
||||
},
|
||||
{
|
||||
Name: "InvalidPKCEChallengeMethodLowerCaseS256",
|
||||
Clients: []schema.OpenIDConnectClientConfiguration{
|
||||
{
|
||||
ID: "client-bad-pkce-mode-s256",
|
||||
Secret: MustDecodeSecret("$plaintext$a-secret"),
|
||||
Policy: policyTwoFactor,
|
||||
RedirectURIs: []string{
|
||||
"https://google.com",
|
||||
},
|
||||
PKCEChallengeMethod: "s256",
|
||||
},
|
||||
},
|
||||
Errors: []string{
|
||||
fmt.Sprintf(errFmtOIDCClientInvalidPKCEChallengeMethod, "client-bad-pkce-mode-s256", "s256"),
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
for _, tc := range testCases {
|
||||
|
@ -609,7 +643,7 @@ func TestValidateIdentityProvidersShouldRaiseErrorsOnInvalidClientTypes(t *testi
|
|||
assert.EqualError(t, validator.Errors()[1], fmt.Sprintf(errFmtOIDCClientRedirectURIPublic, "client-with-bad-redirect-uri", oauth2InstalledApp))
|
||||
}
|
||||
|
||||
func TestValidateIdentityProvidersShouldNotRaiseErrorsOnValidPublicClients(t *testing.T) {
|
||||
func TestValidateIdentityProvidersShouldNotRaiseErrorsOnValidClientOptions(t *testing.T) {
|
||||
validator := schema.NewStructValidator()
|
||||
config := &schema.IdentityProvidersConfiguration{
|
||||
OIDC: &schema.OpenIDConnectConfiguration{
|
||||
|
@ -640,6 +674,24 @@ func TestValidateIdentityProvidersShouldNotRaiseErrorsOnValidPublicClients(t *te
|
|||
"http://127.0.0.1",
|
||||
},
|
||||
},
|
||||
{
|
||||
ID: "client-with-pkce-mode-plain",
|
||||
Public: true,
|
||||
Policy: "two_factor",
|
||||
RedirectURIs: []string{
|
||||
"https://pkce.com",
|
||||
},
|
||||
PKCEChallengeMethod: "plain",
|
||||
},
|
||||
{
|
||||
ID: "client-with-pkce-mode-S256",
|
||||
Public: true,
|
||||
Policy: "two_factor",
|
||||
RedirectURIs: []string{
|
||||
"https://pkce.com",
|
||||
},
|
||||
PKCEChallengeMethod: "S256",
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
|
|
@ -52,6 +52,16 @@ func OpenIDConnectAuthorization(ctx *middlewares.AutheliaCtx, rw http.ResponseWr
|
|||
return
|
||||
}
|
||||
|
||||
if err = client.ValidateAuthorizationPolicy(requester); err != nil {
|
||||
rfc := fosite.ErrorToRFC6749Error(err)
|
||||
|
||||
ctx.Logger.Errorf("Authorization Request with id '%s' on client with id '%s' failed to validate the authorization policy: %s", requester.GetID(), clientID, rfc.WithExposeDebug(true).GetDescription())
|
||||
|
||||
ctx.Providers.OpenIDConnect.WriteAuthorizeError(ctx, rw, requester, err)
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
issuer = ctx.RootURL()
|
||||
|
||||
userSession := ctx.GetSession()
|
||||
|
|
|
@ -1,7 +1,10 @@
|
|||
package oidc
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"github.com/ory/fosite"
|
||||
"github.com/ory/x/errorsx"
|
||||
|
||||
"github.com/authelia/authelia/v4/internal/authentication"
|
||||
"github.com/authelia/authelia/v4/internal/authorization"
|
||||
|
@ -18,6 +21,10 @@ func NewClient(config schema.OpenIDConnectClientConfiguration) (client *Client)
|
|||
SectorIdentifier: config.SectorIdentifier.String(),
|
||||
Public: config.Public,
|
||||
|
||||
EnforcePKCE: config.EnforcePKCE || config.PKCEChallengeMethod != "",
|
||||
EnforcePKCEChallengeMethod: config.PKCEChallengeMethod != "",
|
||||
PKCEChallengeMethod: config.PKCEChallengeMethod,
|
||||
|
||||
Audience: config.Audience,
|
||||
Scopes: config.Scopes,
|
||||
RedirectURIs: config.RedirectURIs,
|
||||
|
@ -39,6 +46,29 @@ func NewClient(config schema.OpenIDConnectClientConfiguration) (client *Client)
|
|||
return client
|
||||
}
|
||||
|
||||
// ValidateAuthorizationPolicy is a helper function to validate additional policy constraints on a per-client basis.
|
||||
func (c *Client) ValidateAuthorizationPolicy(r fosite.Requester) (err error) {
|
||||
form := r.GetRequestForm()
|
||||
|
||||
if c.EnforcePKCE {
|
||||
if form.Get("code_challenge") == "" {
|
||||
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.EnforcePKCEChallengeMethod {
|
||||
if method := form.Get("code_challenge_method"); method != c.PKCEChallengeMethod {
|
||||
return errorsx.WithStack(fosite.ErrInvalidRequest.
|
||||
WithHint(fmt.Sprintf("Client must use code_challenge_method=%s, %s is not allowed.", c.PKCEChallengeMethod, method)).
|
||||
WithDebug(fmt.Sprintf("The server is configured in a way that enforces PKCE %s as challenge method for this client.", c.PKCEChallengeMethod)))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// IsAuthenticationLevelSufficient returns if the provided authentication.Level is sufficient for the client of the AutheliaClient.
|
||||
func (c *Client) IsAuthenticationLevelSufficient(level authentication.Level) bool {
|
||||
if level == authentication.NotAuthenticated {
|
||||
|
@ -48,11 +78,6 @@ func (c *Client) IsAuthenticationLevelSufficient(level authentication.Level) boo
|
|||
return authorization.IsAuthLevelSufficient(level, c.Policy)
|
||||
}
|
||||
|
||||
// GetID returns the ID.
|
||||
func (c *Client) GetID() string {
|
||||
return c.ID
|
||||
}
|
||||
|
||||
// GetSectorIdentifier returns the SectorIdentifier for this client.
|
||||
func (c *Client) GetSectorIdentifier() string {
|
||||
return c.SectorIdentifier
|
||||
|
@ -74,6 +99,11 @@ func (c *Client) GetConsentResponseBody(consent *model.OAuth2ConsentSession) Con
|
|||
return body
|
||||
}
|
||||
|
||||
// GetID returns the ID.
|
||||
func (c *Client) GetID() string {
|
||||
return c.ID
|
||||
}
|
||||
|
||||
// GetHashedSecret returns the Secret.
|
||||
func (c *Client) GetHashedSecret() []byte {
|
||||
if c.Secret == nil {
|
||||
|
|
|
@ -217,6 +217,90 @@ func TestClient_GetResponseTypes(t *testing.T) {
|
|||
assert.Equal(t, "id_token", responseTypes[1])
|
||||
}
|
||||
|
||||
func TestNewClientPKCE(t *testing.T) {
|
||||
testCases := []struct {
|
||||
name string
|
||||
have schema.OpenIDConnectClientConfiguration
|
||||
expectedEnforcePKCE bool
|
||||
expectedEnforcePKCEChallengeMethod bool
|
||||
expected string
|
||||
req *fosite.Request
|
||||
err string
|
||||
}{
|
||||
{
|
||||
"ShouldNotEnforcePKCEAndNotErrorOnNonPKCERequest",
|
||||
schema.OpenIDConnectClientConfiguration{},
|
||||
false,
|
||||
false,
|
||||
"",
|
||||
&fosite.Request{},
|
||||
"",
|
||||
},
|
||||
{
|
||||
"ShouldEnforcePKCEAndErrorOnNonPKCERequest",
|
||||
schema.OpenIDConnectClientConfiguration{EnforcePKCE: true},
|
||||
true,
|
||||
false,
|
||||
"",
|
||||
&fosite.Request{},
|
||||
"invalid_request",
|
||||
},
|
||||
{
|
||||
"ShouldEnforcePKCEAndNotErrorOnPKCERequest",
|
||||
schema.OpenIDConnectClientConfiguration{EnforcePKCE: true},
|
||||
true,
|
||||
false,
|
||||
"",
|
||||
&fosite.Request{Form: map[string][]string{"code_challenge": {"abc"}}},
|
||||
"",
|
||||
},
|
||||
{"ShouldEnforcePKCEFromChallengeMethodAndErrorOnNonPKCERequest",
|
||||
schema.OpenIDConnectClientConfiguration{PKCEChallengeMethod: "S256"},
|
||||
true,
|
||||
true,
|
||||
"S256",
|
||||
&fosite.Request{},
|
||||
"invalid_request",
|
||||
},
|
||||
{"ShouldEnforcePKCEFromChallengeMethodAndErrorOnInvalidChallengeMethod",
|
||||
schema.OpenIDConnectClientConfiguration{PKCEChallengeMethod: "S256"},
|
||||
true,
|
||||
true,
|
||||
"S256",
|
||||
&fosite.Request{Form: map[string][]string{"code_challenge": {"abc"}}},
|
||||
"invalid_request",
|
||||
},
|
||||
{"ShouldEnforcePKCEFromChallengeMethodAndNotErrorOnValidRequest",
|
||||
schema.OpenIDConnectClientConfiguration{PKCEChallengeMethod: "S256"},
|
||||
true,
|
||||
true,
|
||||
"S256",
|
||||
&fosite.Request{Form: map[string][]string{"code_challenge": {"abc"}, "code_challenge_method": {"S256"}}},
|
||||
"",
|
||||
},
|
||||
}
|
||||
|
||||
for _, tc := range testCases {
|
||||
t.Run(tc.name, func(t *testing.T) {
|
||||
client := NewClient(tc.have)
|
||||
|
||||
assert.Equal(t, tc.expectedEnforcePKCE, client.EnforcePKCE)
|
||||
assert.Equal(t, tc.expectedEnforcePKCEChallengeMethod, client.EnforcePKCEChallengeMethod)
|
||||
assert.Equal(t, tc.expected, client.PKCEChallengeMethod)
|
||||
|
||||
if tc.req != nil {
|
||||
err := client.ValidateAuthorizationPolicy(tc.req)
|
||||
|
||||
if tc.err != "" {
|
||||
assert.EqualError(t, err, tc.err)
|
||||
} else {
|
||||
assert.NoError(t, err)
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestClient_IsPublic(t *testing.T) {
|
||||
c := Client{}
|
||||
|
||||
|
|
|
@ -107,6 +107,10 @@ type Client struct {
|
|||
SectorIdentifier string
|
||||
Public bool
|
||||
|
||||
EnforcePKCE bool
|
||||
EnforcePKCEChallengeMethod bool
|
||||
PKCEChallengeMethod string
|
||||
|
||||
Audience []string
|
||||
Scopes []string
|
||||
RedirectURIs []string
|
||||
|
|
Loading…
Reference in New Issue