summaryrefslogtreecommitdiff
path: root/internal/handlers
diff options
context:
space:
mode:
Diffstat (limited to 'internal/handlers')
-rw-r--r--internal/handlers/handler_authz.go25
-rw-r--r--internal/handlers/handler_authz_authn.go210
-rw-r--r--internal/handlers/handler_authz_builder.go11
-rw-r--r--internal/handlers/handler_authz_test.go18
-rw-r--r--internal/handlers/handler_authz_types.go15
-rw-r--r--internal/handlers/handler_oidc_token.go12
-rw-r--r--internal/handlers/handler_oidc_userinfo.go2
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)