diff options
Diffstat (limited to 'internal/handlers')
| -rw-r--r-- | internal/handlers/handler_authz.go | 25 | ||||
| -rw-r--r-- | internal/handlers/handler_authz_authn.go | 210 | ||||
| -rw-r--r-- | internal/handlers/handler_authz_builder.go | 11 | ||||
| -rw-r--r-- | internal/handlers/handler_authz_test.go | 18 | ||||
| -rw-r--r-- | internal/handlers/handler_authz_types.go | 15 | ||||
| -rw-r--r-- | internal/handlers/handler_oidc_token.go | 12 | ||||
| -rw-r--r-- | internal/handlers/handler_oidc_userinfo.go | 2 |
7 files changed, 232 insertions, 61 deletions
diff --git a/internal/handlers/handler_authz.go b/internal/handlers/handler_authz.go index 859958aac..b1935863e 100644 --- a/internal/handlers/handler_authz.go +++ b/internal/handlers/handler_authz.go @@ -53,11 +53,11 @@ func (authz *Authz) Handler(ctx *middlewares.AutheliaCtx) { } var ( - authn Authn + authn *Authn strategy AuthnStrategy ) - if authn, strategy, err = authz.authn(ctx, provider); err != nil { + if authn, strategy, err = authz.authn(ctx, provider, &object); err != nil { authn.Object = object ctx.Logger.WithError(err).Error("Error occurred while attempting to authenticate a request") @@ -66,7 +66,7 @@ func (authz *Authz) Handler(ctx *middlewares.AutheliaCtx) { case nil: ctx.ReplyUnauthorized() default: - strategy.HandleUnauthorized(ctx, &authn, authz.getRedirectionURL(&object, autheliaURL)) + strategy.HandleUnauthorized(ctx, authn, authz.getRedirectionURL(&object, autheliaURL)) } return @@ -79,6 +79,7 @@ func (authz *Authz) Handler(ctx *middlewares.AutheliaCtx) { authorization.Subject{ Username: authn.Details.Username, Groups: authn.Details.Groups, + ClientID: authn.ClientID, IP: ctx.RemoteIP(), }, object, @@ -97,9 +98,9 @@ func (authz *Authz) Handler(ctx *middlewares.AutheliaCtx) { handler = authz.handleUnauthorized } - handler(ctx, &authn, authz.getRedirectionURL(&object, autheliaURL)) + handler(ctx, authn, authz.getRedirectionURL(&object, autheliaURL)) case AuthzResultAuthorized: - authz.handleAuthorized(ctx, &authn) + authz.handleAuthorized(ctx, authn) } } @@ -151,14 +152,20 @@ func (authz *Authz) getRedirectionURL(object *authorization.Object, autheliaURL return redirectionURL } -func (authz *Authz) authn(ctx *middlewares.AutheliaCtx, provider *session.Session) (authn Authn, strategy AuthnStrategy, err error) { +func (authz *Authz) authn(ctx *middlewares.AutheliaCtx, provider *session.Session, object *authorization.Object) (authn *Authn, strategy AuthnStrategy, err error) { for _, strategy = range authz.strategies { - if authn, err = strategy.Get(ctx, provider); err != nil { + if authn, err = strategy.Get(ctx, provider, object); err != nil { + // Ensure an error returned can never result in an authenticated user. + authn.Level = authentication.NotAuthenticated + authn.Username = anonymous + authn.ClientID = "" + authn.Details = authentication.UserDetails{} + if strategy.CanHandleUnauthorized() { - return Authn{Type: authn.Type, Level: authentication.NotAuthenticated, Username: anonymous}, strategy, err + return authn, strategy, err } - return Authn{Type: authn.Type, Level: authentication.NotAuthenticated, Username: anonymous}, nil, err + return authn, nil, err } if authn.Level != authentication.NotAuthenticated { diff --git a/internal/handlers/handler_authz_authn.go b/internal/handlers/handler_authz_authn.go index 853e98a5c..5a8b80224 100644 --- a/internal/handlers/handler_authz_authn.go +++ b/internal/handlers/handler_authz_authn.go @@ -2,6 +2,7 @@ package handlers import ( "bytes" + "context" "encoding/base64" "errors" "fmt" @@ -9,12 +10,16 @@ import ( "strings" "time" + "github.com/ory/fosite" "github.com/sirupsen/logrus" "github.com/valyala/fasthttp" "github.com/authelia/authelia/v4/internal/authentication" + "github.com/authelia/authelia/v4/internal/authorization" "github.com/authelia/authelia/v4/internal/configuration/schema" "github.com/authelia/authelia/v4/internal/middlewares" + "github.com/authelia/authelia/v4/internal/model" + "github.com/authelia/authelia/v4/internal/oidc" "github.com/authelia/authelia/v4/internal/session" "github.com/authelia/authelia/v4/internal/utils" ) @@ -28,38 +33,41 @@ func NewCookieSessionAuthnStrategy(refresh schema.RefreshIntervalDuration) *Cook // NewHeaderAuthorizationAuthnStrategy creates a new HeaderAuthnStrategy using the Authorization and WWW-Authenticate // headers, and the 407 Proxy Auth Required response. -func NewHeaderAuthorizationAuthnStrategy() *HeaderAuthnStrategy { +func NewHeaderAuthorizationAuthnStrategy(schemes ...string) *HeaderAuthnStrategy { return &HeaderAuthnStrategy{ authn: AuthnTypeAuthorization, headerAuthorize: headerAuthorization, headerAuthenticate: headerWWWAuthenticate, handleAuthenticate: true, statusAuthenticate: fasthttp.StatusUnauthorized, + schemes: model.NewAuthorizationSchemes(schemes...), } } // NewHeaderProxyAuthorizationAuthnStrategy creates a new HeaderAuthnStrategy using the Proxy-Authorization and // Proxy-Authenticate headers, and the 407 Proxy Auth Required response. -func NewHeaderProxyAuthorizationAuthnStrategy() *HeaderAuthnStrategy { +func NewHeaderProxyAuthorizationAuthnStrategy(schemes ...string) *HeaderAuthnStrategy { return &HeaderAuthnStrategy{ authn: AuthnTypeProxyAuthorization, headerAuthorize: headerProxyAuthorization, headerAuthenticate: headerProxyAuthenticate, handleAuthenticate: true, statusAuthenticate: fasthttp.StatusProxyAuthRequired, + schemes: model.NewAuthorizationSchemes(schemes...), } } // NewHeaderProxyAuthorizationAuthRequestAuthnStrategy creates a new HeaderAuthnStrategy using the Proxy-Authorization // and WWW-Authenticate headers, and the 401 Proxy Auth Required response. This is a special AuthnStrategy for the // AuthRequest implementation. -func NewHeaderProxyAuthorizationAuthRequestAuthnStrategy() *HeaderAuthnStrategy { +func NewHeaderProxyAuthorizationAuthRequestAuthnStrategy(schemes ...string) *HeaderAuthnStrategy { return &HeaderAuthnStrategy{ authn: AuthnTypeProxyAuthorization, headerAuthorize: headerProxyAuthorization, headerAuthenticate: headerWWWAuthenticate, handleAuthenticate: true, statusAuthenticate: fasthttp.StatusUnauthorized, + schemes: model.NewAuthorizationSchemes(schemes...), } } @@ -74,10 +82,10 @@ type CookieSessionAuthnStrategy struct { } // Get returns the Authn information for this AuthnStrategy. -func (s *CookieSessionAuthnStrategy) Get(ctx *middlewares.AutheliaCtx, provider *session.Session) (authn Authn, err error) { +func (s *CookieSessionAuthnStrategy) Get(ctx *middlewares.AutheliaCtx, provider *session.Session, _ *authorization.Object) (authn *Authn, err error) { var userSession session.UserSession - authn = Authn{ + authn = &Authn{ Type: AuthnTypeCookie, Level: authentication.NotAuthenticated, Username: anonymous, @@ -120,7 +128,7 @@ func (s *CookieSessionAuthnStrategy) Get(ctx *middlewares.AutheliaCtx, provider ctx.Logger.WithError(err).Error("Unable to save updated user session") } - return Authn{ + return &Authn{ Username: friendlyUsername(userSession.Username), Details: authentication.UserDetails{ Username: userSession.Username, @@ -149,75 +157,118 @@ type HeaderAuthnStrategy struct { headerAuthenticate []byte handleAuthenticate bool statusAuthenticate int + schemes model.AuthorizationSchemes } // Get returns the Authn information for this AuthnStrategy. -func (s *HeaderAuthnStrategy) Get(ctx *middlewares.AutheliaCtx, _ *session.Session) (authn Authn, err error) { - var ( - username, password string - value []byte - ) +func (s *HeaderAuthnStrategy) Get(ctx *middlewares.AutheliaCtx, _ *session.Session, object *authorization.Object) (authn *Authn, err error) { + var value []byte - authn = Authn{ + authn = &Authn{ Type: s.authn, Level: authentication.NotAuthenticated, Username: anonymous, } - if value = ctx.Request.Header.PeekBytes(s.headerAuthorize); value == nil { + if value = ctx.Request.Header.PeekBytes(s.headerAuthorize); len(value) == 0 { return authn, nil } - if username, password, err = headerAuthorizationParse(value); err != nil { + authz := model.NewAuthorization() + + if err = authz.ParseBytes(value); err != nil { return authn, fmt.Errorf("failed to parse content of %s header: %w", s.headerAuthorize, err) } - if username == "" || password == "" { - return authn, fmt.Errorf("failed to validate parsed credentials of %s header for user '%s': %w", s.headerAuthorize, username, err) - } + authn.Header.Authorization = authz var ( - valid bool - details *authentication.UserDetails + username, clientID string + + ccs bool + level authentication.Level ) - if valid, err = ctx.Providers.UserProvider.CheckUserPassword(username, password); err != nil { - return authn, fmt.Errorf("failed to validate parsed credentials of %s header for user '%s': %w", s.headerAuthorize, username, err) + scheme := authn.Header.Authorization.Scheme() + + if !s.schemes.Has(scheme) { + return authn, fmt.Errorf("invalid scheme: scheme with name '%s' isn't available on this endpoint", scheme.String()) } - if !valid { - return authn, fmt.Errorf("validated parsed credentials of %s header but they are not valid for user '%s': %w", s.headerAuthorize, username, err) + switch scheme { + case model.AuthorizationSchemeBasic: + username, level, err = s.handleGetBasic(ctx, authn, object) + case model.AuthorizationSchemeBearer: + username, clientID, ccs, level, err = handleVerifyGETAuthorizationBearer(ctx, authn, object) + default: + err = fmt.Errorf("failed to parse content of %s header: the scheme '%s' is not known", s.headerAuthorize, authn.Header.Authorization.SchemeRaw()) } - if details, err = ctx.Providers.UserProvider.GetDetails(username); err != nil { - if errors.Is(err, authentication.ErrUserNotFound) { - ctx.Logger.WithField("username", username).Error("Error occurred while attempting to get user details for user: the user was not found indicating they were deleted, disabled, or otherwise no longer authorized to login") + if err != nil { + return authn, fmt.Errorf("failed to validate %s header with %s scheme: %w", s.headerAuthorize, scheme, err) + } - return authn, err + switch { + case ccs: + if len(clientID) == 0 { + return authn, fmt.Errorf("failed to determine client id from the %s header", s.headerAuthorize) } - return authn, fmt.Errorf("unable to retrieve details for user '%s': %w", username, err) + authn.ClientID = clientID + case len(username) == 0: + return authn, fmt.Errorf("failed to determine username from the %s header", s.headerAuthorize) + default: + var details *authentication.UserDetails + + if details, err = ctx.Providers.UserProvider.GetDetails(username); err != nil { + if errors.Is(err, authentication.ErrUserNotFound) { + ctx.Logger.WithField("username", username).Error("Error occurred while attempting to get user details for user: the user was not found indicating they were deleted, disabled, or otherwise no longer authorized to login") + + return authn, err + } + + return authn, fmt.Errorf("unable to retrieve details for user '%s': %w", username, err) + } + + authn.Username = friendlyUsername(details.Username) + authn.Details = *details } - authn.Username = friendlyUsername(details.Username) - authn.Details = *details - authn.Level = authentication.OneFactor + authn.Level = level return authn, nil } +func (s *HeaderAuthnStrategy) handleGetBasic(ctx *middlewares.AutheliaCtx, authn *Authn, _ *authorization.Object) (username string, level authentication.Level, err error) { + var ( + valid bool + ) + + if valid, err = ctx.Providers.UserProvider.CheckUserPassword(authn.Header.Authorization.Basic()); err != nil { + return "", authentication.NotAuthenticated, fmt.Errorf("failed to validate parsed credentials of %s header for user '%s': %w", s.headerAuthorize, authn.Header.Authorization.BasicUsername(), err) + } + + if !valid { + return "", authentication.NotAuthenticated, fmt.Errorf("validated parsed credentials of %s header but they are not valid for user '%s': %w", s.headerAuthorize, authn.Header.Authorization.BasicUsername(), err) + } + + return authn.Header.Authorization.BasicUsername(), authentication.OneFactor, nil +} + // CanHandleUnauthorized returns true if this AuthnStrategy should handle Unauthorized requests. func (s *HeaderAuthnStrategy) CanHandleUnauthorized() (handle bool) { return s.handleAuthenticate } // HandleUnauthorized is the Unauthorized handler for the header AuthnStrategy. -func (s *HeaderAuthnStrategy) HandleUnauthorized(ctx *middlewares.AutheliaCtx, _ *Authn, _ *url.URL) { +func (s *HeaderAuthnStrategy) HandleUnauthorized(ctx *middlewares.AutheliaCtx, authn *Authn, _ *url.URL) { ctx.Logger.Debugf("Responding %d %s", s.statusAuthenticate, s.headerAuthenticate) ctx.ReplyStatusCode(s.statusAuthenticate) - if s.headerAuthenticate != nil { + if authn.Header.Authorization != nil && authn.Header.Authorization.Scheme() == model.AuthorizationSchemeBearer && authn.Header.Error != nil { + ctx.Response.Header.SetBytesK(s.headerAuthenticate, fmt.Sprintf(`Bearer %s`, oidc.RFC6750Header(authn.Header.Realm, authn.Header.Scope, authn.Header.Error))) + } else if s.headerAuthenticate != nil { ctx.Response.Header.SetBytesKV(s.headerAuthenticate, headerValueAuthenticateBasic) } } @@ -226,13 +277,13 @@ func (s *HeaderAuthnStrategy) HandleUnauthorized(ctx *middlewares.AutheliaCtx, _ type HeaderLegacyAuthnStrategy struct{} // Get returns the Authn information for this AuthnStrategy. -func (s *HeaderLegacyAuthnStrategy) Get(ctx *middlewares.AutheliaCtx, _ *session.Session) (authn Authn, err error) { +func (s *HeaderLegacyAuthnStrategy) Get(ctx *middlewares.AutheliaCtx, _ *session.Session, _ *authorization.Object) (authn *Authn, err error) { var ( username, password string value, header []byte ) - authn = Authn{ + authn = &Authn{ Level: authentication.NotAuthenticated, Username: anonymous, } @@ -402,6 +453,95 @@ func handleVerifyGETAuthnCookieValidateRefresh(ctx *middlewares.AutheliaCtx, use return false } +func handleVerifyGETAuthorizationBearer(ctx *middlewares.AutheliaCtx, authn *Authn, object *authorization.Object) (username, clientID string, ccs bool, level authentication.Level, err error) { + if ctx.Providers.OpenIDConnect == nil || ctx.Configuration.IdentityProviders.OIDC == nil || !ctx.Configuration.IdentityProviders.OIDC.Discovery.BearerAuthorization { + return "", "", false, authentication.NotAuthenticated, fmt.Errorf("authorization bearer scheme requires an OpenID Connect 1.0 configuration but it's absent") + } + + if !ctx.Configuration.IdentityProviders.OIDC.Discovery.BearerAuthorization { + return "", "", false, authentication.NotAuthenticated, fmt.Errorf("authorization bearer scheme requires an OAuth 2.0 or OpenID Connect 1.0 client to be registered with the '%s' scope but there are none", oidc.ScopeAutheliaBearerAuthz) + } + + return handleVerifyGETAuthorizationBearerIntrospection(ctx, ctx.Providers.OpenIDConnect, authn, object) +} + +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) +} + +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 + ) + + authn.Header.Error = &fosite.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 { + return "", "", false, authentication.NotAuthenticated, fmt.Errorf("error performing token introspection: %w", err) + } + + if use != fosite.AccessToken { + authn.Header.Error = fosite.ErrInvalidRequest + + return "", "", false, authentication.NotAuthenticated, fmt.Errorf("token is not an access token") + } + + audience := []string{object.URL.String()} + strategy := provider.GetAudienceStrategy(ctx) + + if err = strategy(requester.GetGrantedAudience(), audience); err != nil { + return "", "", false, authentication.NotAuthenticated, fmt.Errorf("token does not contain a valid audience for the url '%s' with the error: %w", audience[0], err) + } + + fsession := requester.GetSession() + + var ( + client oidc.Client + osession *oidc.Session + ok bool + ) + + if osession, ok = fsession.(*oidc.Session); !ok { + return "", "", false, authentication.NotAuthenticated, fmt.Errorf("introspection returned an invalid session type") + } + + if client, err = provider.GetFullClient(ctx, osession.ClientID); err != nil || client == nil { + return "", "", false, authentication.NotAuthenticated, fmt.Errorf("client id '%s' is not registered", osession.ClientID) + } + + if !client.GetScopes().Has(oidc.ScopeAutheliaBearerAuthz) { + return "", "", false, authentication.NotAuthenticated, fmt.Errorf("client id '%s' is registered but does not permit the '%s' scope", osession.ClientID, oidc.ScopeAutheliaBearerAuthz) + } + + if err = strategy(client.GetAudience(), audience); err != nil { + return "", "", false, authentication.NotAuthenticated, fmt.Errorf("client id '%s' is registered but does not permit an audience for the url '%s' with the error: %w", osession.ClientID, audience[0], err) + } + + if osession.DefaultSession == nil || osession.DefaultSession.Claims == nil { + return "", "", false, authentication.NotAuthenticated, fmt.Errorf("introspection returned a session missing required values") + } + + authn.Header.Error = nil + + if osession.ClientCredentials { + return "", osession.ClientID, true, authentication.OneFactor, nil + } + + if oidc.NewAuthenticationMethodsReferencesFromClaim(osession.DefaultSession.Claims.AuthenticationMethodsReferences).MultiFactorAuthentication() { + level = authentication.TwoFactor + } else { + level = authentication.OneFactor + } + + return osession.Username, "", false, level, nil +} + func headerAuthorizationParse(value []byte) (username, password string, err error) { if bytes.Equal(value, qryValueEmpty) { return "", "", fmt.Errorf("header is malformed: empty value") diff --git a/internal/handlers/handler_authz_builder.go b/internal/handlers/handler_authz_builder.go index 43be45c0d..c82b52c3c 100644 --- a/internal/handlers/handler_authz_builder.go +++ b/internal/handlers/handler_authz_builder.go @@ -4,6 +4,7 @@ import ( "github.com/valyala/fasthttp" "github.com/authelia/authelia/v4/internal/configuration/schema" + "github.com/authelia/authelia/v4/internal/model" ) // NewAuthzBuilder creates a new AuthzBuilder. @@ -87,11 +88,11 @@ func (b *AuthzBuilder) WithEndpointConfig(config schema.ServerEndpointsAuthz) *A case AuthnStrategyCookieSession: b.strategies = append(b.strategies, NewCookieSessionAuthnStrategy(b.config.RefreshInterval)) case AuthnStrategyHeaderAuthorization: - b.strategies = append(b.strategies, NewHeaderAuthorizationAuthnStrategy()) + b.strategies = append(b.strategies, NewHeaderAuthorizationAuthnStrategy(strategy.Schemes...)) case AuthnStrategyHeaderProxyAuthorization: - b.strategies = append(b.strategies, NewHeaderProxyAuthorizationAuthnStrategy()) + b.strategies = append(b.strategies, NewHeaderProxyAuthorizationAuthnStrategy(strategy.Schemes...)) case AuthnStrategyHeaderAuthRequestProxyAuthorization: - b.strategies = append(b.strategies, NewHeaderProxyAuthorizationAuthRequestAuthnStrategy()) + b.strategies = append(b.strategies, NewHeaderProxyAuthorizationAuthRequestAuthnStrategy(strategy.Schemes...)) case AuthnStrategyHeaderLegacy: b.strategies = append(b.strategies, NewHeaderLegacyAuthnStrategy()) } @@ -116,9 +117,9 @@ func (b *AuthzBuilder) Build() (authz *Authz) { case AuthzImplLegacy: authz.strategies = []AuthnStrategy{NewHeaderLegacyAuthnStrategy(), NewCookieSessionAuthnStrategy(b.config.RefreshInterval)} case AuthzImplAuthRequest: - authz.strategies = []AuthnStrategy{NewHeaderProxyAuthorizationAuthRequestAuthnStrategy(), NewCookieSessionAuthnStrategy(b.config.RefreshInterval)} + authz.strategies = []AuthnStrategy{NewHeaderProxyAuthorizationAuthRequestAuthnStrategy(model.AuthorizationSchemeBasic.String()), NewCookieSessionAuthnStrategy(b.config.RefreshInterval)} default: - authz.strategies = []AuthnStrategy{NewHeaderProxyAuthorizationAuthnStrategy(), NewCookieSessionAuthnStrategy(b.config.RefreshInterval)} + authz.strategies = []AuthnStrategy{NewHeaderProxyAuthorizationAuthnStrategy(model.AuthorizationSchemeBasic.String()), NewCookieSessionAuthnStrategy(b.config.RefreshInterval)} } } diff --git a/internal/handlers/handler_authz_test.go b/internal/handlers/handler_authz_test.go index 559a5cb57..dbba67e46 100644 --- a/internal/handlers/handler_authz_test.go +++ b/internal/handlers/handler_authz_test.go @@ -493,8 +493,8 @@ func (s *AuthzSuite) TestShouldApplyPolicyOfOneFactorDomainWithAuthorizationHead builder := NewAuthzBuilder().WithImplementationLegacy() builder = builder.WithStrategies( - NewHeaderAuthorizationAuthnStrategy(), - NewHeaderProxyAuthorizationAuthRequestAuthnStrategy(), + NewHeaderAuthorizationAuthnStrategy("basic"), + NewHeaderProxyAuthorizationAuthRequestAuthnStrategy("basic"), NewCookieSessionAuthnStrategy(builder.config.RefreshInterval), ) @@ -540,8 +540,8 @@ func (s *AuthzSuite) TestShouldHandleAuthzWithoutHeaderNoCookie() { builder := NewAuthzBuilder().WithImplementationLegacy() builder = builder.WithStrategies( - NewHeaderAuthorizationAuthnStrategy(), - NewHeaderProxyAuthorizationAuthRequestAuthnStrategy(), + NewHeaderAuthorizationAuthnStrategy("basic"), + NewHeaderProxyAuthorizationAuthRequestAuthnStrategy("basic"), ) authz := builder.Build() @@ -573,8 +573,8 @@ func (s *AuthzSuite) TestShouldHandleAuthzWithEmptyAuthorizationHeader() { builder := NewAuthzBuilder().WithImplementationLegacy() builder = builder.WithStrategies( - NewHeaderAuthorizationAuthnStrategy(), - NewHeaderProxyAuthorizationAuthRequestAuthnStrategy(), + NewHeaderAuthorizationAuthnStrategy("basic"), + NewHeaderProxyAuthorizationAuthRequestAuthnStrategy("basic"), ) authz := builder.Build() @@ -608,8 +608,8 @@ func (s *AuthzSuite) TestShouldHandleAuthzWithAuthorizationHeaderInvalidPassword builder := NewAuthzBuilder().WithImplementationLegacy() builder = builder.WithStrategies( - NewHeaderAuthorizationAuthnStrategy(), - NewHeaderProxyAuthorizationAuthRequestAuthnStrategy(), + NewHeaderAuthorizationAuthnStrategy("basic"), + NewHeaderProxyAuthorizationAuthRequestAuthnStrategy("basic"), ) authz := builder.Build() @@ -645,7 +645,7 @@ func (s *AuthzSuite) TestShouldHandleAuthzWithIncorrectAuthHeader() { // TestSho builder := s.Builder() builder = builder.WithStrategies( - NewHeaderAuthorizationAuthnStrategy(), + NewHeaderAuthorizationAuthnStrategy("basic"), ) authz := builder.Build() diff --git a/internal/handlers/handler_authz_types.go b/internal/handlers/handler_authz_types.go index 6a2f5d370..ec6192a87 100644 --- a/internal/handlers/handler_authz_types.go +++ b/internal/handlers/handler_authz_types.go @@ -3,10 +3,13 @@ package handlers import ( "net/url" + "github.com/ory/fosite" + "github.com/authelia/authelia/v4/internal/authentication" "github.com/authelia/authelia/v4/internal/authorization" "github.com/authelia/authelia/v4/internal/configuration/schema" "github.com/authelia/authelia/v4/internal/middlewares" + "github.com/authelia/authelia/v4/internal/model" "github.com/authelia/authelia/v4/internal/session" ) @@ -66,11 +69,21 @@ const ( type Authn struct { Username string Method string + ClientID string Details authentication.UserDetails Level authentication.Level Object authorization.Object Type AuthnType + + Header HeaderAuthorization +} + +type HeaderAuthorization struct { + Authorization *model.Authorization + Realm string + Scope string + Error *fosite.RFC6749Error } // AuthzConfig represents the configuration elements of the Authz type. @@ -91,7 +104,7 @@ type AuthzBuilder struct { // AuthnStrategy is a strategy used for Authz authentication. type AuthnStrategy interface { - Get(ctx *middlewares.AutheliaCtx, provider *session.Session) (authn Authn, err error) + Get(ctx *middlewares.AutheliaCtx, provider *session.Session, object *authorization.Object) (authn *Authn, err error) CanHandleUnauthorized() (handle bool) HandleUnauthorized(ctx *middlewares.AutheliaCtx, authn *Authn, redirectionURL *url.URL) } diff --git a/internal/handlers/handler_oidc_token.go b/internal/handlers/handler_oidc_token.go index eadd16839..cb8cc854f 100644 --- a/internal/handlers/handler_oidc_token.go +++ b/internal/handlers/handler_oidc_token.go @@ -34,10 +34,20 @@ func OpenIDConnectTokenPOST(ctx *middlewares.AutheliaCtx, rw http.ResponseWriter ctx.Logger.Debugf("Access Request with id '%s' on client with id '%s' is being processed", requester.GetID(), client.GetID()) if requester.GetGrantTypes().ExactOne(oidc.GrantTypeClientCredentials) { - if err = oidc.PopulateClientCredentialsFlowSessionWithAccessRequest(ctx, requester, session, ctx.Providers.OpenIDConnect.KeyManager.GetKeyID); err != nil { + if err = oidc.PopulateClientCredentialsFlowSessionWithAccessRequest(ctx, client, session); err != nil { ctx.Logger.Errorf("Access Response for Request with id '%s' failed to be created with error: %s", requester.GetID(), oidc.ErrorToDebugRFC6749Error(err)) ctx.Providers.OpenIDConnect.WriteAccessError(ctx, rw, requester, err) + + return + } + + if err = oidc.PopulateClientCredentialsFlowRequester(ctx, ctx.Providers.OpenIDConnect, client, requester); err != nil { + ctx.Logger.Errorf("Access Response for Request with id '%s' failed to be created with error: %s", requester.GetID(), oidc.ErrorToDebugRFC6749Error(err)) + + ctx.Providers.OpenIDConnect.WriteAccessError(ctx, rw, requester, err) + + return } } diff --git a/internal/handlers/handler_oidc_userinfo.go b/internal/handlers/handler_oidc_userinfo.go index 34ca1291a..bd83bc6e2 100644 --- a/internal/handlers/handler_oidc_userinfo.go +++ b/internal/handlers/handler_oidc_userinfo.go @@ -44,7 +44,7 @@ func OpenIDConnectUserinfo(ctx *middlewares.AutheliaCtx, rw http.ResponseWriter, 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 { - rw.Header().Set(fasthttp.HeaderWWWAuthenticate, fmt.Sprintf(`Bearer error="%s",error_description="%s"`, rfc.ErrorField, rfc.GetDescription())) + rw.Header().Set(fasthttp.HeaderWWWAuthenticate, fmt.Sprintf(`Bearer %s`, oidc.RFC6750Header("", "", rfc))) } ctx.Providers.OpenIDConnect.WriteError(rw, req, err) |
