2021-05-05 05:06:05 +07:00
package handlers
import (
"encoding/json"
"fmt"
2022-07-26 12:43:39 +07:00
"net/url"
"path"
"strings"
2022-04-08 12:35:21 +07:00
"time"
2021-05-05 05:06:05 +07:00
2022-07-26 12:43:39 +07:00
"github.com/google/uuid"
2021-08-11 08:04:35 +07:00
"github.com/authelia/authelia/v4/internal/middlewares"
2022-04-07 12:33:53 +07:00
"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"
2021-05-05 05:06:05 +07:00
)
2022-04-07 07:58:51 +07:00
// OpenIDConnectConsentGET handles requests to provide consent for OpenID Connect.
func OpenIDConnectConsentGET ( ctx * middlewares . AutheliaCtx ) {
2022-07-26 12:43:39 +07:00
var (
consentID uuid . UUID
err error
)
if consentID , err = uuid . Parse ( string ( ctx . RequestCtx . QueryArgs ( ) . Peek ( "consent_id" ) ) ) ; err != nil {
ctx . Logger . Errorf ( "Unable to convert '%s' into a UUID: %+v" , ctx . RequestCtx . QueryArgs ( ) . Peek ( "consent_id" ) , err )
ctx . ReplyForbidden ( )
return
}
userSession , consent , client , handled := oidcConsentGetSessionsAndClient ( ctx , consentID )
2022-04-07 12:33:53 +07:00
if handled {
2021-05-05 05:06:05 +07:00
return
}
if ! client . IsAuthenticationLevelSufficient ( userSession . AuthenticationLevel ) {
2022-04-07 12:33:53 +07:00
ctx . Logger . Errorf ( "Unable to perform consent without sufficient authentication for user '%s' and client id '%s'" , userSession . Username , consent . ClientID )
2021-05-05 05:06:05 +07:00
ctx . ReplyForbidden ( )
return
}
2022-07-26 12:43:39 +07:00
if err = ctx . SetJSONBody ( client . GetConsentResponseBody ( consent ) ) ; err != nil {
2021-09-17 12:53:40 +07:00
ctx . Error ( fmt . Errorf ( "unable to set JSON body: %v" , err ) , "Operation failed" )
2021-05-05 05:06:05 +07:00
}
}
2022-07-26 12:43:39 +07:00
//nolint:gocyclo
2022-04-07 07:58:51 +07:00
// OpenIDConnectConsentPOST handles consent responses for OpenID Connect.
func OpenIDConnectConsentPOST ( ctx * middlewares . AutheliaCtx ) {
2022-04-07 12:33:53 +07:00
var (
2022-07-26 12:43:39 +07:00
consentID uuid . UUID
bodyJSON oidc . ConsentPostRequestBody
err error
2022-04-07 12:33:53 +07:00
)
2021-05-05 05:06:05 +07:00
2022-07-26 12:43:39 +07:00
if err = json . Unmarshal ( ctx . Request . Body ( ) , & bodyJSON ) ; err != nil {
ctx . Logger . Errorf ( "Failed to parse JSON bodyJSON in consent POST: %+v" , err )
2022-04-07 12:33:53 +07:00
ctx . SetJSONError ( messageOperationFailed )
2021-05-05 05:06:05 +07:00
return
}
2022-07-26 12:43:39 +07:00
if consentID , err = uuid . Parse ( bodyJSON . ConsentID ) ; err != nil {
ctx . Logger . Errorf ( "Unable to convert '%s' into a UUID: %+v" , ctx . RequestCtx . QueryArgs ( ) . Peek ( "consent_id" ) , err )
ctx . ReplyForbidden ( )
return
}
userSession , consent , client , handled := oidcConsentGetSessionsAndClient ( ctx , consentID )
2022-04-07 12:33:53 +07:00
if handled {
2021-05-05 05:06:05 +07:00
return
}
if ! client . IsAuthenticationLevelSufficient ( userSession . AuthenticationLevel ) {
2022-04-07 12:33:53 +07:00
ctx . Logger . Debugf ( "Insufficient permissions to give consent during POST current level: %d, require 2FA: %d" , userSession . AuthenticationLevel , client . Policy )
2021-05-05 05:06:05 +07:00
ctx . ReplyForbidden ( )
return
}
2022-07-26 12:43:39 +07:00
if consent . ClientID != bodyJSON . ClientID {
2022-04-07 12:33:53 +07:00
ctx . Logger . Errorf ( "User '%s' consented to scopes of another client (%s) than expected (%s). Beware this can be a sign of attack" ,
2022-07-26 12:43:39 +07:00
userSession . Username , bodyJSON . ClientID , consent . ClientID )
2022-04-07 12:33:53 +07:00
ctx . SetJSONError ( messageOperationFailed )
2021-05-05 05:06:05 +07:00
return
}
2022-07-26 12:43:39 +07:00
if bodyJSON . Consent {
if bodyJSON . PreConfigure {
2022-04-08 12:35:21 +07:00
if client . PreConfiguredConsentDuration == nil {
2022-07-26 12:43:39 +07:00
ctx . Logger . Warnf ( "Consent session with id '%s' for user '%s': consent pre-configuration was requested and was ignored because it is not permitted on this client" , consent . ChallengeID , userSession . Username )
2022-04-08 12:35:21 +07:00
} else {
expiresAt := time . Now ( ) . Add ( * client . PreConfiguredConsentDuration )
consent . ExpiresAt = & expiresAt
2022-07-26 12:43:39 +07:00
ctx . Logger . Debugf ( "Consent session with id '%s' for user '%s': pre-configured and set to expire at %v" , consent . ChallengeID , userSession . Username , consent . ExpiresAt )
2022-04-08 12:35:21 +07:00
}
}
2022-04-07 12:33:53 +07:00
consent . GrantedScopes = consent . RequestedScopes
consent . GrantedAudience = consent . RequestedAudience
if ! utils . IsStringInSlice ( consent . ClientID , consent . GrantedAudience ) {
consent . GrantedAudience = append ( consent . GrantedAudience , consent . ClientID )
}
2022-07-26 12:43:39 +07:00
}
var externalRootURL string
if externalRootURL , err = ctx . ExternalRootURL ( ) ; err != nil {
ctx . Logger . Errorf ( "Could not determine the external URL during consent session processing with id '%s' for user '%s': %v" , consent . ChallengeID , userSession . Username , err )
ctx . SetJSONError ( messageOperationFailed )
2021-05-05 05:06:05 +07:00
return
}
2022-07-26 12:43:39 +07:00
if err = ctx . Providers . StorageProvider . SaveOAuth2ConsentSessionResponse ( ctx , * consent , bodyJSON . Consent ) ; err != nil {
2022-04-07 12:33:53 +07:00
ctx . Logger . Errorf ( "Failed to save the consent session response to the database: %+v" , err )
ctx . SetJSONError ( messageOperationFailed )
2021-05-05 05:06:05 +07:00
return
}
2022-07-26 12:43:39 +07:00
var (
redirectURI * url . URL
query url . Values
)
if redirectURI , err = url . ParseRequestURI ( externalRootURL ) ; err != nil {
ctx . Logger . Errorf ( "Failed to parse the consent redirect URL: %+v" , err )
ctx . SetJSONError ( messageOperationFailed )
return
}
if ! strings . HasSuffix ( redirectURI . Path , "/" ) {
redirectURI . Path += "/"
}
if query , err = url . ParseQuery ( consent . Form ) ; err != nil {
ctx . Logger . Errorf ( "Failed to parse the consent form values: %+v" , err )
ctx . SetJSONError ( messageOperationFailed )
return
}
query . Set ( "consent_id" , consent . ChallengeID . String ( ) )
redirectURI . Path = path . Join ( redirectURI . Path , oidc . AuthorizationPath )
redirectURI . RawQuery = query . Encode ( )
response := oidc . ConsentPostResponseBody { RedirectURI : redirectURI . String ( ) }
2022-04-07 12:33:53 +07:00
if err = ctx . SetJSONBody ( response ) ; err != nil {
2022-07-26 12:43:39 +07:00
ctx . Error ( fmt . Errorf ( "unable to set JSON bodyJSON in response" ) , "Operation failed" )
2022-04-07 12:33:53 +07:00
}
}
2021-05-05 05:06:05 +07:00
2022-07-26 12:43:39 +07:00
func oidcConsentGetSessionsAndClient ( ctx * middlewares . AutheliaCtx , consentID uuid . UUID ) ( userSession session . UserSession , consent * model . OAuth2ConsentSession , client * oidc . Client , handled bool ) {
2022-04-07 12:33:53 +07:00
var (
err error
)
2021-05-05 05:06:05 +07:00
2022-04-07 12:33:53 +07:00
userSession = ctx . GetSession ( )
2021-05-05 05:06:05 +07:00
2022-07-26 12:43:39 +07:00
if consent , err = ctx . Providers . StorageProvider . LoadOAuth2ConsentSessionByChallengeID ( ctx , consentID ) ; err != nil {
ctx . Logger . Errorf ( "Unable to load consent session with challenge id '%s': %v" , consentID , err )
2022-04-07 12:33:53 +07:00
ctx . ReplyForbidden ( )
return userSession , nil , nil , true
2021-05-05 05:06:05 +07:00
}
2022-07-26 12:43:39 +07:00
if client , err = ctx . Providers . OpenIDConnect . Store . GetFullClient ( consent . ClientID ) ; err != nil {
ctx . Logger . Errorf ( "Unable to find related client configuration with name '%s': %v" , consent . ClientID , err )
2022-04-07 12:33:53 +07:00
ctx . ReplyForbidden ( )
2021-05-05 05:06:05 +07:00
2022-04-07 12:33:53 +07:00
return userSession , nil , nil , true
2021-05-05 05:06:05 +07:00
}
2022-04-07 12:33:53 +07:00
2022-07-26 12:43:39 +07:00
if err = verifyOIDCUserAuthorizedForConsent ( ctx , client , userSession , consent , uuid . UUID { } ) ; err != nil {
ctx . Logger . Errorf ( "Could not authorize the user user '%s' for the consent session with challenge id '%s' on client with id '%s': %v" , userSession . Username , consent . ChallengeID , client . GetID ( ) , err )
2022-04-07 12:33:53 +07:00
ctx . ReplyForbidden ( )
return userSession , nil , nil , true
}
return userSession , consent , client , false
2021-05-05 05:06:05 +07:00
}