2021-05-04 22:06:05 +00:00
package handlers
import (
2022-03-15 22:55:38 +00:00
"errors"
2021-05-04 22:06:05 +00:00
"fmt"
"net/http"
"strings"
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
"time"
2021-05-04 22:06:05 +00:00
"github.com/ory/fosite"
2022-04-01 11:18:58 +00:00
"github.com/authelia/authelia/v4/internal/authorization"
2021-08-11 01:04:35 +00:00
"github.com/authelia/authelia/v4/internal/middlewares"
2022-04-01 11:18:58 +00:00
"github.com/authelia/authelia/v4/internal/model"
2021-08-11 01:04:35 +00:00
"github.com/authelia/authelia/v4/internal/oidc"
"github.com/authelia/authelia/v4/internal/session"
2021-05-04 22:06:05 +00:00
)
2022-04-07 00:58:51 +00:00
// OpenIDConnectAuthorizationGET handles GET requests to the OpenID Connect 1.0 Authorization endpoint.
//
// https://openid.net/specs/openid-connect-core-1_0.html#AuthorizationEndpoint
func OpenIDConnectAuthorizationGET ( ctx * middlewares . AutheliaCtx , rw http . ResponseWriter , r * http . Request ) {
2022-03-15 22:55:38 +00:00
var (
requester fosite . AuthorizeRequester
responder fosite . AuthorizeResponder
client * oidc . InternalClient
authTime time . Time
issuer string
err error
)
if requester , err = ctx . Providers . OpenIDConnect . Fosite . NewAuthorizeRequest ( ctx , r ) ; err != nil {
rfc := fosite . ErrorToRFC6749Error ( err )
ctx . Logger . Errorf ( "Authorization Request failed with error: %+v" , rfc )
ctx . Providers . OpenIDConnect . Fosite . WriteAuthorizeError ( rw , requester , err )
return
}
clientID := requester . GetClient ( ) . GetID ( )
ctx . Logger . Debugf ( "Authorization Request with id '%s' on client with id '%s' is being processed" , requester . GetID ( ) , clientID )
if client , err = ctx . Providers . OpenIDConnect . Store . GetInternalClient ( clientID ) ; err != nil {
if errors . Is ( err , fosite . ErrNotFound ) {
ctx . Logger . Errorf ( "Authorization Request with id '%s' on client with id '%s' could not be processed: client was not found" , requester . GetID ( ) , clientID )
} else {
ctx . Logger . Errorf ( "Authorization Request with id '%s' on client with id '%s' could not be processed: failed to find client: %+v" , requester . GetID ( ) , clientID , err )
}
ctx . Providers . OpenIDConnect . Fosite . WriteAuthorizeError ( rw , requester , err )
2021-05-04 22:06:05 +00:00
return
}
2022-03-15 22:55:38 +00:00
if issuer , err = ctx . ExternalRootURL ( ) ; err != nil {
ctx . Logger . Errorf ( "Authorization Request with id '%s' on client with id '%s' could not be processed: error occurred determining issuer: %+v" , requester . GetID ( ) , clientID , err )
2021-05-04 22:06:05 +00:00
2022-03-15 22:55:38 +00:00
ctx . Providers . OpenIDConnect . Fosite . WriteAuthorizeError ( rw , requester , fosite . ErrServerError . WithHint ( "Could not determine issuer." ) )
2021-05-04 22:06:05 +00:00
return
}
userSession := ctx . GetSession ( )
2022-03-15 22:55:38 +00:00
requestedScopes := requester . GetRequestedScopes ( )
requestedAudience := requester . GetRequestedAudience ( )
2021-05-04 22:06:05 +00:00
isAuthInsufficient := ! client . IsAuthenticationLevelSufficient ( userSession . AuthenticationLevel )
if isAuthInsufficient || ( isConsentMissing ( userSession . OIDCWorkflowSession , requestedScopes , requestedAudience ) ) {
2022-03-15 22:55:38 +00:00
oidcAuthorizeHandleAuthorizationOrConsentInsufficient ( ctx , userSession , client , isAuthInsufficient , rw , r , requester , issuer )
2021-05-04 22:06:05 +00:00
return
}
2022-03-15 22:55:38 +00:00
extraClaims := oidcGrantRequests ( requester , requestedScopes , requestedAudience , & userSession )
2021-05-04 22:06:05 +00:00
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
workflowCreated := time . Unix ( userSession . OIDCWorkflowSession . CreatedTimestamp , 0 )
2021-05-04 22:06:05 +00:00
userSession . OIDCWorkflowSession = nil
2022-03-15 22:55:38 +00:00
if err = ctx . SaveSession ( userSession ) ; err != nil {
ctx . Logger . Errorf ( "Authorization Request with id '%s' on client with id '%s' could not be processed: error occurred saving session: %+v" , requester . GetID ( ) , client . GetID ( ) , err )
2021-05-04 22:06:05 +00:00
2022-03-15 22:55:38 +00:00
ctx . Providers . OpenIDConnect . Fosite . WriteAuthorizeError ( rw , requester , fosite . ErrServerError . WithHint ( "Could not save the session." ) )
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
return
}
2022-03-15 22:55:38 +00:00
if authTime , err = userSession . AuthenticatedTime ( client . Policy ) ; err != nil {
ctx . Logger . Errorf ( "Authorization Request with id '%s' on client with id '%s' could not be processed: error occurred checking authentication time: %+v" , requester . GetID ( ) , client . GetID ( ) , err )
ctx . Providers . OpenIDConnect . Fosite . WriteAuthorizeError ( rw , requester , fosite . ErrServerError . WithHint ( "Could not obtain the authentication time." ) )
2021-05-04 22:06:05 +00:00
return
}
2022-03-15 22:55:38 +00:00
ctx . Logger . Debugf ( "Authorization Request with id '%s' on client with id '%s' was successfully processed, proceeding to build Authorization Response" , requester . GetID ( ) , clientID )
subject := userSession . Username
oidcSession := oidc . NewSessionWithAuthorizeRequest ( issuer , ctx . Providers . OpenIDConnect . KeyManager . GetActiveKeyID ( ) ,
2022-04-01 11:18:58 +00:00
subject , userSession . Username , userSession . AuthenticationMethodRefs . MarshalRFC8176 ( ) , extraClaims , authTime , workflowCreated , requester )
2022-03-15 22:55:38 +00:00
ctx . Logger . Tracef ( "Authorization Request with id '%s' on client with id '%s' creating session for Authorization Response for subject '%s' with username '%s' with claims: %+v" ,
requester . GetID ( ) , oidcSession . ClientID , oidcSession . Subject , oidcSession . Username , oidcSession . Claims )
ctx . Logger . Tracef ( "Authorization Request with id '%s' on client with id '%s' creating session for Authorization Response for subject '%s' with username '%s' with headers: %+v" ,
requester . GetID ( ) , oidcSession . ClientID , oidcSession . Subject , oidcSession . Username , oidcSession . Headers )
if responder , err = ctx . Providers . OpenIDConnect . Fosite . NewAuthorizeResponse ( ctx , requester , oidcSession ) ; err != nil {
rfc := fosite . ErrorToRFC6749Error ( err )
ctx . Logger . Errorf ( "Authorization Response for Request with id '%s' on client with id '%s' could not be created: %+v" , requester . GetID ( ) , clientID , rfc )
ctx . Providers . OpenIDConnect . Fosite . WriteAuthorizeError ( rw , requester , err )
2021-05-04 22:06:05 +00:00
return
}
2022-03-15 22:55:38 +00:00
ctx . Providers . OpenIDConnect . Fosite . WriteAuthorizeResponse ( rw , requester , responder )
2021-05-04 22:06:05 +00:00
}
func oidcAuthorizeHandleAuthorizationOrConsentInsufficient (
ctx * middlewares . AutheliaCtx , userSession session . UserSession , client * oidc . InternalClient , isAuthInsufficient bool ,
rw http . ResponseWriter , r * http . Request ,
2022-03-15 22:55:38 +00:00
requester fosite . AuthorizeRequester , issuer string ) {
2021-08-10 00:31:08 +00:00
redirectURL := fmt . Sprintf ( "%s%s" , issuer , string ( ctx . Request . RequestURI ( ) ) )
2021-05-04 22:06:05 +00:00
2022-03-15 22:55:38 +00:00
ctx . Logger . Debugf ( "Authorization Request with id '%s' on client with id '%s' requires user '%s' provides consent for scopes '%s'" ,
requester . GetID ( ) , client . GetID ( ) , userSession . Username , strings . Join ( requester . GetRequestedScopes ( ) , "', '" ) )
2021-05-04 22:06:05 +00:00
2022-04-01 11:18:58 +00:00
userSession . OIDCWorkflowSession = & model . OIDCWorkflowSession {
ClientID : client . GetID ( ) ,
RequestedScopes : requester . GetRequestedScopes ( ) ,
RequestedAudience : requester . GetRequestedAudience ( ) ,
AuthURI : redirectURL ,
TargetURI : requester . GetRedirectURI ( ) . String ( ) ,
Require2FA : client . Policy == authorization . TwoFactor ,
CreatedTimestamp : time . Now ( ) . Unix ( ) ,
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
}
2021-05-04 22:06:05 +00:00
if err := ctx . SaveSession ( userSession ) ; err != nil {
2022-03-15 22:55:38 +00:00
ctx . Logger . Errorf ( "Authorization Request with id '%s' on client with id '%s' could not be processed: error occurred saving session for consent: %+v" , requester . GetID ( ) , client . GetID ( ) , err )
ctx . Providers . OpenIDConnect . Fosite . WriteAuthorizeError ( rw , requester , fosite . ErrServerError . WithHint ( "Could not save the session." ) )
2021-05-04 22:06:05 +00:00
return
}
if isAuthInsufficient {
2021-08-10 00:31:08 +00:00
http . Redirect ( rw , r , issuer , http . StatusFound )
2021-05-04 22:06:05 +00:00
} else {
2021-08-10 00:31:08 +00:00
http . Redirect ( rw , r , fmt . Sprintf ( "%s/consent" , issuer ) , http . StatusFound )
2021-05-04 22:06:05 +00:00
}
}