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
"net/http"
2022-10-20 02:16:36 +00:00
"net/url"
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"
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"
2023-01-25 09:36:40 +00:00
"github.com/authelia/authelia/v4/internal/session"
2021-05-04 22:06:05 +00:00
)
2022-10-26 08:14:43 +00:00
// OpenIDConnectAuthorization handles GET/POST requests to the OpenID Connect 1.0 Authorization endpoint.
2022-04-07 00:58:51 +00:00
//
// https://openid.net/specs/openid-connect-core-1_0.html#AuthorizationEndpoint
2022-10-26 08:14:43 +00:00
func OpenIDConnectAuthorization ( ctx * middlewares . AutheliaCtx , rw http . ResponseWriter , r * http . Request ) {
2022-03-15 22:55:38 +00:00
var (
requester fosite . AuthorizeRequester
responder fosite . AuthorizeResponder
2023-04-13 10:58:18 +00:00
client oidc . Client
2022-03-15 22:55:38 +00:00
authTime time . Time
2022-10-20 02:16:36 +00:00
issuer * url . URL
2022-03-15 22:55:38 +00:00
err error
)
2022-10-20 02:16:36 +00:00
if requester , err = ctx . Providers . OpenIDConnect . NewAuthorizeRequest ( ctx , r ) ; err != nil {
2022-03-15 22:55:38 +00:00
rfc := fosite . ErrorToRFC6749Error ( err )
2022-04-25 00:31:05 +00:00
ctx . Logger . Errorf ( "Authorization Request failed with error: %s" , rfc . WithExposeDebug ( true ) . GetDescription ( ) )
2022-03-15 22:55:38 +00:00
2022-11-13 03:26:10 +00:00
ctx . Providers . OpenIDConnect . WriteAuthorizeError ( ctx , rw , requester , err )
2022-03-15 22:55:38 +00:00
return
}
clientID := requester . GetClient ( ) . GetID ( )
ctx . Logger . Debugf ( "Authorization Request with id '%s' on client with id '%s' is being processed" , requester . GetID ( ) , clientID )
2022-10-20 02:16:36 +00:00
if client , err = ctx . Providers . OpenIDConnect . GetFullClient ( clientID ) ; err != nil {
2022-03-15 22:55:38 +00:00
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 )
}
2022-11-13 03:26:10 +00:00
ctx . Providers . OpenIDConnect . WriteAuthorizeError ( ctx , rw , requester , err )
2021-05-04 22:06:05 +00:00
return
}
2023-03-06 03:58:50 +00:00
if err = client . ValidatePARPolicy ( requester , ctx . Providers . OpenIDConnect . GetPushedAuthorizeRequestURIPrefix ( ctx ) ) ; err != nil {
2023-01-03 15:03:23 +00:00
rfc := fosite . ErrorToRFC6749Error ( err )
2023-03-06 03:58:50 +00:00
ctx . Logger . Errorf ( "Authorization Request with id '%s' on client with id '%s' failed to validate the PAR policy: %s" , requester . GetID ( ) , clientID , rfc . WithExposeDebug ( true ) . GetDescription ( ) )
ctx . Providers . OpenIDConnect . WriteAuthorizeError ( ctx , rw , requester , err )
return
}
2023-04-11 11:29:02 +00:00
if ! oidc . IsPushedAuthorizedRequest ( requester , ctx . Providers . OpenIDConnect . GetPushedAuthorizeRequestURIPrefix ( ctx ) ) {
if err = client . ValidatePKCEPolicy ( requester ) ; err != nil {
rfc := fosite . ErrorToRFC6749Error ( err )
ctx . Logger . Errorf ( "Authorization Request with id '%s' on client with id '%s' failed to validate the PKCE policy: %s" , requester . GetID ( ) , client . GetID ( ) , rfc . WithExposeDebug ( true ) . GetDescription ( ) )
ctx . Providers . OpenIDConnect . WriteAuthorizeError ( ctx , rw , requester , err )
return
}
if err = client . ValidateResponseModePolicy ( requester ) ; err != nil {
rfc := fosite . ErrorToRFC6749Error ( err )
ctx . Logger . Errorf ( "Authorization Request with id '%s' on client with id '%s' failed to validate the Response Mode: %s" , requester . GetID ( ) , client . GetID ( ) , rfc . WithExposeDebug ( true ) . GetDescription ( ) )
ctx . Providers . OpenIDConnect . WriteAuthorizeError ( ctx , rw , requester , err )
return
}
}
2023-03-06 03:58:50 +00:00
if err = client . ValidatePKCEPolicy ( requester ) ; err != nil {
rfc := fosite . ErrorToRFC6749Error ( err )
ctx . Logger . Errorf ( "Authorization Request with id '%s' on client with id '%s' failed to validate the PKCE policy: %s" , requester . GetID ( ) , clientID , rfc . WithExposeDebug ( true ) . GetDescription ( ) )
2023-01-03 15:03:23 +00:00
ctx . Providers . OpenIDConnect . WriteAuthorizeError ( ctx , rw , requester , err )
return
}
2022-12-17 00:49:05 +00:00
issuer = ctx . RootURL ( )
2021-05-04 22:06:05 +00:00
2022-04-07 05:33:53 +00:00
var (
2023-01-25 09:36:40 +00:00
userSession session . UserSession
consent * model . OAuth2ConsentSession
handled bool
2022-04-07 05:33:53 +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
2023-01-25 09:36:40 +00:00
if userSession , err = ctx . GetSession ( ) ; err != nil {
ctx . Logger . Errorf ( "Authorization Request with id '%s' on client with id '%s' could not be processed: error occurred obtaining session information: %+v" , requester . GetID ( ) , client . GetID ( ) , err )
ctx . Providers . OpenIDConnect . WriteAuthorizeError ( ctx , rw , requester , fosite . ErrServerError . WithHint ( "Could not obtain the user session." ) )
return
}
2022-07-26 05:43:39 +00:00
if consent , handled = handleOIDCAuthorizationConsent ( ctx , issuer , client , userSession , rw , r , requester ) ; handled {
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-04-07 05:33:53 +00:00
extraClaims := oidcGrantRequests ( requester , consent , & userSession )
2023-04-13 10:58:18 +00:00
if authTime , err = userSession . AuthenticatedTime ( client . GetAuthorizationPolicy ( ) ) ; 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 checking authentication time: %+v" , requester . GetID ( ) , client . GetID ( ) , err )
2022-11-13 03:26:10 +00:00
ctx . Providers . OpenIDConnect . WriteAuthorizeError ( ctx , 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 )
2023-05-15 00:32:10 +00:00
session := oidc . NewSessionWithAuthorizeRequest ( issuer , ctx . Providers . OpenIDConnect . KeyManager . GetKeyIDFromAlg ( ctx , client . GetIDTokenSigningAlg ( ) ) ,
2022-04-07 05:33:53 +00:00
userSession . Username , userSession . AuthenticationMethodRefs . MarshalRFC8176 ( ) , extraClaims , authTime , consent , 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" ,
2023-03-06 03:58:50 +00:00
requester . GetID ( ) , session . ClientID , session . Subject , session . Username , session . Claims )
2022-03-15 22:55:38 +00:00
2023-03-06 03:58:50 +00:00
if responder , err = ctx . Providers . OpenIDConnect . NewAuthorizeResponse ( ctx , requester , session ) ; err != nil {
2022-03-15 22:55:38 +00:00
rfc := fosite . ErrorToRFC6749Error ( err )
2022-04-25 00:31:05 +00:00
ctx . Logger . Errorf ( "Authorization Response for Request with id '%s' on client with id '%s' could not be created: %s" , requester . GetID ( ) , clientID , rfc . WithExposeDebug ( true ) . GetDescription ( ) )
2022-03-15 22:55:38 +00:00
2022-11-13 03:26:10 +00:00
ctx . Providers . OpenIDConnect . WriteAuthorizeError ( ctx , rw , requester , err )
2021-05-04 22:06:05 +00:00
return
}
2022-04-07 05:33:53 +00:00
if err = ctx . Providers . StorageProvider . SaveOAuth2ConsentSessionGranted ( ctx , consent . ID ) ; err != nil {
ctx . Logger . Errorf ( "Authorization Request with id '%s' on client with id '%s' could not be processed: error occurred saving consent session: %+v" , requester . GetID ( ) , client . GetID ( ) , err )
2022-03-15 22:55:38 +00:00
2022-11-13 03:26:10 +00:00
ctx . Providers . OpenIDConnect . WriteAuthorizeError ( ctx , rw , requester , oidc . ErrConsentCouldNotSave )
2021-05-04 22:06:05 +00:00
return
}
2023-01-07 20:04:06 +00:00
if requester . GetResponseMode ( ) == oidc . ResponseModeFormPost {
ctx . SetUserValueBytes ( middlewares . UserValueKeyFormPost , true )
}
2022-11-13 03:26:10 +00:00
ctx . Providers . OpenIDConnect . WriteAuthorizeResponse ( ctx , rw , requester , responder )
2021-05-04 22:06:05 +00:00
}
2023-03-06 03:58:50 +00:00
// OpenIDConnectPushedAuthorizationRequest handles POST requests to the OAuth 2.0 Pushed Authorization Requests endpoint.
//
// RFC9126 https://www.rfc-editor.org/rfc/rfc9126.html
func OpenIDConnectPushedAuthorizationRequest ( ctx * middlewares . AutheliaCtx , rw http . ResponseWriter , r * http . Request ) {
var (
requester fosite . AuthorizeRequester
responder fosite . PushedAuthorizeResponder
err error
)
if requester , err = ctx . Providers . OpenIDConnect . NewPushedAuthorizeRequest ( ctx , r ) ; err != nil {
rfc := fosite . ErrorToRFC6749Error ( err )
ctx . Logger . Errorf ( "Pushed Authorization Request failed with error: %s" , rfc . WithExposeDebug ( true ) . GetDescription ( ) )
ctx . Providers . OpenIDConnect . WritePushedAuthorizeError ( ctx , rw , requester , err )
return
}
2023-04-13 10:58:18 +00:00
var client oidc . Client
2023-03-06 03:58:50 +00:00
clientID := requester . GetClient ( ) . GetID ( )
if client , err = ctx . Providers . OpenIDConnect . GetFullClient ( clientID ) ; err != nil {
if errors . Is ( err , fosite . ErrNotFound ) {
ctx . Logger . Errorf ( "Pushed 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 ( "Pushed 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 . WritePushedAuthorizeError ( ctx , rw , requester , err )
return
}
if err = client . ValidatePKCEPolicy ( requester ) ; err != nil {
rfc := fosite . ErrorToRFC6749Error ( err )
2023-04-11 11:29:02 +00:00
ctx . Logger . Errorf ( "Pushed Authorization Request with id '%s' on client with id '%s' failed to validate the PKCE policy: %s" , requester . GetID ( ) , client . GetID ( ) , rfc . WithExposeDebug ( true ) . GetDescription ( ) )
2023-03-06 03:58:50 +00:00
2023-04-11 11:29:02 +00:00
ctx . Providers . OpenIDConnect . WriteAuthorizeError ( ctx , rw , requester , err )
return
}
if err = client . ValidateResponseModePolicy ( requester ) ; err != nil {
rfc := fosite . ErrorToRFC6749Error ( err )
ctx . Logger . Errorf ( "Pushed Authorization Request with id '%s' on client with id '%s' failed to validate the Response Mode: %s" , requester . GetID ( ) , client . GetID ( ) , rfc . WithExposeDebug ( true ) . GetDescription ( ) )
ctx . Providers . OpenIDConnect . WriteAuthorizeError ( ctx , rw , requester , err )
2023-03-06 03:58:50 +00:00
return
}
if responder , err = ctx . Providers . OpenIDConnect . NewPushedAuthorizeResponse ( ctx , requester , oidc . NewSession ( ) ) ; err != nil {
rfc := fosite . ErrorToRFC6749Error ( err )
ctx . Logger . Errorf ( "Pushed Authorization Request failed with error: %s" , rfc . WithExposeDebug ( true ) . GetDescription ( ) )
ctx . Providers . OpenIDConnect . WritePushedAuthorizeError ( ctx , rw , requester , err )
return
}
ctx . Providers . OpenIDConnect . WritePushedAuthorizeResponse ( ctx , rw , requester , responder )
}