Compare commits
4 Commits
master
...
feat-oidc-
Author | SHA1 | Date |
---|---|---|
James Elliott | 33fdacb6e1 | |
James Elliott | f290fd90b1 | |
James Elliott | 2f9da2b7e0 | |
James Elliott | 5e5eead729 |
|
@ -208,6 +208,10 @@ func domainToPrefixSuffix(domain string) (prefix, suffix string) {
|
||||||
return parts[0], strings.Join(parts[1:], ".")
|
return parts[0], strings.Join(parts[1:], ".")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func NewSubjects(subjectRules [][]string) (subjects []AccessControlSubjects) {
|
||||||
|
return schemaSubjectsToACL(subjectRules)
|
||||||
|
}
|
||||||
|
|
||||||
// IsAuthLevelSufficient returns true if the current authenticationLevel is above the authorizationLevel.
|
// IsAuthLevelSufficient returns true if the current authenticationLevel is above the authorizationLevel.
|
||||||
func IsAuthLevelSufficient(authenticationLevel authentication.Level, authorizationLevel Level) bool {
|
func IsAuthLevelSufficient(authenticationLevel authentication.Level, authorizationLevel Level) bool {
|
||||||
switch authorizationLevel {
|
switch authorizationLevel {
|
||||||
|
|
|
@ -35,11 +35,25 @@ type OpenIDConnect struct {
|
||||||
|
|
||||||
Clients []OpenIDConnectClient `koanf:"clients"`
|
Clients []OpenIDConnectClient `koanf:"clients"`
|
||||||
|
|
||||||
|
Policies map[string]OpenIDConnectPolicy `koanf:"policies"`
|
||||||
|
|
||||||
Discovery OpenIDConnectDiscovery // MetaData value. Not configurable by users.
|
Discovery OpenIDConnectDiscovery // MetaData value. Not configurable by users.
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type OpenIDConnectPolicy struct {
|
||||||
|
DefaultPolicy string `koanf:"default_policy"`
|
||||||
|
|
||||||
|
Rules []OpenIDConnectPolicyRule `koanf:"rules"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type OpenIDConnectPolicyRule struct {
|
||||||
|
Policy string `koanf:"policy"`
|
||||||
|
Subjects [][]string `koanf:"subject"`
|
||||||
|
}
|
||||||
|
|
||||||
// OpenIDConnectDiscovery is information discovered during validation reused for the discovery handlers.
|
// OpenIDConnectDiscovery is information discovered during validation reused for the discovery handlers.
|
||||||
type OpenIDConnectDiscovery struct {
|
type OpenIDConnectDiscovery struct {
|
||||||
|
Policies []string
|
||||||
DefaultKeyIDs map[string]string
|
DefaultKeyIDs map[string]string
|
||||||
DefaultKeyID string
|
DefaultKeyID string
|
||||||
ResponseObjectSigningKeyIDs []string
|
ResponseObjectSigningKeyIDs []string
|
||||||
|
|
|
@ -72,6 +72,11 @@ var Keys = []string{
|
||||||
"identity_providers.oidc.clients[].public_keys.values[].key",
|
"identity_providers.oidc.clients[].public_keys.values[].key",
|
||||||
"identity_providers.oidc.clients[].public_keys.values[].certificate_chain",
|
"identity_providers.oidc.clients[].public_keys.values[].certificate_chain",
|
||||||
"identity_providers.oidc.clients[]",
|
"identity_providers.oidc.clients[]",
|
||||||
|
"identity_providers.oidc.policies",
|
||||||
|
"identity_providers.oidc.policies.*.default_policy",
|
||||||
|
"identity_providers.oidc.policies.*.rules",
|
||||||
|
"identity_providers.oidc.policies.*.rules[].policy",
|
||||||
|
"identity_providers.oidc.policies.*.rules[].subject",
|
||||||
"identity_providers.oidc",
|
"identity_providers.oidc",
|
||||||
"authentication_backend.password_reset.disable",
|
"authentication_backend.password_reset.disable",
|
||||||
"authentication_backend.password_reset.custom_url",
|
"authentication_backend.password_reset.custom_url",
|
||||||
|
|
|
@ -124,6 +124,12 @@ notifier:
|
||||||
|
|
||||||
identity_providers:
|
identity_providers:
|
||||||
oidc:
|
oidc:
|
||||||
|
policies:
|
||||||
|
abc:
|
||||||
|
default_policy: 'two_factor'
|
||||||
|
rules:
|
||||||
|
- subject: 'group:admin'
|
||||||
|
policy: 'one_factor'
|
||||||
cors:
|
cors:
|
||||||
allowed_origins:
|
allowed_origins:
|
||||||
- 'https://google.com'
|
- 'https://google.com'
|
||||||
|
|
|
@ -30,6 +30,7 @@ func validateOIDC(config *schema.OpenIDConnect, val *schema.StructValidator) {
|
||||||
setOIDCDefaults(config)
|
setOIDCDefaults(config)
|
||||||
|
|
||||||
validateOIDCIssuer(config, val)
|
validateOIDCIssuer(config, val)
|
||||||
|
validateOIDCPolicies(config, val)
|
||||||
|
|
||||||
sort.Sort(oidc.SortedSigningAlgs(config.Discovery.ResponseObjectSigningAlgs))
|
sort.Sort(oidc.SortedSigningAlgs(config.Discovery.ResponseObjectSigningAlgs))
|
||||||
|
|
||||||
|
@ -58,6 +59,53 @@ func validateOIDC(config *schema.OpenIDConnect, val *schema.StructValidator) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func validateOIDCPolicies(config *schema.OpenIDConnect, val *schema.StructValidator) {
|
||||||
|
config.Discovery.Policies = []string{policyOneFactor, policyTwoFactor}
|
||||||
|
|
||||||
|
for name, policy := range config.Policies {
|
||||||
|
switch name {
|
||||||
|
case "":
|
||||||
|
val.Push(fmt.Errorf("policy must have a name"))
|
||||||
|
case policyOneFactor, policyTwoFactor, policyDeny:
|
||||||
|
val.Push(fmt.Errorf("policy names can't be named any of %s", strJoinAnd([]string{policyOneFactor, policyTwoFactor, policyDeny})))
|
||||||
|
default:
|
||||||
|
break
|
||||||
|
}
|
||||||
|
|
||||||
|
switch policy.DefaultPolicy {
|
||||||
|
case "":
|
||||||
|
policy.DefaultPolicy = policyTwoFactor
|
||||||
|
case policyOneFactor, policyTwoFactor, policyDeny:
|
||||||
|
break
|
||||||
|
default:
|
||||||
|
val.Push(fmt.Errorf("policy must be one of %s", strJoinAnd([]string{policyOneFactor, policyTwoFactor, policyDeny})))
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(policy.Rules) == 0 {
|
||||||
|
val.Push(fmt.Errorf("policy must include at least one rule"))
|
||||||
|
}
|
||||||
|
|
||||||
|
for i, rule := range policy.Rules {
|
||||||
|
switch rule.Policy {
|
||||||
|
case "":
|
||||||
|
policy.Rules[i].Policy = policyTwoFactor
|
||||||
|
case policyOneFactor, policyTwoFactor, policyDeny:
|
||||||
|
break
|
||||||
|
default:
|
||||||
|
val.Push(fmt.Errorf("policy must be one of %s", strJoinAnd([]string{policyOneFactor, policyTwoFactor, policyDeny})))
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(rule.Subjects) == 0 {
|
||||||
|
val.Push(fmt.Errorf("policy must include at least one criteria"))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
config.Policies[name] = policy
|
||||||
|
|
||||||
|
config.Discovery.Policies = append(config.Discovery.Policies, name)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func validateOIDCIssuer(config *schema.OpenIDConnect, val *schema.StructValidator) {
|
func validateOIDCIssuer(config *schema.OpenIDConnect, val *schema.StructValidator) {
|
||||||
switch {
|
switch {
|
||||||
case config.IssuerPrivateKey != nil:
|
case config.IssuerPrivateKey != nil:
|
||||||
|
@ -337,13 +385,13 @@ func validateOIDCClient(c int, config *schema.OpenIDConnect, val *schema.StructV
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
switch config.Clients[c].Policy {
|
switch {
|
||||||
case "":
|
case config.Clients[c].Policy == "":
|
||||||
config.Clients[c].Policy = schema.DefaultOpenIDConnectClientConfiguration.Policy
|
config.Clients[c].Policy = schema.DefaultOpenIDConnectClientConfiguration.Policy
|
||||||
case policyOneFactor, policyTwoFactor:
|
case utils.IsStringInSlice(config.Clients[c].Policy, config.Discovery.Policies):
|
||||||
break
|
break
|
||||||
default:
|
default:
|
||||||
val.Push(fmt.Errorf(errFmtOIDCClientInvalidValue, config.Clients[c].ID, "authorization_policy", strJoinOr([]string{policyOneFactor, policyTwoFactor}), config.Clients[c].Policy))
|
val.Push(fmt.Errorf(errFmtOIDCClientInvalidValue, config.Clients[c].ID, "authorization_policy", strJoinOr(config.Discovery.Policies), config.Clients[c].Policy))
|
||||||
}
|
}
|
||||||
|
|
||||||
switch config.Clients[c].PKCEChallengeMethod {
|
switch config.Clients[c].PKCEChallengeMethod {
|
||||||
|
|
|
@ -67,16 +67,18 @@ KEYS:
|
||||||
// NewKeyPattern returns patterns which are required to match key patterns.
|
// NewKeyPattern returns patterns which are required to match key patterns.
|
||||||
func NewKeyPattern(key string) (pattern *regexp.Regexp, err error) {
|
func NewKeyPattern(key string) (pattern *regexp.Regexp, err error) {
|
||||||
switch {
|
switch {
|
||||||
case strings.Contains(key, ".*."):
|
case reIsMapKey.MatchString(key):
|
||||||
return NewKeyMapPattern(key)
|
return NewKeyMapPattern(key)
|
||||||
default:
|
default:
|
||||||
return nil, nil
|
return nil, nil
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var reIsMapKey = regexp.MustCompile(`\.\*(\[]|\.)`)
|
||||||
|
|
||||||
// NewKeyMapPattern returns a pattern required to match map keys.
|
// NewKeyMapPattern returns a pattern required to match map keys.
|
||||||
func NewKeyMapPattern(key string) (pattern *regexp.Regexp, err error) {
|
func NewKeyMapPattern(key string) (pattern *regexp.Regexp, err error) {
|
||||||
parts := strings.Split(key, ".*.")
|
parts := strings.Split(key, ".*")
|
||||||
|
|
||||||
buf := &strings.Builder{}
|
buf := &strings.Builder{}
|
||||||
|
|
||||||
|
@ -85,11 +87,16 @@ func NewKeyMapPattern(key string) (pattern *regexp.Regexp, err error) {
|
||||||
n := len(parts) - 1
|
n := len(parts) - 1
|
||||||
|
|
||||||
for i, part := range parts {
|
for i, part := range parts {
|
||||||
if i != 0 {
|
if i != 0 && !strings.HasPrefix(part, "[]") {
|
||||||
buf.WriteString("\\.")
|
buf.WriteString("\\.")
|
||||||
}
|
}
|
||||||
|
|
||||||
for _, r := range part {
|
for j, r := range part {
|
||||||
|
// Skip prefixed period.
|
||||||
|
if j == 0 && r == '.' {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
switch r {
|
switch r {
|
||||||
case '[', ']', '.', '{', '}':
|
case '[', ']', '.', '{', '}':
|
||||||
buf.WriteRune('\\')
|
buf.WriteRune('\\')
|
||||||
|
|
|
@ -8,6 +8,7 @@ import (
|
||||||
|
|
||||||
"github.com/ory/fosite"
|
"github.com/ory/fosite"
|
||||||
|
|
||||||
|
"github.com/authelia/authelia/v4/internal/authorization"
|
||||||
"github.com/authelia/authelia/v4/internal/middlewares"
|
"github.com/authelia/authelia/v4/internal/middlewares"
|
||||||
"github.com/authelia/authelia/v4/internal/model"
|
"github.com/authelia/authelia/v4/internal/model"
|
||||||
"github.com/authelia/authelia/v4/internal/oidc"
|
"github.com/authelia/authelia/v4/internal/oidc"
|
||||||
|
@ -117,7 +118,7 @@ func OpenIDConnectAuthorization(ctx *middlewares.AutheliaCtx, rw http.ResponseWr
|
||||||
|
|
||||||
extraClaims := oidcGrantRequests(requester, consent, &userSession)
|
extraClaims := oidcGrantRequests(requester, consent, &userSession)
|
||||||
|
|
||||||
if authTime, err = userSession.AuthenticatedTime(client.GetAuthorizationPolicy()); err != nil {
|
if authTime, err = userSession.AuthenticatedTime(client.GetAuthorizationPolicy(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.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, fosite.ErrServerError.WithHint("Could not obtain the authentication time."))
|
||||||
|
|
|
@ -11,6 +11,7 @@ import (
|
||||||
"github.com/google/uuid"
|
"github.com/google/uuid"
|
||||||
"github.com/ory/fosite"
|
"github.com/ory/fosite"
|
||||||
|
|
||||||
|
"github.com/authelia/authelia/v4/internal/authorization"
|
||||||
"github.com/authelia/authelia/v4/internal/middlewares"
|
"github.com/authelia/authelia/v4/internal/middlewares"
|
||||||
"github.com/authelia/authelia/v4/internal/model"
|
"github.com/authelia/authelia/v4/internal/model"
|
||||||
"github.com/authelia/authelia/v4/internal/oidc"
|
"github.com/authelia/authelia/v4/internal/oidc"
|
||||||
|
@ -28,10 +29,12 @@ func handleOIDCAuthorizationConsent(ctx *middlewares.AutheliaCtx, issuer *url.UR
|
||||||
|
|
||||||
var handler handlerAuthorizationConsent
|
var handler handlerAuthorizationConsent
|
||||||
|
|
||||||
|
policy := client.GetAuthorizationPolicy(authorization.Subject{Username: userSession.Username, Groups: userSession.Groups, IP: ctx.RemoteIP()})
|
||||||
|
|
||||||
switch {
|
switch {
|
||||||
case userSession.IsAnonymous():
|
case userSession.IsAnonymous():
|
||||||
handler = handleOIDCAuthorizationConsentNotAuthenticated
|
handler = handleOIDCAuthorizationConsentNotAuthenticated
|
||||||
case client.IsAuthenticationLevelSufficient(userSession.AuthenticationLevel):
|
case authorization.IsAuthLevelSufficient(userSession.AuthenticationLevel, policy):
|
||||||
if subject, err = ctx.Providers.OpenIDConnect.GetSubject(ctx, client.GetSectorIdentifier(), userSession.Username); err != nil {
|
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)
|
ctx.Logger.Errorf(logFmtErrConsentCantGetSubject, requester.GetID(), client.GetID(), client.GetConsentPolicy(), userSession.Username, client.GetSectorIdentifier(), err)
|
||||||
|
|
||||||
|
@ -55,6 +58,14 @@ func handleOIDCAuthorizationConsent(ctx *middlewares.AutheliaCtx, issuer *url.UR
|
||||||
return nil, true
|
return nil, true
|
||||||
}
|
}
|
||||||
default:
|
default:
|
||||||
|
if policy == authorization.Denied {
|
||||||
|
ctx.Logger.Errorf("Authorization Request with id '%s' on client with id '%s' could not be processed: the user '%s' is not authorized to use this client", requester.GetID(), client.GetID(), userSession.Username)
|
||||||
|
|
||||||
|
ctx.Providers.OpenIDConnect.WriteAuthorizeError(ctx, rw, requester, oidc.ErrClientAuthorizationUserAccessDenied)
|
||||||
|
|
||||||
|
return nil, true
|
||||||
|
}
|
||||||
|
|
||||||
if subject, err = ctx.Providers.OpenIDConnect.GetSubject(ctx, client.GetSectorIdentifier(), userSession.Username); err != nil {
|
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)
|
ctx.Logger.Errorf(logFmtErrConsentCantGetSubject, requester.GetID(), client.GetID(), client.GetConsentPolicy(), userSession.Username, client.GetSectorIdentifier(), err)
|
||||||
|
|
||||||
|
@ -121,7 +132,7 @@ func handleOIDCAuthorizationConsentRedirect(ctx *middlewares.AutheliaCtx, issuer
|
||||||
userSession session.UserSession, rw http.ResponseWriter, r *http.Request, requester fosite.AuthorizeRequester) {
|
userSession session.UserSession, rw http.ResponseWriter, r *http.Request, requester fosite.AuthorizeRequester) {
|
||||||
var location *url.URL
|
var location *url.URL
|
||||||
|
|
||||||
if client.IsAuthenticationLevelSufficient(userSession.AuthenticationLevel) {
|
if client.IsAuthenticationLevelSufficient(userSession.AuthenticationLevel, authorization.Subject{Username: userSession.Username, Groups: userSession.Groups, IP: ctx.RemoteIP()}) {
|
||||||
location, _ = url.ParseRequestURI(issuer.String())
|
location, _ = url.ParseRequestURI(issuer.String())
|
||||||
location.Path = path.Join(location.Path, oidc.EndpointPathConsent)
|
location.Path = path.Join(location.Path, oidc.EndpointPathConsent)
|
||||||
|
|
||||||
|
@ -130,11 +141,11 @@ func handleOIDCAuthorizationConsentRedirect(ctx *middlewares.AutheliaCtx, issuer
|
||||||
|
|
||||||
location.RawQuery = query.Encode()
|
location.RawQuery = query.Encode()
|
||||||
|
|
||||||
ctx.Logger.Debugf(logFmtDbgConsentAuthenticationSufficiency, requester.GetID(), client.GetID(), client.GetConsentPolicy(), userSession.AuthenticationLevel.String(), "sufficient", client.GetAuthorizationPolicy())
|
ctx.Logger.Debugf(logFmtDbgConsentAuthenticationSufficiency, requester.GetID(), client.GetID(), client.GetConsentPolicy(), userSession.AuthenticationLevel.String(), "sufficient", client.GetAuthorizationPolicy(authorization.Subject{Username: userSession.Username, Groups: userSession.Groups, IP: ctx.RemoteIP()}))
|
||||||
} else {
|
} else {
|
||||||
location = handleOIDCAuthorizationConsentGetRedirectionURL(issuer, consent, requester)
|
location = handleOIDCAuthorizationConsentGetRedirectionURL(issuer, consent, requester)
|
||||||
|
|
||||||
ctx.Logger.Debugf(logFmtDbgConsentAuthenticationSufficiency, requester.GetID(), client.GetID(), client.GetConsentPolicy(), userSession.AuthenticationLevel.String(), "insufficient", client.GetAuthorizationPolicy())
|
ctx.Logger.Debugf(logFmtDbgConsentAuthenticationSufficiency, requester.GetID(), client.GetID(), client.GetConsentPolicy(), userSession.AuthenticationLevel.String(), "insufficient", client.GetAuthorizationPolicy(authorization.Subject{Username: userSession.Username, Groups: userSession.Groups, IP: ctx.RemoteIP()}))
|
||||||
}
|
}
|
||||||
|
|
||||||
ctx.Logger.Debugf(logFmtDbgConsentRedirect, requester.GetID(), client.GetID(), client.GetConsentPolicy(), location)
|
ctx.Logger.Debugf(logFmtDbgConsentRedirect, requester.GetID(), client.GetID(), client.GetConsentPolicy(), location)
|
||||||
|
|
|
@ -10,6 +10,7 @@ import (
|
||||||
|
|
||||||
"github.com/google/uuid"
|
"github.com/google/uuid"
|
||||||
|
|
||||||
|
"github.com/authelia/authelia/v4/internal/authorization"
|
||||||
"github.com/authelia/authelia/v4/internal/middlewares"
|
"github.com/authelia/authelia/v4/internal/middlewares"
|
||||||
"github.com/authelia/authelia/v4/internal/model"
|
"github.com/authelia/authelia/v4/internal/model"
|
||||||
"github.com/authelia/authelia/v4/internal/oidc"
|
"github.com/authelia/authelia/v4/internal/oidc"
|
||||||
|
@ -86,6 +87,14 @@ func OpenIDConnectConsentPOST(ctx *middlewares.AutheliaCtx) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if !client.IsAuthenticationLevelSufficient(userSession.AuthenticationLevel, authorization.Subject{Username: userSession.Username, Groups: userSession.Groups, IP: ctx.RemoteIP()}) {
|
||||||
|
ctx.Logger.Errorf("User '%s' can't consent to authorization request for client with id '%s' as they are not sufficiently authenticated",
|
||||||
|
userSession.Username, consent.ClientID)
|
||||||
|
ctx.SetJSONError(messageOperationFailed)
|
||||||
|
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
if bodyJSON.Consent {
|
if bodyJSON.Consent {
|
||||||
consent.Grant()
|
consent.Grant()
|
||||||
|
|
||||||
|
@ -206,7 +215,7 @@ func oidcConsentGetSessionsAndClient(ctx *middlewares.AutheliaCtx, consentID uui
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if !client.IsAuthenticationLevelSufficient(userSession.AuthenticationLevel) {
|
if !client.IsAuthenticationLevelSufficient(userSession.AuthenticationLevel, authorization.Subject{Username: userSession.Username, Groups: userSession.Groups, IP: ctx.RemoteIP()}) {
|
||||||
ctx.Logger.Errorf("Unable to perform OpenID Connect Consent for user '%s' and client id '%s': the user is not sufficiently authenticated", userSession.Username, consent.ClientID)
|
ctx.Logger.Errorf("Unable to perform OpenID Connect Consent for user '%s' and client id '%s': the user is not sufficiently authenticated", userSession.Username, consent.ClientID)
|
||||||
ctx.ReplyForbidden()
|
ctx.ReplyForbidden()
|
||||||
|
|
||||||
|
|
|
@ -221,13 +221,10 @@ func handleOIDCWorkflowResponseWithID(ctx *middlewares.AutheliaCtx, id string) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
if !client.IsAuthenticationLevelSufficient(userSession.AuthenticationLevel) {
|
policy := client.GetAuthorizationPolicy(authorization.Subject{Username: userSession.Username, Groups: userSession.Groups, IP: ctx.RemoteIP()})
|
||||||
ctx.Logger.Warnf("OpenID Connect client '%s' requires 2FA, cannot be redirected yet", client.GetID())
|
|
||||||
ctx.ReplyOK()
|
|
||||||
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
|
switch {
|
||||||
|
case authorization.IsAuthLevelSufficient(userSession.AuthenticationLevel, policy), policy == authorization.Denied:
|
||||||
var (
|
var (
|
||||||
targetURL *url.URL
|
targetURL *url.URL
|
||||||
form url.Values
|
form url.Values
|
||||||
|
@ -249,6 +246,12 @@ func handleOIDCWorkflowResponseWithID(ctx *middlewares.AutheliaCtx, id string) {
|
||||||
if err = ctx.SetJSONBody(redirectResponse{Redirect: targetURL.String()}); err != nil {
|
if err = ctx.SetJSONBody(redirectResponse{Redirect: targetURL.String()}); err != nil {
|
||||||
ctx.Logger.Errorf("Unable to set default redirection URL in body: %s", err)
|
ctx.Logger.Errorf("Unable to set default redirection URL in body: %s", err)
|
||||||
}
|
}
|
||||||
|
default:
|
||||||
|
ctx.Logger.Warnf("OpenID Connect client '%s' requires 2FA, cannot be redirected yet", client.GetID())
|
||||||
|
ctx.ReplyOK()
|
||||||
|
|
||||||
|
return
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func markAuthenticationAttempt(ctx *middlewares.AutheliaCtx, successful bool, bannedUntil *time.Time, username string, authType string, errAuth error) (err error) {
|
func markAuthenticationAttempt(ctx *middlewares.AutheliaCtx, successful bool, bannedUntil *time.Time, username string, authType string, errAuth error) (err error) {
|
||||||
|
|
|
@ -13,7 +13,7 @@ import (
|
||||||
)
|
)
|
||||||
|
|
||||||
// NewClient creates a new Client.
|
// NewClient creates a new Client.
|
||||||
func NewClient(config schema.OpenIDConnectClient) (client Client) {
|
func NewClient(config schema.OpenIDConnectClient, c *schema.OpenIDConnect) (client Client) {
|
||||||
base := &BaseClient{
|
base := &BaseClient{
|
||||||
ID: config.ID,
|
ID: config.ID,
|
||||||
Description: config.Description,
|
Description: config.Description,
|
||||||
|
@ -39,8 +39,7 @@ func NewClient(config schema.OpenIDConnectClient) (client Client) {
|
||||||
UserinfoSigningAlg: config.UserinfoSigningAlg,
|
UserinfoSigningAlg: config.UserinfoSigningAlg,
|
||||||
UserinfoSigningKeyID: config.UserinfoSigningKeyID,
|
UserinfoSigningKeyID: config.UserinfoSigningKeyID,
|
||||||
|
|
||||||
Policy: authorization.NewLevel(config.Policy),
|
Policy: NewClientPolicy(config.Policy, c),
|
||||||
|
|
||||||
Consent: NewClientConsent(config.ConsentMode, config.ConsentPreConfiguredDuration),
|
Consent: NewClientConsent(config.ConsentMode, config.ConsentPreConfiguredDuration),
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -192,9 +191,18 @@ func (c *BaseClient) GetPKCEChallengeMethod() string {
|
||||||
return c.PKCEChallengeMethod
|
return c.PKCEChallengeMethod
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// IsAuthenticationLevelSufficient returns if the provided authentication.Level is sufficient for the client of the AutheliaClient.
|
||||||
|
func (c *BaseClient) IsAuthenticationLevelSufficient(level authentication.Level, subject authorization.Subject) bool {
|
||||||
|
if level == authentication.NotAuthenticated {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
return authorization.IsAuthLevelSufficient(level, c.GetAuthorizationPolicy(subject))
|
||||||
|
}
|
||||||
|
|
||||||
// GetAuthorizationPolicy returns Policy.
|
// GetAuthorizationPolicy returns Policy.
|
||||||
func (c *BaseClient) GetAuthorizationPolicy() authorization.Level {
|
func (c *BaseClient) GetAuthorizationPolicy(subject authorization.Subject) authorization.Level {
|
||||||
return c.Policy
|
return c.Policy.GetRequiredLevel(subject)
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetConsentPolicy returns Consent.
|
// GetConsentPolicy returns Consent.
|
||||||
|
@ -223,15 +231,6 @@ func (c *BaseClient) IsPublic() bool {
|
||||||
return c.Public
|
return c.Public
|
||||||
}
|
}
|
||||||
|
|
||||||
// IsAuthenticationLevelSufficient returns if the provided authentication.Level is sufficient for the client of the AutheliaClient.
|
|
||||||
func (c *BaseClient) IsAuthenticationLevelSufficient(level authentication.Level) bool {
|
|
||||||
if level == authentication.NotAuthenticated {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
return authorization.IsAuthLevelSufficient(level, c.Policy)
|
|
||||||
}
|
|
||||||
|
|
||||||
// ValidatePKCEPolicy is a helper function to validate PKCE policy constraints on a per-client basis.
|
// ValidatePKCEPolicy is a helper function to validate PKCE policy constraints on a per-client basis.
|
||||||
func (c *BaseClient) ValidatePKCEPolicy(r fosite.Requester) (err error) {
|
func (c *BaseClient) ValidatePKCEPolicy(r fosite.Requester) (err error) {
|
||||||
form := r.GetRequestForm()
|
form := r.GetRequestForm()
|
||||||
|
|
|
@ -0,0 +1,73 @@
|
||||||
|
package oidc
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/authelia/authelia/v4/internal/authorization"
|
||||||
|
"github.com/authelia/authelia/v4/internal/configuration/schema"
|
||||||
|
)
|
||||||
|
|
||||||
|
func NewClientPolicy(name string, config *schema.OpenIDConnect) (policy ClientPolicy) {
|
||||||
|
switch name {
|
||||||
|
case authorization.OneFactor.String(), authorization.TwoFactor.String():
|
||||||
|
return ClientPolicy{DefaultPolicy: authorization.NewLevel(name)}
|
||||||
|
default:
|
||||||
|
if p, ok := config.Policies[name]; ok {
|
||||||
|
policy.DefaultPolicy = authorization.NewLevel(p.DefaultPolicy)
|
||||||
|
|
||||||
|
for _, r := range p.Rules {
|
||||||
|
policy.Rules = append(policy.Rules, ClientPolicyRule{
|
||||||
|
Policy: authorization.NewLevel(r.Policy),
|
||||||
|
Subjects: authorization.NewSubjects(r.Subjects),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
return policy
|
||||||
|
}
|
||||||
|
|
||||||
|
return ClientPolicy{DefaultPolicy: authorization.TwoFactor}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// ClientPolicy controls and represents a client policy.
|
||||||
|
type ClientPolicy struct {
|
||||||
|
DefaultPolicy authorization.Level
|
||||||
|
Rules []ClientPolicyRule
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *ClientPolicy) GetRequiredLevel(subject authorization.Subject) authorization.Level {
|
||||||
|
for _, rule := range p.Rules {
|
||||||
|
if rule.IsMatch(subject) {
|
||||||
|
return rule.Policy
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return p.DefaultPolicy
|
||||||
|
}
|
||||||
|
|
||||||
|
type ClientPolicyRule struct {
|
||||||
|
Subjects []authorization.AccessControlSubjects
|
||||||
|
Policy authorization.Level
|
||||||
|
}
|
||||||
|
|
||||||
|
// MatchesSubjects returns true if the rule matches the subjects.
|
||||||
|
func (p *ClientPolicyRule) MatchesSubjects(subject authorization.Subject) (match bool) {
|
||||||
|
// If there are no subjects in this rule then the subject condition is a match.
|
||||||
|
if len(p.Subjects) == 0 {
|
||||||
|
return true
|
||||||
|
} else if subject.IsAnonymous() {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
// Iterate over the subjects until we find a match (return true) or until we exit the loop (return false).
|
||||||
|
for _, rule := range p.Subjects {
|
||||||
|
if rule.IsMatch(subject) {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
// IsMatch returns true if all elements of an AccessControlRule match the object and subject.
|
||||||
|
func (p *ClientPolicyRule) IsMatch(subject authorization.Subject) (match bool) {
|
||||||
|
return p.MatchesSubjects(subject)
|
||||||
|
}
|
|
@ -19,7 +19,7 @@ import (
|
||||||
|
|
||||||
func TestNewClient(t *testing.T) {
|
func TestNewClient(t *testing.T) {
|
||||||
config := schema.OpenIDConnectClient{}
|
config := schema.OpenIDConnectClient{}
|
||||||
client := oidc.NewClient(config)
|
client := oidc.NewClient(config, &schema.OpenIDConnect{})
|
||||||
assert.Equal(t, "", client.GetID())
|
assert.Equal(t, "", client.GetID())
|
||||||
assert.Equal(t, "", client.GetDescription())
|
assert.Equal(t, "", client.GetDescription())
|
||||||
assert.Len(t, client.GetResponseModes(), 0)
|
assert.Len(t, client.GetResponseModes(), 0)
|
||||||
|
@ -47,17 +47,17 @@ func TestNewClient(t *testing.T) {
|
||||||
ResponseModes: schema.DefaultOpenIDConnectClientConfiguration.ResponseModes,
|
ResponseModes: schema.DefaultOpenIDConnectClientConfiguration.ResponseModes,
|
||||||
}
|
}
|
||||||
|
|
||||||
client = oidc.NewClient(config)
|
client = oidc.NewClient(config, &schema.OpenIDConnect{})
|
||||||
assert.Equal(t, myclient, client.GetID())
|
assert.Equal(t, myclient, client.GetID())
|
||||||
require.Len(t, client.GetResponseModes(), 1)
|
require.Len(t, client.GetResponseModes(), 1)
|
||||||
assert.Equal(t, fosite.ResponseModeFormPost, client.GetResponseModes()[0])
|
assert.Equal(t, fosite.ResponseModeFormPost, client.GetResponseModes()[0])
|
||||||
assert.Equal(t, authorization.TwoFactor, client.GetAuthorizationPolicy())
|
assert.Equal(t, authorization.TwoFactor, client.GetAuthorizationPolicy(authorization.Subject{}))
|
||||||
|
|
||||||
config = schema.OpenIDConnectClient{
|
config = schema.OpenIDConnectClient{
|
||||||
TokenEndpointAuthMethod: oidc.ClientAuthMethodClientSecretPost,
|
TokenEndpointAuthMethod: oidc.ClientAuthMethodClientSecretPost,
|
||||||
}
|
}
|
||||||
|
|
||||||
client = oidc.NewClient(config)
|
client = oidc.NewClient(config, &schema.OpenIDConnect{})
|
||||||
|
|
||||||
fclient, ok := client.(*oidc.FullClient)
|
fclient, ok := client.(*oidc.FullClient)
|
||||||
|
|
||||||
|
@ -204,25 +204,25 @@ func TestBaseClient_ValidatePARPolicy(t *testing.T) {
|
||||||
func TestIsAuthenticationLevelSufficient(t *testing.T) {
|
func TestIsAuthenticationLevelSufficient(t *testing.T) {
|
||||||
c := &oidc.FullClient{BaseClient: &oidc.BaseClient{}}
|
c := &oidc.FullClient{BaseClient: &oidc.BaseClient{}}
|
||||||
|
|
||||||
c.Policy = authorization.Bypass
|
c.Policy = oidc.ClientPolicy{DefaultPolicy: authorization.Bypass}
|
||||||
assert.False(t, c.IsAuthenticationLevelSufficient(authentication.NotAuthenticated))
|
assert.False(t, c.IsAuthenticationLevelSufficient(authentication.NotAuthenticated, authorization.Subject{}))
|
||||||
assert.True(t, c.IsAuthenticationLevelSufficient(authentication.OneFactor))
|
assert.True(t, c.IsAuthenticationLevelSufficient(authentication.OneFactor, authorization.Subject{}))
|
||||||
assert.True(t, c.IsAuthenticationLevelSufficient(authentication.TwoFactor))
|
assert.True(t, c.IsAuthenticationLevelSufficient(authentication.TwoFactor, authorization.Subject{}))
|
||||||
|
|
||||||
c.Policy = authorization.OneFactor
|
c.Policy = oidc.ClientPolicy{DefaultPolicy: authorization.OneFactor}
|
||||||
assert.False(t, c.IsAuthenticationLevelSufficient(authentication.NotAuthenticated))
|
assert.False(t, c.IsAuthenticationLevelSufficient(authentication.NotAuthenticated, authorization.Subject{}))
|
||||||
assert.True(t, c.IsAuthenticationLevelSufficient(authentication.OneFactor))
|
assert.True(t, c.IsAuthenticationLevelSufficient(authentication.OneFactor, authorization.Subject{}))
|
||||||
assert.True(t, c.IsAuthenticationLevelSufficient(authentication.TwoFactor))
|
assert.True(t, c.IsAuthenticationLevelSufficient(authentication.TwoFactor, authorization.Subject{}))
|
||||||
|
|
||||||
c.Policy = authorization.TwoFactor
|
c.Policy = oidc.ClientPolicy{DefaultPolicy: authorization.TwoFactor}
|
||||||
assert.False(t, c.IsAuthenticationLevelSufficient(authentication.NotAuthenticated))
|
assert.False(t, c.IsAuthenticationLevelSufficient(authentication.NotAuthenticated, authorization.Subject{}))
|
||||||
assert.False(t, c.IsAuthenticationLevelSufficient(authentication.OneFactor))
|
assert.False(t, c.IsAuthenticationLevelSufficient(authentication.OneFactor, authorization.Subject{}))
|
||||||
assert.True(t, c.IsAuthenticationLevelSufficient(authentication.TwoFactor))
|
assert.True(t, c.IsAuthenticationLevelSufficient(authentication.TwoFactor, authorization.Subject{}))
|
||||||
|
|
||||||
c.Policy = authorization.Denied
|
c.Policy = oidc.ClientPolicy{DefaultPolicy: authorization.Denied}
|
||||||
assert.False(t, c.IsAuthenticationLevelSufficient(authentication.NotAuthenticated))
|
assert.False(t, c.IsAuthenticationLevelSufficient(authentication.NotAuthenticated, authorization.Subject{}))
|
||||||
assert.False(t, c.IsAuthenticationLevelSufficient(authentication.OneFactor))
|
assert.False(t, c.IsAuthenticationLevelSufficient(authentication.OneFactor, authorization.Subject{}))
|
||||||
assert.False(t, c.IsAuthenticationLevelSufficient(authentication.TwoFactor))
|
assert.False(t, c.IsAuthenticationLevelSufficient(authentication.TwoFactor, authorization.Subject{}))
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestClient_GetConsentResponseBody(t *testing.T) {
|
func TestClient_GetConsentResponseBody(t *testing.T) {
|
||||||
|
@ -446,7 +446,7 @@ func TestNewClientPKCE(t *testing.T) {
|
||||||
|
|
||||||
for _, tc := range testCases {
|
for _, tc := range testCases {
|
||||||
t.Run(tc.name, func(t *testing.T) {
|
t.Run(tc.name, func(t *testing.T) {
|
||||||
client := oidc.NewClient(tc.have)
|
client := oidc.NewClient(tc.have, &schema.OpenIDConnect{})
|
||||||
|
|
||||||
assert.Equal(t, tc.expectedEnforcePKCE, client.GetPKCEEnforcement())
|
assert.Equal(t, tc.expectedEnforcePKCE, client.GetPKCEEnforcement())
|
||||||
assert.Equal(t, tc.expectedEnforcePKCEChallengeMethod, client.GetPKCEChallengeMethodEnforcement())
|
assert.Equal(t, tc.expectedEnforcePKCEChallengeMethod, client.GetPKCEChallengeMethodEnforcement())
|
||||||
|
@ -511,7 +511,7 @@ func TestNewClientPAR(t *testing.T) {
|
||||||
|
|
||||||
for _, tc := range testCases {
|
for _, tc := range testCases {
|
||||||
t.Run(tc.name, func(t *testing.T) {
|
t.Run(tc.name, func(t *testing.T) {
|
||||||
client := oidc.NewClient(tc.have)
|
client := oidc.NewClient(tc.have, &schema.OpenIDConnect{})
|
||||||
|
|
||||||
assert.Equal(t, tc.expected, client.GetPAREnforcement())
|
assert.Equal(t, tc.expected, client.GetPAREnforcement())
|
||||||
|
|
||||||
|
@ -575,7 +575,7 @@ func TestNewClientResponseModes(t *testing.T) {
|
||||||
|
|
||||||
for _, tc := range testCases {
|
for _, tc := range testCases {
|
||||||
t.Run(tc.name, func(t *testing.T) {
|
t.Run(tc.name, func(t *testing.T) {
|
||||||
client := oidc.NewClient(tc.have)
|
client := oidc.NewClient(tc.have, &schema.OpenIDConnect{})
|
||||||
|
|
||||||
assert.Equal(t, tc.expected, client.GetResponseModes())
|
assert.Equal(t, tc.expected, client.GetResponseModes())
|
||||||
|
|
||||||
|
@ -615,7 +615,7 @@ func TestNewClient_JSONWebKeySetURI(t *testing.T) {
|
||||||
PublicKeys: schema.OpenIDConnectClientPublicKeys{
|
PublicKeys: schema.OpenIDConnectClientPublicKeys{
|
||||||
URI: MustParseRequestURI("https://google.com"),
|
URI: MustParseRequestURI("https://google.com"),
|
||||||
},
|
},
|
||||||
})
|
}, &schema.OpenIDConnect{})
|
||||||
|
|
||||||
require.NotNil(t, client)
|
require.NotNil(t, client)
|
||||||
|
|
||||||
|
@ -630,7 +630,7 @@ func TestNewClient_JSONWebKeySetURI(t *testing.T) {
|
||||||
PublicKeys: schema.OpenIDConnectClientPublicKeys{
|
PublicKeys: schema.OpenIDConnectClientPublicKeys{
|
||||||
URI: nil,
|
URI: nil,
|
||||||
},
|
},
|
||||||
})
|
}, &schema.OpenIDConnect{})
|
||||||
|
|
||||||
require.NotNil(t, client)
|
require.NotNil(t, client)
|
||||||
|
|
||||||
|
|
|
@ -32,4 +32,6 @@ var (
|
||||||
|
|
||||||
// ErrPAREnforcedClientMissingPAR is sent when a client has EnforcePAR configured but the Authorization Request was not Pushed.
|
// ErrPAREnforcedClientMissingPAR is sent when a client has EnforcePAR 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.")
|
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.")
|
||||||
)
|
)
|
||||||
|
|
|
@ -31,7 +31,7 @@ func NewStore(config *schema.OpenIDConnect, provider storage.Provider) (store *S
|
||||||
policy := authorization.NewLevel(client.Policy)
|
policy := authorization.NewLevel(client.Policy)
|
||||||
logger.Debugf("Registering client %s with policy %s (%v)", client.ID, client.Policy, policy)
|
logger.Debugf("Registering client %s with policy %s (%v)", client.ID, client.Policy, policy)
|
||||||
|
|
||||||
store.clients[client.ID] = NewClient(client)
|
store.clients[client.ID] = NewClient(client, config)
|
||||||
}
|
}
|
||||||
|
|
||||||
return store
|
return store
|
||||||
|
@ -65,16 +65,6 @@ func (s *Store) GetSubject(ctx context.Context, sectorID, username string) (subj
|
||||||
return opaqueID.Identifier, nil
|
return opaqueID.Identifier, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetClientPolicy retrieves the policy from the client with the matching provided id.
|
|
||||||
func (s *Store) GetClientPolicy(id string) (level authorization.Level) {
|
|
||||||
client, err := s.GetFullClient(id)
|
|
||||||
if err != nil {
|
|
||||||
return authorization.TwoFactor
|
|
||||||
}
|
|
||||||
|
|
||||||
return client.GetAuthorizationPolicy()
|
|
||||||
}
|
|
||||||
|
|
||||||
// GetFullClient returns a fosite.Client asserted as an Client matching the provided id.
|
// GetFullClient returns a fosite.Client asserted as an Client matching the provided id.
|
||||||
func (s *Store) GetFullClient(id string) (client Client, err error) {
|
func (s *Store) GetFullClient(id string) (client Client, err error) {
|
||||||
client, ok := s.clients[id]
|
client, ok := s.clients[id]
|
||||||
|
|
|
@ -23,38 +23,6 @@ import (
|
||||||
"github.com/authelia/authelia/v4/internal/storage"
|
"github.com/authelia/authelia/v4/internal/storage"
|
||||||
)
|
)
|
||||||
|
|
||||||
func TestOpenIDConnectStore_GetClientPolicy(t *testing.T) {
|
|
||||||
s := oidc.NewStore(&schema.OpenIDConnect{
|
|
||||||
IssuerCertificateChain: schema.X509CertificateChain{},
|
|
||||||
IssuerPrivateKey: keyRSA2048,
|
|
||||||
Clients: []schema.OpenIDConnectClient{
|
|
||||||
{
|
|
||||||
ID: myclient,
|
|
||||||
Description: myclientdesc,
|
|
||||||
Policy: onefactor,
|
|
||||||
Scopes: []string{oidc.ScopeOpenID, oidc.ScopeProfile},
|
|
||||||
Secret: tOpenIDConnectPlainTextClientSecret,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
ID: "myotherclient",
|
|
||||||
Description: myclientdesc,
|
|
||||||
Policy: twofactor,
|
|
||||||
Scopes: []string{oidc.ScopeOpenID, oidc.ScopeProfile},
|
|
||||||
Secret: tOpenIDConnectPlainTextClientSecret,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
}, nil)
|
|
||||||
|
|
||||||
policyOne := s.GetClientPolicy(myclient)
|
|
||||||
assert.Equal(t, authorization.OneFactor, policyOne)
|
|
||||||
|
|
||||||
policyTwo := s.GetClientPolicy("myotherclient")
|
|
||||||
assert.Equal(t, authorization.TwoFactor, policyTwo)
|
|
||||||
|
|
||||||
policyInvalid := s.GetClientPolicy("invalidclient")
|
|
||||||
assert.Equal(t, authorization.TwoFactor, policyInvalid)
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestOpenIDConnectStore_GetInternalClient(t *testing.T) {
|
func TestOpenIDConnectStore_GetInternalClient(t *testing.T) {
|
||||||
s := oidc.NewStore(&schema.OpenIDConnect{
|
s := oidc.NewStore(&schema.OpenIDConnect{
|
||||||
IssuerCertificateChain: schema.X509CertificateChain{},
|
IssuerCertificateChain: schema.X509CertificateChain{},
|
||||||
|
@ -106,7 +74,7 @@ func TestOpenIDConnectStore_GetInternalClient_ValidClient(t *testing.T) {
|
||||||
assert.Equal(t, fosite.Arguments([]string{oidc.GrantTypeAuthorizationCode}), client.GetGrantTypes())
|
assert.Equal(t, fosite.Arguments([]string{oidc.GrantTypeAuthorizationCode}), client.GetGrantTypes())
|
||||||
assert.Equal(t, fosite.Arguments([]string{oidc.ResponseTypeAuthorizationCodeFlow}), client.GetResponseTypes())
|
assert.Equal(t, fosite.Arguments([]string{oidc.ResponseTypeAuthorizationCodeFlow}), client.GetResponseTypes())
|
||||||
assert.Equal(t, []string(nil), client.GetRedirectURIs())
|
assert.Equal(t, []string(nil), client.GetRedirectURIs())
|
||||||
assert.Equal(t, authorization.OneFactor, client.GetAuthorizationPolicy())
|
assert.Equal(t, authorization.OneFactor, client.GetAuthorizationPolicy(authorization.Subject{}))
|
||||||
assert.Equal(t, "$plaintext$client-secret", client.GetSecret().Encode())
|
assert.Equal(t, "$plaintext$client-secret", client.GetSecret().Encode())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -10,7 +10,7 @@ import (
|
||||||
"github.com/ory/fosite/handler/openid"
|
"github.com/ory/fosite/handler/openid"
|
||||||
"github.com/ory/fosite/token/jwt"
|
"github.com/ory/fosite/token/jwt"
|
||||||
"github.com/ory/herodot"
|
"github.com/ory/herodot"
|
||||||
"gopkg.in/square/go-jose.v2"
|
jose "gopkg.in/square/go-jose.v2"
|
||||||
|
|
||||||
"github.com/authelia/authelia/v4/internal/authentication"
|
"github.com/authelia/authelia/v4/internal/authentication"
|
||||||
"github.com/authelia/authelia/v4/internal/authorization"
|
"github.com/authelia/authelia/v4/internal/authorization"
|
||||||
|
@ -127,7 +127,7 @@ type BaseClient struct {
|
||||||
UserinfoSigningAlg string
|
UserinfoSigningAlg string
|
||||||
UserinfoSigningKeyID string
|
UserinfoSigningKeyID string
|
||||||
|
|
||||||
Policy authorization.Level
|
Policy ClientPolicy
|
||||||
|
|
||||||
Consent ClientConsent
|
Consent ClientConsent
|
||||||
}
|
}
|
||||||
|
@ -164,14 +164,14 @@ type Client interface {
|
||||||
GetPKCEEnforcement() bool
|
GetPKCEEnforcement() bool
|
||||||
GetPKCEChallengeMethodEnforcement() bool
|
GetPKCEChallengeMethodEnforcement() bool
|
||||||
GetPKCEChallengeMethod() string
|
GetPKCEChallengeMethod() string
|
||||||
GetAuthorizationPolicy() authorization.Level
|
|
||||||
GetConsentPolicy() ClientConsent
|
|
||||||
|
|
||||||
IsAuthenticationLevelSufficient(level authentication.Level) bool
|
|
||||||
|
|
||||||
ValidatePKCEPolicy(r fosite.Requester) (err error)
|
ValidatePKCEPolicy(r fosite.Requester) (err error)
|
||||||
ValidatePARPolicy(r fosite.Requester, prefix string) (err error)
|
ValidatePARPolicy(r fosite.Requester, prefix string) (err error)
|
||||||
ValidateResponseModePolicy(r fosite.AuthorizeRequester) (err error)
|
ValidateResponseModePolicy(r fosite.AuthorizeRequester) (err error)
|
||||||
|
|
||||||
|
GetConsentPolicy() ClientConsent
|
||||||
|
IsAuthenticationLevelSufficient(level authentication.Level, subject authorization.Subject) bool
|
||||||
|
GetAuthorizationPolicy(subject authorization.Subject) authorization.Level
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewClientConsent converts the schema.OpenIDConnectClientConsentConfig into a oidc.ClientConsent.
|
// NewClientConsent converts the schema.OpenIDConnectClientConsentConfig into a oidc.ClientConsent.
|
||||||
|
|
Loading…
Reference in New Issue