2021-05-04 22:06:05 +00:00
package handlers
import (
"encoding/json"
"fmt"
2022-04-08 05:35:21 +00:00
"time"
2021-05-04 22:06:05 +00:00
2021-08-11 01:04:35 +00:00
"github.com/authelia/authelia/v4/internal/middlewares"
2022-04-07 05:33:53 +00: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-04 22:06:05 +00:00
)
2022-04-07 00:58:51 +00:00
// OpenIDConnectConsentGET handles requests to provide consent for OpenID Connect.
func OpenIDConnectConsentGET ( ctx * middlewares . AutheliaCtx ) {
2022-04-07 05:33:53 +00:00
userSession , consent , client , handled := oidcConsentGetSessionsAndClient ( ctx )
if handled {
2021-05-04 22:06:05 +00:00
return
}
if ! client . IsAuthenticationLevelSufficient ( userSession . AuthenticationLevel ) {
2022-04-07 05:33:53 +00:00
ctx . Logger . Errorf ( "Unable to perform consent without sufficient authentication for user '%s' and client id '%s'" , userSession . Username , consent . ClientID )
2021-05-04 22:06:05 +00:00
ctx . ReplyForbidden ( )
return
}
2022-04-07 05:33:53 +00:00
if err := ctx . SetJSONBody ( client . GetConsentResponseBody ( consent ) ) ; err != nil {
2021-09-17 05:53:40 +00:00
ctx . Error ( fmt . Errorf ( "unable to set JSON body: %v" , err ) , "Operation failed" )
2021-05-04 22:06:05 +00:00
}
}
2022-04-07 00:58:51 +00:00
// OpenIDConnectConsentPOST handles consent responses for OpenID Connect.
func OpenIDConnectConsentPOST ( ctx * middlewares . AutheliaCtx ) {
2022-04-07 05:33:53 +00:00
var (
body oidc . ConsentPostRequestBody
err error
)
2021-05-04 22:06:05 +00:00
2022-04-07 05:33:53 +00:00
if err = json . Unmarshal ( ctx . Request . Body ( ) , & body ) ; err != nil {
ctx . Logger . Errorf ( "Failed to parse JSON body in consent POST: %+v" , err )
ctx . SetJSONError ( messageOperationFailed )
2021-05-04 22:06:05 +00:00
return
}
2022-04-07 05:33:53 +00:00
userSession , consent , client , handled := oidcConsentGetSessionsAndClient ( ctx )
if handled {
2021-05-04 22:06:05 +00:00
return
}
if ! client . IsAuthenticationLevelSufficient ( userSession . AuthenticationLevel ) {
2022-04-07 05:33:53 +00:00
ctx . Logger . Debugf ( "Insufficient permissions to give consent during POST current level: %d, require 2FA: %d" , userSession . AuthenticationLevel , client . Policy )
2021-05-04 22:06:05 +00:00
ctx . ReplyForbidden ( )
return
}
2022-04-07 05:33:53 +00:00
if consent . ClientID != body . ClientID {
ctx . Logger . Errorf ( "User '%s' consented to scopes of another client (%s) than expected (%s). Beware this can be a sign of attack" ,
userSession . Username , body . ClientID , consent . ClientID )
ctx . SetJSONError ( messageOperationFailed )
2021-05-04 22:06:05 +00:00
return
}
2022-04-07 05:33:53 +00:00
var (
externalRootURL string
authorized = true
)
switch body . AcceptOrReject {
case accept :
if externalRootURL , err = ctx . ExternalRootURL ( ) ; err != nil {
ctx . Logger . Errorf ( "Could not determine the external URL during consent session processing with challenge id '%s' for user '%s': %v" , consent . ChallengeID . String ( ) , userSession . Username , err )
ctx . SetJSONError ( messageOperationFailed )
return
}
2022-04-08 05:35:21 +00:00
if body . PreConfigure {
if client . PreConfiguredConsentDuration == nil {
ctx . Logger . Warnf ( "Consent session with challenge id '%s' for user '%s': consent pre-configuration was requested and was ignored because it is not permitted on this client" , consent . ChallengeID . String ( ) , userSession . Username )
} else {
expiresAt := time . Now ( ) . Add ( * client . PreConfiguredConsentDuration )
consent . ExpiresAt = & expiresAt
ctx . Logger . Debugf ( "Consent session with challenge id '%s' for user '%s': pre-configured and set to expire at %v" , consent . ChallengeID . String ( ) , userSession . Username , consent . ExpiresAt )
}
}
2022-04-07 05:33:53 +00:00
consent . GrantedScopes = consent . RequestedScopes
consent . GrantedAudience = consent . RequestedAudience
if ! utils . IsStringInSlice ( consent . ClientID , consent . GrantedAudience ) {
consent . GrantedAudience = append ( consent . GrantedAudience , consent . ClientID )
}
case reject :
authorized = false
default :
2022-04-08 05:35:21 +00:00
ctx . Logger . Warnf ( "User '%s' tried to reply to consent with an unexpected verb '%s'" , userSession . Username , body . AcceptOrReject )
2021-05-04 22:06:05 +00:00
ctx . ReplyBadRequest ( )
return
}
2022-04-07 05:33:53 +00:00
if err = ctx . Providers . StorageProvider . SaveOAuth2ConsentSessionResponse ( ctx , * consent , authorized ) ; err != nil {
ctx . Logger . Errorf ( "Failed to save the consent session response to the database: %+v" , err )
ctx . SetJSONError ( messageOperationFailed )
2021-05-04 22:06:05 +00:00
return
}
2022-04-07 05:33:53 +00:00
response := oidc . ConsentPostResponseBody { RedirectURI : fmt . Sprintf ( "%s%s?%s" , externalRootURL , oidc . AuthorizationPath , consent . Form ) }
if err = ctx . SetJSONBody ( response ) ; err != nil {
ctx . Error ( fmt . Errorf ( "unable to set JSON body in response" ) , "Operation failed" )
}
}
2021-05-04 22:06:05 +00:00
2022-04-07 05:33:53 +00:00
func oidcConsentGetSessionsAndClient ( ctx * middlewares . AutheliaCtx ) ( userSession session . UserSession , consent * model . OAuth2ConsentSession , client * oidc . Client , handled bool ) {
var (
err error
)
2021-05-04 22:06:05 +00:00
2022-04-07 05:33:53 +00:00
userSession = ctx . GetSession ( )
2021-05-04 22:06:05 +00:00
2022-04-07 05:33:53 +00:00
if userSession . ConsentChallengeID == nil {
ctx . Logger . Errorf ( "Cannot consent for user '%s' when OIDC consent session has not been initiated" , userSession . Username )
ctx . ReplyForbidden ( )
return userSession , nil , nil , true
2021-05-04 22:06:05 +00:00
}
2022-04-07 05:33:53 +00:00
if consent , err = ctx . Providers . StorageProvider . LoadOAuth2ConsentSessionByChallengeID ( ctx , * userSession . ConsentChallengeID ) ; err != nil {
ctx . Logger . Errorf ( "Unable to load consent session with challenge id '%s': %v" , userSession . ConsentChallengeID . String ( ) , err )
ctx . ReplyForbidden ( )
2021-05-04 22:06:05 +00:00
2022-04-07 05:33:53 +00:00
return userSession , nil , nil , true
2021-05-04 22:06:05 +00:00
}
2022-04-07 05:33:53 +00: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 )
ctx . ReplyForbidden ( )
return userSession , nil , nil , true
}
return userSession , consent , client , false
2021-05-04 22:06:05 +00:00
}