2021-05-04 22:06:05 +00:00
package handlers
import (
"encoding/json"
"fmt"
2021-08-11 01:04:35 +00:00
"github.com/authelia/authelia/v4/internal/middlewares"
2021-05-04 22:06:05 +00:00
)
func oidcConsent ( ctx * middlewares . AutheliaCtx ) {
userSession := ctx . GetSession ( )
if userSession . OIDCWorkflowSession == nil {
ctx . Logger . Debugf ( "Cannot consent for user %s when OIDC workflow has not been initiated" , userSession . Username )
ctx . ReplyForbidden ( )
return
}
clientID := userSession . OIDCWorkflowSession . ClientID
client , err := ctx . Providers . OpenIDConnect . Store . GetInternalClient ( clientID )
if err != nil {
ctx . Logger . Debugf ( "Unable to find related client configuration with name '%s': %v" , clientID , err )
ctx . ReplyForbidden ( )
return
}
if ! client . IsAuthenticationLevelSufficient ( userSession . AuthenticationLevel ) {
ctx . Logger . Debugf ( "Insufficient permissions to give consent v2 %d -> %d" , userSession . AuthenticationLevel , userSession . OIDCWorkflowSession . RequiredAuthorizationLevel )
ctx . ReplyForbidden ( )
return
}
feat(oidc): add additional config options, accurate token times, and refactoring (#1991)
* This gives admins more control over their OIDC installation exposing options that had defaults before. Things like lifespans for authorize codes, access tokens, id tokens, refresh tokens, a option to enable the debug client messages, minimum parameter entropy. It also allows admins to configure the response modes.
* Additionally this records specific values about a users session indicating when they performed a specific authz factor so this is represented in the token accurately.
* Lastly we also implemented a OIDC key manager which calculates the kid for jwk's using the SHA1 digest instead of being static, or more specifically the first 7 chars. As per https://datatracker.ietf.org/doc/html/draft-ietf-jose-json-web-key#section-8.1.1 the kid should not exceed 8 chars. While it's allowed to exceed 8 chars, it must only be done so with a compelling reason, which we do not have.
2021-07-03 23:44:30 +00:00
if err := ctx . SetJSONBody ( client . GetConsentResponseBody ( userSession . OIDCWorkflowSession ) ) ; 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
}
}
func oidcConsentPOST ( ctx * middlewares . AutheliaCtx ) {
userSession := ctx . GetSession ( )
if userSession . OIDCWorkflowSession == nil {
ctx . Logger . Debugf ( "Cannot consent for user %s when OIDC workflow has not been initiated" , userSession . Username )
ctx . ReplyForbidden ( )
return
}
client , err := ctx . Providers . OpenIDConnect . Store . GetInternalClient ( userSession . OIDCWorkflowSession . ClientID )
if err != nil {
ctx . Logger . Debugf ( "Unable to find related client configuration with name '%s': %v" , userSession . OIDCWorkflowSession . ClientID , err )
ctx . ReplyForbidden ( )
return
}
if ! client . IsAuthenticationLevelSufficient ( userSession . AuthenticationLevel ) {
ctx . Logger . Debugf ( "Insufficient permissions to give consent v1 %d -> %d" , userSession . AuthenticationLevel , userSession . OIDCWorkflowSession . RequiredAuthorizationLevel )
ctx . ReplyForbidden ( )
return
}
var body ConsentPostRequestBody
err = json . Unmarshal ( ctx . Request . Body ( ) , & body )
if err != nil {
2021-09-17 05:53:40 +00:00
ctx . Error ( fmt . Errorf ( "unable to unmarshal body: %v" , err ) , "Operation failed" )
2021-05-04 22:06:05 +00:00
return
}
if body . AcceptOrReject != accept && body . AcceptOrReject != reject {
ctx . Logger . Infof ( "User %s tried to reply to consent with an unexpected verb" , userSession . Username )
ctx . ReplyBadRequest ( )
return
}
if userSession . OIDCWorkflowSession . ClientID != body . ClientID {
ctx . Logger . Infof ( "User %s consented to scopes of another client (%s) than expected (%s). Beware this can be a sign of attack" ,
userSession . Username , body . ClientID , userSession . OIDCWorkflowSession . ClientID )
ctx . ReplyBadRequest ( )
return
}
var redirectionURL string
if body . AcceptOrReject == accept {
redirectionURL = userSession . OIDCWorkflowSession . AuthURI
userSession . OIDCWorkflowSession . GrantedScopes = userSession . OIDCWorkflowSession . RequestedScopes
userSession . OIDCWorkflowSession . GrantedAudience = userSession . OIDCWorkflowSession . RequestedAudience
if err := ctx . SaveSession ( userSession ) ; err != nil {
2021-09-17 05:53:40 +00:00
ctx . Error ( fmt . Errorf ( "unable to write session: %v" , err ) , "Operation failed" )
2021-05-04 22:06:05 +00:00
return
}
} else if body . AcceptOrReject == reject {
redirectionURL = fmt . Sprintf ( "%s?error=access_denied&error_description=%s" ,
userSession . OIDCWorkflowSession . TargetURI , "User has rejected the scopes" )
userSession . OIDCWorkflowSession = nil
if err := ctx . SaveSession ( userSession ) ; err != nil {
2021-09-17 05:53:40 +00:00
ctx . Error ( fmt . Errorf ( "unable to write session: %v" , err ) , "Operation failed" )
2021-05-04 22:06:05 +00:00
return
}
}
response := ConsentPostResponseBody { RedirectURI : redirectionURL }
if err := ctx . SetJSONBody ( response ) ; err != nil {
2021-09-17 05:53:40 +00:00
ctx . Error ( fmt . Errorf ( "unable to set JSON body in response" ) , "Operation failed" )
2021-05-04 22:06:05 +00:00
}
}