2021-05-04 22:06:05 +00:00
package oidc
import (
2023-04-13 10:58:18 +00:00
"github.com/go-crypt/crypt/algorithm"
2021-05-04 22:06:05 +00:00
"github.com/ory/fosite"
2023-01-03 15:03:23 +00:00
"github.com/ory/x/errorsx"
2023-04-13 10:58:18 +00:00
"gopkg.in/square/go-jose.v2"
2021-05-04 22:06:05 +00:00
2021-08-11 01:04:35 +00:00
"github.com/authelia/authelia/v4/internal/authentication"
"github.com/authelia/authelia/v4/internal/authorization"
"github.com/authelia/authelia/v4/internal/configuration/schema"
2022-04-01 11:18:58 +00:00
"github.com/authelia/authelia/v4/internal/model"
2021-05-04 22:06:05 +00:00
)
2022-04-07 05:33:53 +00:00
// NewClient creates a new Client.
2023-04-13 10:58:18 +00:00
func NewClient ( config schema . OpenIDConnectClientConfiguration ) ( client Client ) {
base := & BaseClient {
2022-04-07 06:13:01 +00:00
ID : config . ID ,
Description : config . Description ,
2022-10-20 03:21:45 +00:00
Secret : config . Secret ,
2022-04-07 06:13:01 +00:00
SectorIdentifier : config . SectorIdentifier . String ( ) ,
Public : config . Public ,
2021-07-15 11:02:03 +00:00
2023-01-03 15:03:23 +00:00
EnforcePKCE : config . EnforcePKCE || config . PKCEChallengeMethod != "" ,
EnforcePKCEChallengeMethod : config . PKCEChallengeMethod != "" ,
PKCEChallengeMethod : config . PKCEChallengeMethod ,
2021-07-15 11:02:03 +00:00
Audience : config . Audience ,
Scopes : config . Scopes ,
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
RedirectURIs : config . RedirectURIs ,
GrantTypes : config . GrantTypes ,
ResponseTypes : config . ResponseTypes ,
2023-04-11 11:29:02 +00:00
ResponseModes : [ ] fosite . ResponseModeType { } ,
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-03-06 03:58:50 +00:00
EnforcePAR : config . EnforcePAR ,
2021-07-10 04:56:33 +00:00
UserinfoSigningAlgorithm : config . UserinfoSigningAlgorithm ,
2022-04-08 05:35:21 +00:00
2022-12-17 12:39:24 +00:00
Policy : authorization . NewLevel ( config . Policy ) ,
2022-04-08 05:35:21 +00:00
2022-10-20 02:16:36 +00:00
Consent : NewClientConsent ( config . ConsentMode , config . ConsentPreConfiguredDuration ) ,
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
}
for _ , mode := range config . ResponseModes {
2023-04-13 10:58:18 +00:00
base . ResponseModes = append ( base . ResponseModes , fosite . ResponseModeType ( mode ) )
}
if config . TokenEndpointAuthMethod != "" && config . TokenEndpointAuthMethod != "auto" {
client = & FullClient {
BaseClient : base ,
TokenEndpointAuthMethod : config . TokenEndpointAuthMethod ,
}
} else {
client = base
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 client
2021-05-04 22:06:05 +00:00
}
2023-04-13 10:58:18 +00:00
// GetID returns the ID.
func ( c * BaseClient ) GetID ( ) string {
return c . ID
}
// GetDescription returns the Description.
func ( c * BaseClient ) GetDescription ( ) string {
if c . Description == "" {
c . Description = c . GetID ( )
}
return c . Description
}
// GetSecret returns the Secret.
func ( c * BaseClient ) GetSecret ( ) algorithm . Digest {
return c . Secret
}
// GetSectorIdentifier returns the SectorIdentifier for this client.
func ( c * BaseClient ) GetSectorIdentifier ( ) string {
return c . SectorIdentifier
}
// GetHashedSecret returns the Secret.
func ( c * BaseClient ) GetHashedSecret ( ) ( secret [ ] byte ) {
if c . Secret == nil {
return [ ] byte ( nil )
}
return [ ] byte ( c . Secret . Encode ( ) )
}
// GetRedirectURIs returns the RedirectURIs.
func ( c * BaseClient ) GetRedirectURIs ( ) ( redirectURIs [ ] string ) {
return c . RedirectURIs
}
// GetGrantTypes returns the GrantTypes.
func ( c * BaseClient ) GetGrantTypes ( ) fosite . Arguments {
if len ( c . GrantTypes ) == 0 {
return fosite . Arguments { "authorization_code" }
}
return c . GrantTypes
}
// GetResponseTypes returns the ResponseTypes.
func ( c * BaseClient ) GetResponseTypes ( ) fosite . Arguments {
if len ( c . ResponseTypes ) == 0 {
return fosite . Arguments { "code" }
}
return c . ResponseTypes
}
// GetScopes returns the Scopes.
func ( c * BaseClient ) GetScopes ( ) fosite . Arguments {
return c . Scopes
}
// GetAudience returns the Audience.
func ( c * BaseClient ) GetAudience ( ) fosite . Arguments {
return c . Audience
}
// GetResponseModes returns the valid response modes for this client.
//
// Implements the fosite.ResponseModeClient.
func ( c * BaseClient ) GetResponseModes ( ) [ ] fosite . ResponseModeType {
return c . ResponseModes
}
// GetUserinfoSigningAlgorithm returns the UserinfoSigningAlgorithm.
func ( c * BaseClient ) GetUserinfoSigningAlgorithm ( ) string {
if c . UserinfoSigningAlgorithm == "" {
c . UserinfoSigningAlgorithm = SigningAlgorithmNone
}
return c . UserinfoSigningAlgorithm
}
// GetPAREnforcement returns EnforcePAR.
func ( c * BaseClient ) GetPAREnforcement ( ) bool {
return c . EnforcePAR
}
// GetPKCEEnforcement returns EnforcePKCE.
func ( c * BaseClient ) GetPKCEEnforcement ( ) bool {
return c . EnforcePKCE
}
// GetPKCEChallengeMethodEnforcement returns EnforcePKCEChallengeMethod.
func ( c * BaseClient ) GetPKCEChallengeMethodEnforcement ( ) bool {
return c . EnforcePKCEChallengeMethod
}
// GetPKCEChallengeMethod returns PKCEChallengeMethod.
func ( c * BaseClient ) GetPKCEChallengeMethod ( ) string {
return c . PKCEChallengeMethod
}
// GetAuthorizationPolicy returns Policy.
func ( c * BaseClient ) GetAuthorizationPolicy ( ) authorization . Level {
return c . Policy
}
// GetConsentPolicy returns Consent.
func ( c * BaseClient ) GetConsentPolicy ( ) ClientConsent {
return c . Consent
}
// GetConsentResponseBody returns the proper consent response body for this session.OIDCWorkflowSession.
func ( c * BaseClient ) GetConsentResponseBody ( consent * model . OAuth2ConsentSession ) ConsentGetResponseBody {
body := ConsentGetResponseBody {
ClientID : c . ID ,
ClientDescription : c . Description ,
PreConfiguration : c . Consent . Mode == ClientConsentModePreConfigured ,
}
if consent != nil {
body . Scopes = consent . RequestedScopes
body . Audience = consent . RequestedAudience
}
return body
}
// IsPublic returns the value of the Public property.
func ( c * BaseClient ) IsPublic ( ) bool {
return c . Public
}
// IsAuthenticationLevelSufficient returns if the provided authentication.Level is sufficient for the client of the AutheliaClient.
func ( c * BaseClient ) IsAuthenticationLevelSufficient ( level authentication . Level ) bool {
if level == authentication . NotAuthenticated {
return false
}
return authorization . IsAuthLevelSufficient ( level , c . Policy )
}
2023-03-06 03:58:50 +00:00
// ValidatePKCEPolicy is a helper function to validate PKCE policy constraints on a per-client basis.
2023-04-13 10:58:18 +00:00
func ( c * BaseClient ) ValidatePKCEPolicy ( r fosite . Requester ) ( err error ) {
2023-01-03 15:03:23 +00:00
form := r . GetRequestForm ( )
if c . EnforcePKCE {
2023-03-06 03:58:50 +00:00
if form . Get ( FormParameterCodeChallenge ) == "" {
2023-01-03 15:03:23 +00:00
return errorsx . WithStack ( fosite . ErrInvalidRequest .
WithHint ( "Clients must include a code_challenge when performing the authorize code flow, but it is missing." ) .
WithDebug ( "The server is configured in a way that enforces PKCE for this client." ) )
}
if c . EnforcePKCEChallengeMethod {
2023-03-06 03:58:50 +00:00
if method := form . Get ( FormParameterCodeChallengeMethod ) ; method != c . PKCEChallengeMethod {
2023-01-03 15:03:23 +00:00
return errorsx . WithStack ( fosite . ErrInvalidRequest .
2023-03-06 03:58:50 +00:00
WithHintf ( "Client must use code_challenge_method=%s, %s is not allowed." , c . PKCEChallengeMethod , method ) .
WithDebugf ( "The server is configured in a way that enforces PKCE %s as challenge method for this client." , c . PKCEChallengeMethod ) )
2023-01-03 15:03:23 +00:00
}
}
}
return nil
}
2023-03-06 03:58:50 +00:00
// ValidatePARPolicy is a helper function to validate additional policy constraints on a per-client basis.
2023-04-13 10:58:18 +00:00
func ( c * BaseClient ) ValidatePARPolicy ( r fosite . Requester , prefix string ) ( err error ) {
2023-03-06 03:58:50 +00:00
if c . EnforcePAR {
2023-04-11 11:29:02 +00:00
if ! IsPushedAuthorizedRequest ( r , prefix ) {
switch requestURI := r . GetRequestForm ( ) . Get ( FormParameterRequestURI ) ; requestURI {
case "" :
2023-03-06 03:58:50 +00:00
return errorsx . WithStack ( ErrPAREnforcedClientMissingPAR . WithDebug ( "The request_uri parameter was empty." ) )
2023-04-11 11:29:02 +00:00
default :
return errorsx . WithStack ( ErrPAREnforcedClientMissingPAR . WithDebugf ( "The request_uri parameter '%s' is malformed." , requestURI ) )
2023-03-06 03:58:50 +00:00
}
}
}
return nil
}
2023-04-11 11:29:02 +00:00
// ValidateResponseModePolicy is an additional check to the response mode parameter to ensure if it's omitted that the
// default response mode for the fosite.AuthorizeRequester is permitted.
2023-04-13 10:58:18 +00:00
func ( c * BaseClient ) ValidateResponseModePolicy ( r fosite . AuthorizeRequester ) ( err error ) {
2023-04-11 11:29:02 +00:00
if r . GetResponseMode ( ) != fosite . ResponseModeDefault {
return nil
}
m := r . GetDefaultResponseMode ( )
modes := c . GetResponseModes ( )
if len ( modes ) == 0 {
return nil
}
for _ , mode := range modes {
if m == mode {
return nil
}
}
return errorsx . WithStack ( fosite . ErrUnsupportedResponseMode . WithHintf ( ` The request omitted the response_mode making the default response_mode "%s" based on the other authorization request parameters but registered OAuth 2.0 client doesn't support this response_mode ` , m ) )
}
2023-04-13 10:58:18 +00:00
// GetRequestURIs is an array of request_uri values that are pre-registered by the RP for use at the OP. Servers MAY
// cache the contents of the files referenced by these URIs and not retrieve them at the time they are used in a request.
// OPs can require that request_uri values used be pre-registered with the require_request_uri_registration
// discovery parameter.
func ( c * FullClient ) GetRequestURIs ( ) [ ] string {
return c . RequestURIs
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-04-13 10:58:18 +00:00
// GetJSONWebKeys returns the JSON Web Key Set containing the public key used by the client to authenticate.
func ( c * FullClient ) GetJSONWebKeys ( ) * jose . JSONWebKeySet {
return c . JSONWebKeys
2023-01-03 15:03:23 +00:00
}
2023-04-13 10:58:18 +00:00
// GetJSONWebKeysURI returns the URL for lookup of JSON Web Key Set containing the
// public key used by the client to authenticate.
func ( c * FullClient ) GetJSONWebKeysURI ( ) string {
return c . JSONWebKeysURI
2021-05-04 22:06:05 +00:00
}
2023-04-13 10:58:18 +00:00
// GetRequestObjectSigningAlgorithm returns the JWS [JWS] alg algorithm [JWA] that MUST be used for signing Request
// Objects sent to the OP. All Request Objects from this Client MUST be rejected, if not signed with this algorithm.
func ( c * FullClient ) GetRequestObjectSigningAlgorithm ( ) string {
return c . RequestObjectSigningAlgorithm
2021-05-04 22:06:05 +00:00
}
2023-04-13 10:58:18 +00:00
// GetTokenEndpointAuthMethod returns the requested Client Authentication Method for the Token Endpoint. The options are
// client_secret_post, client_secret_basic, client_secret_jwt, private_key_jwt, and none.
func ( c * FullClient ) GetTokenEndpointAuthMethod ( ) string {
if c . TokenEndpointAuthMethod == "" {
if c . Public {
c . TokenEndpointAuthMethod = ClientAuthMethodNone
} else {
c . TokenEndpointAuthMethod = ClientAuthMethodClientSecretPost
}
2021-05-04 22:06:05 +00:00
}
2023-04-13 10:58:18 +00:00
return c . TokenEndpointAuthMethod
2021-05-04 22:06:05 +00:00
}
2023-04-13 10:58:18 +00:00
// GetTokenEndpointAuthSigningAlgorithm returns the JWS [JWS] alg algorithm [JWA] that MUST be used for signing the JWT
// [JWT] used to authenticate the Client at the Token Endpoint for the private_key_jwt and client_secret_jwt
// authentication methods.
func ( c * FullClient ) GetTokenEndpointAuthSigningAlgorithm ( ) string {
if c . TokenEndpointAuthSigningAlgorithm == "" {
c . TokenEndpointAuthSigningAlgorithm = SigningAlgorithmRSAWithSHA256
2021-05-04 22:06:05 +00:00
}
2023-04-13 10:58:18 +00:00
return c . TokenEndpointAuthSigningAlgorithm
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
}