2022-04-07 05:33:53 +00:00
package handlers
import (
2022-10-20 02:16:36 +00:00
"errors"
2022-04-07 05:33:53 +00:00
"fmt"
"net/http"
2022-07-26 05:43:39 +00:00
"net/url"
"path"
2022-04-07 05:33:53 +00:00
"strings"
"github.com/google/uuid"
"github.com/ory/fosite"
2022-06-14 05:17:11 +00:00
"github.com/authelia/authelia/v4/internal/authentication"
"github.com/authelia/authelia/v4/internal/authorization"
2022-04-07 05:33:53 +00:00
"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"
)
2022-10-20 02:16:36 +00:00
func handleOIDCAuthorizationConsent ( ctx * middlewares . AutheliaCtx , issuer * url . URL , client * oidc . Client ,
2022-07-26 05:43:39 +00:00
userSession session . UserSession ,
2022-04-07 05:33:53 +00:00
rw http . ResponseWriter , r * http . Request , requester fosite . AuthorizeRequester ) ( consent * model . OAuth2ConsentSession , handled bool ) {
2022-07-26 05:43:39 +00:00
var (
subject uuid . UUID
err error
)
2022-04-25 00:31:05 +00:00
2022-10-20 02:16:36 +00:00
var handler handlerAuthorizationConsent
2022-07-26 05:43:39 +00:00
2022-10-20 02:16:36 +00:00
switch {
case userSession . IsAnonymous ( ) :
handler = handleOIDCAuthorizationConsentNotAuthenticated
case client . IsAuthenticationLevelSufficient ( userSession . AuthenticationLevel ) :
if subject , err = ctx . Providers . OpenIDConnect . GetSubject ( ctx , client . GetSectorIdentifier ( ) , userSession . Username ) ; err != nil {
ctx . Logger . Errorf ( logFmtErrConsentCantGetSubject , requester . GetID ( ) , client . GetID ( ) , client . Consent , userSession . Username , client . GetSectorIdentifier ( ) , err )
2022-04-25 00:31:05 +00:00
2022-10-20 02:16:36 +00:00
ctx . Providers . OpenIDConnect . WriteAuthorizeError ( rw , requester , oidc . ErrSubjectCouldNotLookup )
2022-04-25 00:31:05 +00:00
return nil , true
}
2022-10-20 02:16:36 +00:00
switch client . Consent . Mode {
case oidc . ClientConsentModeExplicit :
handler = handleOIDCAuthorizationConsentModeExplicit
case oidc . ClientConsentModeImplicit :
handler = handleOIDCAuthorizationConsentModeImplicit
case oidc . ClientConsentModePreConfigured :
handler = handleOIDCAuthorizationConsentModePreConfigured
default :
ctx . Logger . Errorf ( logFmtErrConsentCantDetermineConsentMode , requester . GetID ( ) , client . GetID ( ) )
2022-04-07 05:33:53 +00:00
2022-10-20 02:16:36 +00:00
ctx . Providers . OpenIDConnect . WriteAuthorizeError ( rw , requester , fosite . ErrServerError . WithHint ( "Could not determine the client consent mode." ) )
2022-04-07 05:33:53 +00:00
return nil , true
}
2022-10-20 02:16:36 +00:00
default :
if subject , err = ctx . Providers . OpenIDConnect . GetSubject ( ctx , client . GetSectorIdentifier ( ) , userSession . Username ) ; err != nil {
ctx . Logger . Errorf ( logFmtErrConsentCantGetSubject , requester . GetID ( ) , client . GetID ( ) , client . Consent , userSession . Username , client . GetSectorIdentifier ( ) , err )
2022-04-07 05:33:53 +00:00
2022-10-20 02:16:36 +00:00
ctx . Providers . OpenIDConnect . WriteAuthorizeError ( rw , requester , oidc . ErrSubjectCouldNotLookup )
2022-04-07 05:33:53 +00:00
return nil , true
}
2022-10-20 02:16:36 +00:00
handler = handleOIDCAuthorizationConsentGenerate
2022-04-07 05:33:53 +00:00
}
2022-10-20 02:16:36 +00:00
return handler ( ctx , issuer , client , userSession , subject , rw , r , requester )
}
2022-04-07 05:33:53 +00:00
2022-10-20 02:16:36 +00:00
func handleOIDCAuthorizationConsentNotAuthenticated ( _ * middlewares . AutheliaCtx , issuer * url . URL , _ * oidc . Client ,
_ session . UserSession , _ uuid . UUID ,
rw http . ResponseWriter , r * http . Request , requester fosite . AuthorizeRequester ) ( consent * model . OAuth2ConsentSession , handled bool ) {
redirectionURL := handleOIDCAuthorizationConsentGetRedirectionURL ( issuer , nil , requester )
http . Redirect ( rw , r , redirectionURL . String ( ) , http . StatusFound )
return nil , true
2022-04-07 05:33:53 +00:00
}
2022-07-26 05:43:39 +00:00
func handleOIDCAuthorizationConsentGenerate ( ctx * middlewares . AutheliaCtx , issuer * url . URL , client * oidc . Client ,
userSession session . UserSession , subject uuid . UUID ,
2022-04-07 05:33:53 +00:00
rw http . ResponseWriter , r * http . Request , requester fosite . AuthorizeRequester ) ( consent * model . OAuth2ConsentSession , handled bool ) {
var (
2022-04-25 00:31:05 +00:00
err error
2022-04-07 05:33:53 +00:00
)
2022-10-20 02:16:36 +00:00
ctx . Logger . Debugf ( logFmtDbgConsentGenerate , requester . GetID ( ) , client . GetID ( ) , client . Consent )
2022-04-25 00:31:05 +00:00
2022-10-20 02:16:36 +00:00
if len ( ctx . QueryArgs ( ) . PeekBytes ( qryArgConsentID ) ) != 0 {
ctx . Logger . Errorf ( logFmtErrConsentGenerateError , requester . GetID ( ) , client . GetID ( ) , client . Consent , "generating" , errors . New ( "consent id value was present when it should be absent" ) )
2022-04-07 05:33:53 +00:00
2022-10-20 02:16:36 +00:00
ctx . Providers . OpenIDConnect . WriteAuthorizeError ( rw , requester , oidc . ErrConsentCouldNotGenerate )
2022-04-07 05:33:53 +00:00
2022-04-25 00:31:05 +00:00
return nil , true
}
2022-04-07 05:33:53 +00:00
if consent , err = model . NewOAuth2ConsentSession ( subject , requester ) ; err != nil {
2022-10-20 02:16:36 +00:00
ctx . Logger . Errorf ( logFmtErrConsentGenerateError , requester . GetID ( ) , client . GetID ( ) , client . Consent , "generating" , err )
2022-04-07 05:33:53 +00:00
2022-10-20 02:16:36 +00:00
ctx . Providers . OpenIDConnect . WriteAuthorizeError ( rw , requester , oidc . ErrConsentCouldNotGenerate )
2022-04-07 05:33:53 +00:00
return nil , true
}
if err = ctx . Providers . StorageProvider . SaveOAuth2ConsentSession ( ctx , * consent ) ; err != nil {
2022-10-20 02:16:36 +00:00
ctx . Logger . Errorf ( logFmtErrConsentGenerateError , requester . GetID ( ) , client . GetID ( ) , client . Consent , "saving" , err )
2022-04-07 05:33:53 +00:00
2022-10-20 02:16:36 +00:00
ctx . Providers . OpenIDConnect . WriteAuthorizeError ( rw , requester , oidc . ErrConsentCouldNotSave )
2022-04-07 05:33:53 +00:00
return nil , true
}
2022-07-26 05:43:39 +00:00
handleOIDCAuthorizationConsentRedirect ( ctx , issuer , consent , client , userSession , rw , r , requester )
return consent , true
}
func handleOIDCAuthorizationConsentRedirect ( ctx * middlewares . AutheliaCtx , issuer * url . URL , consent * model . OAuth2ConsentSession , client * oidc . Client ,
userSession session . UserSession , rw http . ResponseWriter , r * http . Request , requester fosite . AuthorizeRequester ) {
var location * url . URL
2022-04-07 05:33:53 +00:00
2022-07-26 05:43:39 +00:00
if client . IsAuthenticationLevelSufficient ( userSession . AuthenticationLevel ) {
2022-09-03 01:51:02 +00:00
location , _ = url . ParseRequestURI ( issuer . String ( ) )
2022-10-20 02:16:36 +00:00
location . Path = path . Join ( location . Path , oidc . EndpointPathConsent )
2022-04-07 05:33:53 +00:00
2022-07-26 05:43:39 +00:00
query := location . Query ( )
2022-10-20 02:16:36 +00:00
query . Set ( queryArgID , consent . ChallengeID . String ( ) )
2022-04-07 05:33:53 +00:00
2022-07-26 05:43:39 +00:00
location . RawQuery = query . Encode ( )
2022-10-20 02:16:36 +00:00
ctx . Logger . Debugf ( logFmtDbgConsentAuthenticationSufficiency , requester . GetID ( ) , client . GetID ( ) , client . Consent , authentication . LevelToString ( userSession . AuthenticationLevel ) , "sufficient" , authorization . LevelToString ( client . Policy ) )
2022-07-26 05:43:39 +00:00
} else {
2022-10-20 02:16:36 +00:00
location = handleOIDCAuthorizationConsentGetRedirectionURL ( issuer , consent , requester )
2022-07-26 05:43:39 +00:00
2022-10-20 02:16:36 +00:00
ctx . Logger . Debugf ( logFmtDbgConsentAuthenticationSufficiency , requester . GetID ( ) , client . GetID ( ) , client . Consent , authentication . LevelToString ( userSession . AuthenticationLevel ) , "insufficient" , authorization . LevelToString ( client . Policy ) )
2022-04-07 05:33:53 +00:00
}
2022-10-20 02:16:36 +00:00
ctx . Logger . Debugf ( logFmtDbgConsentRedirect , requester . GetID ( ) , client . GetID ( ) , client . Consent , location )
2022-04-07 05:33:53 +00:00
2022-07-26 05:43:39 +00:00
http . Redirect ( rw , r , location . String ( ) , http . StatusFound )
2022-04-07 05:33:53 +00:00
}
2022-10-20 02:16:36 +00:00
func handleOIDCAuthorizationConsentGetRedirectionURL ( issuer * url . URL , consent * model . OAuth2ConsentSession , requester fosite . AuthorizeRequester ) ( redirectURL * url . URL ) {
iss := issuer . String ( )
2022-06-14 05:17:11 +00:00
2022-10-20 02:16:36 +00:00
if ! strings . HasSuffix ( iss , "/" ) {
iss += "/"
}
redirectURL , _ = url . ParseRequestURI ( iss )
query := redirectURL . Query ( )
query . Set ( queryArgWorkflow , workflowOpenIDConnect )
2022-07-26 05:43:39 +00:00
2022-10-20 02:16:36 +00:00
switch {
case consent != nil :
query . Set ( queryArgWorkflowID , consent . ChallengeID . String ( ) )
case requester != nil :
rd , _ := url . ParseRequestURI ( iss )
rd . Path = path . Join ( rd . Path , oidc . EndpointPathAuthorization )
rd . RawQuery = requester . GetRequestForm ( ) . Encode ( )
query . Set ( queryArgRD , rd . String ( ) )
2022-04-07 05:33:53 +00:00
}
2022-10-20 02:16:36 +00:00
redirectURL . RawQuery = query . Encode ( )
return redirectURL
}
func verifyOIDCUserAuthorizedForConsent ( ctx * middlewares . AutheliaCtx , client * oidc . Client , userSession session . UserSession , consent * model . OAuth2ConsentSession , subject uuid . UUID ) ( err error ) {
var sid uint32
2022-07-26 05:43:39 +00:00
if client == nil {
2022-10-20 02:16:36 +00:00
if client , err = ctx . Providers . OpenIDConnect . GetFullClient ( consent . ClientID ) ; err != nil {
2022-07-26 05:43:39 +00:00
return fmt . Errorf ( "failed to retrieve client: %w" , err )
}
}
2022-06-14 05:17:11 +00:00
2022-07-26 05:43:39 +00:00
if sid = subject . ID ( ) ; sid == 0 {
2022-10-20 02:16:36 +00:00
if subject , err = ctx . Providers . OpenIDConnect . GetSubject ( ctx , client . GetSectorIdentifier ( ) , userSession . Username ) ; err != nil {
2022-07-26 05:43:39 +00:00
return fmt . Errorf ( "failed to lookup subject: %w" , err )
}
sid = subject . ID ( )
}
2022-10-20 02:16:36 +00:00
if ! consent . Subject . Valid {
if sid == 0 {
return fmt . Errorf ( "the consent subject is null for consent session with id '%d' for anonymous user" , consent . ID )
}
2022-07-26 05:43:39 +00:00
2022-10-20 02:16:36 +00:00
consent . Subject = uuid . NullUUID { UUID : subject , Valid : true }
2022-07-26 05:43:39 +00:00
2022-10-20 02:16:36 +00:00
if err = ctx . Providers . StorageProvider . SaveOAuth2ConsentSessionSubject ( ctx , * consent ) ; err != nil {
return fmt . Errorf ( "failed to update the consent subject: %w" , err )
}
}
2022-07-26 05:43:39 +00:00
2022-10-20 02:16:36 +00:00
if consent . Subject . UUID . ID ( ) != sid {
return fmt . Errorf ( "the consent subject identifier '%s' isn't owned by user '%s' who has a subject identifier of '%s' with sector identifier '%s'" , consent . Subject . UUID , userSession . Username , subject , client . GetSectorIdentifier ( ) )
}
2022-07-26 05:43:39 +00:00
2022-10-20 02:16:36 +00:00
return nil
2022-04-07 05:33:53 +00:00
}
2022-05-03 05:28:58 +00:00
func getOIDCExpectedScopesAndAudienceFromRequest ( requester fosite . Requester ) ( scopes , audience [ ] string ) {
return getOIDCExpectedScopesAndAudience ( requester . GetClient ( ) . GetID ( ) , requester . GetRequestedScopes ( ) , requester . GetRequestedAudience ( ) )
}
func getOIDCExpectedScopesAndAudience ( clientID string , scopes , audience [ ] string ) ( expectedScopes , expectedAudience [ ] string ) {
if ! utils . IsStringInSlice ( clientID , audience ) {
audience = append ( audience , clientID )
}
return scopes , audience
}