2021-05-04 22:06:05 +00:00
package validator
import (
2023-04-19 04:24:05 +00:00
"crypto"
"crypto/ecdsa"
"crypto/ed25519"
"crypto/rsa"
2021-05-04 22:06:05 +00:00
"fmt"
"net/url"
2023-04-19 04:24:05 +00:00
"sort"
2023-04-13 10:58:18 +00:00
"strconv"
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
"strings"
"time"
2021-05-04 22:06:05 +00:00
2023-04-19 04:24:05 +00:00
"gopkg.in/square/go-jose.v2"
2021-08-11 01:04:35 +00:00
"github.com/authelia/authelia/v4/internal/configuration/schema"
2022-10-20 02:16:36 +00:00
"github.com/authelia/authelia/v4/internal/oidc"
2021-08-11 01:04:35 +00:00
"github.com/authelia/authelia/v4/internal/utils"
2021-05-04 22:06:05 +00:00
)
2022-04-17 23:58:24 +00:00
// ValidateIdentityProviders validates and updates the IdentityProviders configuration.
2022-11-23 23:16:23 +00:00
func ValidateIdentityProviders ( config * schema . IdentityProvidersConfiguration , val * schema . StructValidator ) {
validateOIDC ( config . OIDC , val )
2021-05-04 22:06:05 +00:00
}
2022-11-23 23:16:23 +00:00
func validateOIDC ( config * schema . OpenIDConnectConfiguration , val * schema . StructValidator ) {
2022-10-02 02:07:40 +00:00
if config == nil {
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
2022-10-02 02:07:40 +00:00
setOIDCDefaults ( config )
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-19 04:24:05 +00:00
validateOIDCIssuer ( config , val )
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-19 04:24:05 +00:00
sort . Sort ( oidc . SortedSigningAlgs ( config . Discovery . RegisteredJWKSigningAlgs ) )
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
2022-10-02 02:07:40 +00:00
if config . MinimumParameterEntropy != 0 && config . MinimumParameterEntropy < 8 {
2022-11-23 23:16:23 +00:00
val . PushWarning ( fmt . Errorf ( errFmtOIDCServerInsecureParameterEntropy , config . MinimumParameterEntropy ) )
2022-10-02 02:07:40 +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
2022-10-02 02:07:40 +00:00
if config . EnforcePKCE != "never" && config . EnforcePKCE != "public_clients_only" && config . EnforcePKCE != "always" {
2022-11-23 23:16:23 +00:00
val . Push ( fmt . Errorf ( errFmtOIDCEnforcePKCEInvalidValue , config . EnforcePKCE ) )
2022-10-02 02:07:40 +00:00
}
2022-03-02 04:44:05 +00:00
2022-11-23 23:16:23 +00:00
validateOIDCOptionsCORS ( config , val )
2022-03-02 04:44:05 +00:00
2022-10-02 02:07:40 +00:00
if len ( config . Clients ) == 0 {
2022-11-23 23:16:23 +00:00
val . Push ( fmt . Errorf ( errFmtOIDCNoClientsConfigured ) )
2022-10-02 02:07:40 +00:00
} else {
2022-11-23 23:16:23 +00:00
validateOIDCClients ( config , val )
2022-10-02 02:07:40 +00:00
}
}
2021-05-04 22:06:05 +00:00
2023-04-19 04:24:05 +00:00
func validateOIDCIssuer ( config * schema . OpenIDConnectConfiguration , val * schema . StructValidator ) {
switch {
case config . IssuerPrivateKey != nil :
validateOIDCIssuerLegacy ( config , val )
fallthrough
case len ( config . IssuerJWKS ) != 0 :
validateOIDCIssuerModern ( config , val )
default :
val . Push ( fmt . Errorf ( errFmtOIDCNoPrivateKey ) )
}
}
func validateOIDCIssuerLegacy ( config * schema . OpenIDConnectConfiguration , val * schema . StructValidator ) {
j := & jose . JSONWebKey { Key : & config . IssuerPrivateKey . PublicKey }
thumbprint , err := j . Thumbprint ( crypto . SHA1 )
if err != nil {
val . Push ( fmt . Errorf ( "identity_providers: oidc: option 'issuer_private_key' failed to calculate thumbprint to configure key id value: %w" , err ) )
return
}
config . IssuerJWKS = append ( config . IssuerJWKS , schema . JWK {
KeyID : fmt . Sprintf ( "%x" , thumbprint ) [ : 6 ] ,
Algorithm : oidc . SigningAlgRSAUsingSHA256 ,
Use : oidc . KeyUseSignature ,
Key : config . IssuerPrivateKey ,
CertificateChain : config . IssuerCertificateChain ,
} )
}
//nolint:gocyclo // Refactor time permitting.
func validateOIDCIssuerModern ( config * schema . OpenIDConnectConfiguration , val * schema . StructValidator ) {
var (
props * JWKProperties
err error
)
kids := make ( [ ] string , len ( config . IssuerJWKS ) )
for i := 0 ; i < len ( config . IssuerJWKS ) ; i ++ {
if key , ok := config . IssuerJWKS [ i ] . Key . ( * rsa . PrivateKey ) ; ok && key . PublicKey . N == nil {
val . Push ( fmt . Errorf ( "identity_providers: oidc: issuer_jwks: key #%d: option 'key' must be a valid RSA private key but the provided data is malformed as it's missing the public key bits" , i + 1 ) )
continue
}
switch n := len ( config . IssuerJWKS [ i ] . KeyID ) ; {
case n == 0 :
j := jose . JSONWebKey { }
switch key := config . IssuerJWKS [ i ] . Key . ( type ) {
case schema . CryptographicPrivateKey :
j . Key = key . Public ( )
case * rsa . PublicKey , * ecdsa . PublicKey , ed25519 . PublicKey :
j . Key = key
default :
break
}
if j . Key == nil {
break
}
var thumbprint [ ] byte
if thumbprint , err = j . Thumbprint ( crypto . SHA1 ) ; err != nil {
val . Push ( fmt . Errorf ( "identity_providers: oidc: issuer_jwks: key #%d: option 'key' failed to calculate thumbprint to configure key id value: %w" , i + 1 , err ) )
continue
}
config . IssuerJWKS [ i ] . KeyID = fmt . Sprintf ( "%x" , thumbprint ) [ : 6 ]
case n > 7 :
val . Push ( fmt . Errorf ( "identity_providers: oidc: issuer_jwks: key #%d with key id '%s': option `key_id`` must be 7 characters or less" , i + 1 , config . IssuerJWKS [ i ] . KeyID ) )
}
if config . IssuerJWKS [ i ] . KeyID != "" && utils . IsStringInSlice ( config . IssuerJWKS [ i ] . KeyID , kids ) {
val . Push ( fmt . Errorf ( "identity_providers: oidc: issuer_jwks: key #%d with key id '%s': option 'key_id' must be unique" , i + 1 , config . IssuerJWKS [ i ] . KeyID ) )
}
kids [ i ] = config . IssuerJWKS [ i ] . KeyID
if ! utils . IsStringAlphaNumeric ( config . IssuerJWKS [ i ] . KeyID ) {
val . Push ( fmt . Errorf ( "identity_providers: oidc: issuer_jwks: key #%d with key id '%s': option 'key_id' must only have alphanumeric characters" , i + 1 , config . IssuerJWKS [ i ] . KeyID ) )
}
if props , err = schemaJWKGetProperties ( config . IssuerJWKS [ i ] ) ; err != nil {
val . Push ( fmt . Errorf ( "identity_providers: oidc: issuer_jwks: key #%d with key id '%s': option 'key' failed to get key properties: %w" , i + 1 , config . IssuerJWKS [ i ] . KeyID , err ) )
continue
}
switch config . IssuerJWKS [ i ] . Use {
case "" :
config . IssuerJWKS [ i ] . Use = props . Use
case oidc . KeyUseSignature :
break
default :
val . Push ( fmt . Errorf ( "identity_providers: oidc: issuer_jwks: key #%d with key id '%s': option '%s' must be one of %s but it's configured as '%s'" , i + 1 , config . IssuerJWKS [ i ] . KeyID , "use" , strJoinOr ( [ ] string { oidc . KeyUseSignature } ) , config . IssuerJWKS [ i ] . Use ) )
}
switch {
case config . IssuerJWKS [ i ] . Algorithm == "" :
config . IssuerJWKS [ i ] . Algorithm = props . Algorithm
case utils . IsStringInSlice ( config . IssuerJWKS [ i ] . Algorithm , validOIDCIssuerJWKSigningAlgs ) :
break
default :
val . Push ( fmt . Errorf ( "identity_providers: oidc: issuer_jwks: key #%d with key id '%s': option '%s' must be one of %s but it's configured as '%s'" , i + 1 , config . IssuerJWKS [ i ] . KeyID , "algorithm" , strJoinOr ( validOIDCIssuerJWKSigningAlgs ) , config . IssuerJWKS [ i ] . Algorithm ) )
}
if config . IssuerJWKS [ i ] . Algorithm != "" {
if utils . IsStringInSlice ( config . IssuerJWKS [ i ] . Algorithm , config . Discovery . RegisteredJWKSigningAlgs ) {
val . Push ( fmt . Errorf ( "identity_providers: oidc: issuer_jwks: key #%d with key id '%s': option 'algorithm' must be unique but another key is using it" , i + 1 , config . IssuerJWKS [ i ] . KeyID ) )
} else {
config . Discovery . RegisteredJWKSigningAlgs = append ( config . Discovery . RegisteredJWKSigningAlgs , config . IssuerJWKS [ i ] . Algorithm )
}
}
if config . IssuerJWKS [ i ] . Algorithm == oidc . SigningAlgRSAUsingSHA256 && config . Discovery . DefaultKeyID == "" {
config . Discovery . DefaultKeyID = config . IssuerJWKS [ i ] . KeyID
}
var checkEqualKey bool
switch key := config . IssuerJWKS [ i ] . Key . ( type ) {
case * rsa . PrivateKey :
checkEqualKey = true
if key . Size ( ) < 256 {
checkEqualKey = false
val . Push ( fmt . Errorf ( "identity_providers: oidc: issuer_jwks: key #%d with key id '%s': option 'key' is an RSA %d bit private key but it must be a RSA 2048 bit private key" , i + 1 , config . IssuerJWKS [ i ] . KeyID , key . Size ( ) * 8 ) )
}
case * ecdsa . PrivateKey :
checkEqualKey = true
default :
val . Push ( fmt . Errorf ( "identity_providers: oidc: issuer_jwks: key #%d with key id '%s': option 'key' must be a *rsa.PrivateKey or *ecdsa.PrivateKey but it's a %T" , i + 1 , config . IssuerJWKS [ i ] . KeyID , key ) )
}
if config . IssuerJWKS [ i ] . CertificateChain . HasCertificates ( ) {
if checkEqualKey && ! config . IssuerJWKS [ i ] . CertificateChain . EqualKey ( config . IssuerJWKS [ i ] . Key ) {
val . Push ( fmt . Errorf ( "identity_providers: oidc: issuer_jwks: key #%d with key id '%s': option 'key' does not appear to be the private key the certificate provided by option 'certificate_chain'" , i + 1 , config . IssuerJWKS [ i ] . KeyID ) )
}
if err = config . IssuerJWKS [ i ] . CertificateChain . Validate ( ) ; err != nil {
val . Push ( fmt . Errorf ( "identity_providers: oidc: issuer_jwks: key #%d with key id '%s': option 'certificate_chain' produced an error during validation of the chain: %w" , i + 1 , config . IssuerJWKS [ i ] . KeyID , err ) )
}
}
}
if len ( config . Discovery . RegisteredJWKSigningAlgs ) != 0 && ! utils . IsStringInSlice ( oidc . SigningAlgRSAUsingSHA256 , config . Discovery . RegisteredJWKSigningAlgs ) {
val . Push ( fmt . Errorf ( "identity_providers: oidc: issuer_jwks: keys: must at least have one key supporting the '%s' algorithm but only has %s" , oidc . SigningAlgRSAUsingSHA256 , strJoinAnd ( config . Discovery . RegisteredJWKSigningAlgs ) ) )
}
}
2022-10-02 02:07:40 +00:00
func setOIDCDefaults ( config * schema . OpenIDConnectConfiguration ) {
if config . AccessTokenLifespan == time . Duration ( 0 ) {
config . AccessTokenLifespan = schema . DefaultOpenIDConnectConfiguration . AccessTokenLifespan
}
if config . AuthorizeCodeLifespan == time . Duration ( 0 ) {
config . AuthorizeCodeLifespan = schema . DefaultOpenIDConnectConfiguration . AuthorizeCodeLifespan
}
if config . IDTokenLifespan == time . Duration ( 0 ) {
config . IDTokenLifespan = schema . DefaultOpenIDConnectConfiguration . IDTokenLifespan
}
if config . RefreshTokenLifespan == time . Duration ( 0 ) {
config . RefreshTokenLifespan = schema . DefaultOpenIDConnectConfiguration . RefreshTokenLifespan
}
if config . EnforcePKCE == "" {
config . EnforcePKCE = schema . DefaultOpenIDConnectConfiguration . EnforcePKCE
2021-05-04 22:06:05 +00:00
}
}
2022-04-07 00:58:51 +00:00
func validateOIDCOptionsCORS ( config * schema . OpenIDConnectConfiguration , validator * schema . StructValidator ) {
validateOIDCOptionsCORSAllowedOrigins ( config , validator )
if config . CORS . AllowedOriginsFromClientRedirectURIs {
validateOIDCOptionsCORSAllowedOriginsFromClientRedirectURIs ( config )
}
validateOIDCOptionsCORSEndpoints ( config , validator )
}
2022-11-23 23:16:23 +00:00
func validateOIDCOptionsCORSAllowedOrigins ( config * schema . OpenIDConnectConfiguration , val * schema . StructValidator ) {
2022-04-07 00:58:51 +00:00
for _ , origin := range config . CORS . AllowedOrigins {
if origin . String ( ) == "*" {
if len ( config . CORS . AllowedOrigins ) != 1 {
2022-11-23 23:16:23 +00:00
val . Push ( fmt . Errorf ( errFmtOIDCCORSInvalidOriginWildcard ) )
2022-04-07 00:58:51 +00:00
}
if config . CORS . AllowedOriginsFromClientRedirectURIs {
2022-11-23 23:16:23 +00:00
val . Push ( fmt . Errorf ( errFmtOIDCCORSInvalidOriginWildcardWithClients ) )
2022-04-07 00:58:51 +00:00
}
continue
}
if origin . Path != "" {
2022-11-23 23:16:23 +00:00
val . Push ( fmt . Errorf ( errFmtOIDCCORSInvalidOrigin , origin . String ( ) , "path" ) )
2022-04-07 00:58:51 +00:00
}
if origin . RawQuery != "" {
2022-11-23 23:16:23 +00:00
val . Push ( fmt . Errorf ( errFmtOIDCCORSInvalidOrigin , origin . String ( ) , "query string" ) )
2022-04-07 00:58:51 +00:00
}
}
}
func validateOIDCOptionsCORSAllowedOriginsFromClientRedirectURIs ( config * schema . OpenIDConnectConfiguration ) {
for _ , client := range config . Clients {
for _ , redirectURI := range client . RedirectURIs {
2022-09-03 01:51:02 +00:00
uri , err := url . ParseRequestURI ( redirectURI )
2022-04-07 00:58:51 +00:00
if err != nil || ( uri . Scheme != schemeHTTP && uri . Scheme != schemeHTTPS ) || uri . Hostname ( ) == "localhost" {
continue
}
2023-04-13 10:58:18 +00:00
origin := utils . OriginFromURL ( uri )
2022-04-07 00:58:51 +00:00
2023-04-13 10:58:18 +00:00
if ! utils . IsURLInSlice ( * origin , config . CORS . AllowedOrigins ) {
config . CORS . AllowedOrigins = append ( config . CORS . AllowedOrigins , * origin )
2022-04-07 00:58:51 +00:00
}
}
}
}
2022-11-23 23:16:23 +00:00
func validateOIDCOptionsCORSEndpoints ( config * schema . OpenIDConnectConfiguration , val * schema . StructValidator ) {
2022-04-07 00:58:51 +00:00
for _ , endpoint := range config . CORS . Endpoints {
if ! utils . IsStringInSlice ( endpoint , validOIDCCORSEndpoints ) {
2023-04-13 10:58:18 +00:00
val . Push ( fmt . Errorf ( errFmtOIDCCORSInvalidEndpoint , endpoint , strJoinOr ( validOIDCCORSEndpoints ) ) )
2022-04-07 00:58:51 +00:00
}
}
}
2022-09-03 01:51:02 +00:00
2022-11-23 23:16:23 +00:00
func validateOIDCClients ( config * schema . OpenIDConnectConfiguration , val * schema . StructValidator ) {
2023-04-13 10:58:18 +00:00
var (
errDeprecated bool
2021-05-04 22:06:05 +00:00
2023-04-13 10:58:18 +00:00
clientIDs , duplicateClientIDs , blankClientIDs [ ] string
)
errDeprecatedFunc := func ( ) { errDeprecated = true }
2021-05-04 22:06:05 +00:00
2022-02-28 03:15:01 +00:00
for c , client := range config . Clients {
2021-05-04 22:06:05 +00:00
if client . ID == "" {
2023-04-13 10:58:18 +00:00
blankClientIDs = append ( blankClientIDs , "#" + strconv . Itoa ( c + 1 ) )
2021-05-04 22:06:05 +00:00
} else {
if client . Description == "" {
2022-02-28 03:15:01 +00:00
config . Clients [ c ] . Description = client . ID
2021-05-04 22:06:05 +00:00
}
2023-04-13 10:58:18 +00:00
if id := strings . ToLower ( client . ID ) ; utils . IsStringInSlice ( id , clientIDs ) {
if ! utils . IsStringInSlice ( id , duplicateClientIDs ) {
duplicateClientIDs = append ( duplicateClientIDs , id )
}
} else {
clientIDs = append ( clientIDs , id )
2021-05-04 22:06:05 +00:00
}
}
2023-04-13 10:58:18 +00:00
validateOIDCClient ( c , config , val , errDeprecatedFunc )
}
2021-05-04 22:06:05 +00:00
2023-04-13 10:58:18 +00:00
if errDeprecated {
val . PushWarning ( fmt . Errorf ( errFmtOIDCClientsDeprecated ) )
}
2021-05-04 22:06:05 +00:00
2023-04-13 10:58:18 +00:00
if len ( blankClientIDs ) != 0 {
val . Push ( fmt . Errorf ( errFmtOIDCClientsWithEmptyID , buildJoinedString ( ", " , "or" , "" , blankClientIDs ) ) )
}
2023-01-03 15:03:23 +00:00
2023-04-13 10:58:18 +00:00
if len ( duplicateClientIDs ) != 0 {
val . Push ( fmt . Errorf ( errFmtOIDCClientsDuplicateID , strJoinOr ( duplicateClientIDs ) ) )
2021-05-04 22:06:05 +00:00
}
2023-04-13 10:58:18 +00:00
}
2021-05-04 22:06:05 +00:00
2023-04-13 10:58:18 +00:00
func validateOIDCClient ( c int , config * schema . OpenIDConnectConfiguration , val * schema . StructValidator , errDeprecatedFunc func ( ) ) {
if config . Clients [ c ] . Public {
if config . Clients [ c ] . Secret != nil {
val . Push ( fmt . Errorf ( errFmtOIDCClientPublicInvalidSecret , config . Clients [ c ] . ID ) )
}
} else {
if config . Clients [ c ] . Secret == nil {
val . Push ( fmt . Errorf ( errFmtOIDCClientInvalidSecret , config . Clients [ c ] . ID ) )
2023-04-15 10:55:38 +00:00
} else {
switch {
case config . Clients [ c ] . Secret . IsPlainText ( ) && config . Clients [ c ] . TokenEndpointAuthMethod != oidc . ClientAuthMethodClientSecretJWT :
val . PushWarning ( fmt . Errorf ( errFmtOIDCClientInvalidSecretPlainText , config . Clients [ c ] . ID ) )
case ! config . Clients [ c ] . Secret . IsPlainText ( ) && config . Clients [ c ] . TokenEndpointAuthMethod == oidc . ClientAuthMethodClientSecretJWT :
val . Push ( fmt . Errorf ( errFmtOIDCClientInvalidSecretNotPlainText , config . Clients [ c ] . ID ) )
}
2023-04-13 10:58:18 +00:00
}
2021-05-04 22:06:05 +00:00
}
2023-04-13 10:58:18 +00:00
switch config . Clients [ c ] . Policy {
case "" :
config . Clients [ c ] . Policy = schema . DefaultOpenIDConnectClientConfiguration . Policy
case policyOneFactor , policyTwoFactor :
break
default :
val . Push ( fmt . Errorf ( errFmtOIDCClientInvalidValue , config . Clients [ c ] . ID , "policy" , strJoinOr ( [ ] string { policyOneFactor , policyTwoFactor } ) , config . Clients [ c ] . Policy ) )
}
switch config . Clients [ c ] . PKCEChallengeMethod {
case "" , oidc . PKCEChallengeMethodPlain , oidc . PKCEChallengeMethodSHA256 :
break
default :
val . Push ( fmt . Errorf ( errFmtOIDCClientInvalidValue , config . Clients [ c ] . ID , attrOIDCPKCEChallengeMethod , strJoinOr ( [ ] string { oidc . PKCEChallengeMethodPlain , oidc . PKCEChallengeMethodSHA256 } ) , config . Clients [ c ] . PKCEChallengeMethod ) )
2021-05-04 22:06:05 +00:00
}
2023-04-13 10:58:18 +00:00
validateOIDCClientConsentMode ( c , config , val )
validateOIDCClientScopes ( c , config , val , errDeprecatedFunc )
validateOIDCClientResponseTypes ( c , config , val , errDeprecatedFunc )
validateOIDCClientResponseModes ( c , config , val , errDeprecatedFunc )
validateOIDCClientGrantTypes ( c , config , val , errDeprecatedFunc )
validateOIDCClientRedirectURIs ( c , config , val , errDeprecatedFunc )
2023-04-15 10:55:38 +00:00
validateOIDCClientTokenEndpointAuth ( c , config , val )
2023-04-19 04:24:05 +00:00
validateOIDDClientSigningAlgs ( c , config , val )
2023-04-13 10:58:18 +00:00
validateOIDCClientSectorIdentifier ( c , config , val )
2021-05-04 22:06:05 +00:00
}
2023-04-13 10:58:18 +00:00
func validateOIDCClientSectorIdentifier ( c int , config * schema . OpenIDConnectConfiguration , val * schema . StructValidator ) {
if config . Clients [ c ] . SectorIdentifier . String ( ) != "" {
if utils . IsURLHostComponent ( config . Clients [ c ] . SectorIdentifier ) || utils . IsURLHostComponentWithPort ( config . Clients [ c ] . SectorIdentifier ) {
2022-04-08 07:38:38 +00:00
return
}
2023-04-13 10:58:18 +00:00
if config . Clients [ c ] . SectorIdentifier . Scheme != "" {
val . Push ( fmt . Errorf ( errFmtOIDCClientInvalidSectorIdentifier , config . Clients [ c ] . ID , config . Clients [ c ] . SectorIdentifier . String ( ) , config . Clients [ c ] . SectorIdentifier . Host , "scheme" , config . Clients [ c ] . SectorIdentifier . Scheme ) )
2022-04-07 06:13:01 +00:00
2023-04-13 10:58:18 +00:00
if config . Clients [ c ] . SectorIdentifier . Path != "" {
val . Push ( fmt . Errorf ( errFmtOIDCClientInvalidSectorIdentifier , config . Clients [ c ] . ID , config . Clients [ c ] . SectorIdentifier . String ( ) , config . Clients [ c ] . SectorIdentifier . Host , "path" , config . Clients [ c ] . SectorIdentifier . Path ) )
2022-04-07 06:13:01 +00:00
}
2023-04-13 10:58:18 +00:00
if config . Clients [ c ] . SectorIdentifier . RawQuery != "" {
val . Push ( fmt . Errorf ( errFmtOIDCClientInvalidSectorIdentifier , config . Clients [ c ] . ID , config . Clients [ c ] . SectorIdentifier . String ( ) , config . Clients [ c ] . SectorIdentifier . Host , "query" , config . Clients [ c ] . SectorIdentifier . RawQuery ) )
2022-04-07 06:13:01 +00:00
}
2023-04-13 10:58:18 +00:00
if config . Clients [ c ] . SectorIdentifier . Fragment != "" {
val . Push ( fmt . Errorf ( errFmtOIDCClientInvalidSectorIdentifier , config . Clients [ c ] . ID , config . Clients [ c ] . SectorIdentifier . String ( ) , config . Clients [ c ] . SectorIdentifier . Host , "fragment" , config . Clients [ c ] . SectorIdentifier . Fragment ) )
2022-04-07 06:13:01 +00:00
}
2023-04-13 10:58:18 +00:00
if config . Clients [ c ] . SectorIdentifier . User != nil {
if config . Clients [ c ] . SectorIdentifier . User . Username ( ) != "" {
val . Push ( fmt . Errorf ( errFmtOIDCClientInvalidSectorIdentifier , config . Clients [ c ] . ID , config . Clients [ c ] . SectorIdentifier . String ( ) , config . Clients [ c ] . SectorIdentifier . Host , "username" , config . Clients [ c ] . SectorIdentifier . User . Username ( ) ) )
2022-04-07 06:13:01 +00:00
}
2023-04-13 10:58:18 +00:00
if _ , set := config . Clients [ c ] . SectorIdentifier . User . Password ( ) ; set {
val . Push ( fmt . Errorf ( errFmtOIDCClientInvalidSectorIdentifierWithoutValue , config . Clients [ c ] . ID , config . Clients [ c ] . SectorIdentifier . String ( ) , config . Clients [ c ] . SectorIdentifier . Host , "password" ) )
2022-04-07 06:13:01 +00:00
}
}
2023-04-13 10:58:18 +00:00
} else if config . Clients [ c ] . SectorIdentifier . Host == "" {
val . Push ( fmt . Errorf ( errFmtOIDCClientInvalidSectorIdentifierHost , config . Clients [ c ] . ID , config . Clients [ c ] . SectorIdentifier . String ( ) ) )
2022-11-23 23:16:23 +00:00
}
}
}
func validateOIDCClientConsentMode ( c int , config * schema . OpenIDConnectConfiguration , val * schema . StructValidator ) {
switch {
2023-04-13 10:58:18 +00:00
case utils . IsStringInSlice ( config . Clients [ c ] . ConsentMode , [ ] string { "" , auto } ) :
2022-11-23 23:16:23 +00:00
if config . Clients [ c ] . ConsentPreConfiguredDuration != nil {
config . Clients [ c ] . ConsentMode = oidc . ClientConsentModePreConfigured . String ( )
} else {
config . Clients [ c ] . ConsentMode = oidc . ClientConsentModeExplicit . String ( )
2022-04-07 06:13:01 +00:00
}
2022-11-23 23:16:23 +00:00
case utils . IsStringInSlice ( config . Clients [ c ] . ConsentMode , validOIDCClientConsentModes ) :
break
default :
2023-04-13 10:58:18 +00:00
val . Push ( fmt . Errorf ( errFmtOIDCClientInvalidConsentMode , config . Clients [ c ] . ID , strJoinOr ( append ( validOIDCClientConsentModes , auto ) ) , config . Clients [ c ] . ConsentMode ) )
2022-11-23 23:16:23 +00:00
}
if config . Clients [ c ] . ConsentMode == oidc . ClientConsentModePreConfigured . String ( ) && config . Clients [ c ] . ConsentPreConfiguredDuration == nil {
config . Clients [ c ] . ConsentPreConfiguredDuration = schema . DefaultOpenIDConnectClientConfiguration . ConsentPreConfiguredDuration
2022-04-07 06:13:01 +00:00
}
}
2023-04-13 10:58:18 +00:00
func validateOIDCClientScopes ( c int , config * schema . OpenIDConnectConfiguration , val * schema . StructValidator , errDeprecatedFunc func ( ) ) {
2022-11-23 23:16:23 +00:00
if len ( config . Clients [ c ] . Scopes ) == 0 {
config . Clients [ c ] . Scopes = schema . DefaultOpenIDConnectClientConfiguration . 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
}
2022-11-23 23:16:23 +00:00
if ! utils . IsStringInSlice ( oidc . ScopeOpenID , config . Clients [ c ] . Scopes ) {
2023-04-13 10:58:18 +00:00
config . Clients [ c ] . Scopes = append ( [ ] string { oidc . ScopeOpenID } , config . Clients [ c ] . 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
}
2023-04-13 10:58:18 +00:00
invalid , duplicates := validateList ( config . Clients [ c ] . Scopes , validOIDCClientScopes , true )
if len ( invalid ) != 0 {
val . Push ( fmt . Errorf ( errFmtOIDCClientInvalidEntries , config . Clients [ c ] . ID , attrOIDCScopes , strJoinOr ( validOIDCClientScopes ) , strJoinAnd ( invalid ) ) )
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
if len ( duplicates ) != 0 {
errDeprecatedFunc ( )
val . PushWarning ( fmt . Errorf ( errFmtOIDCClientInvalidEntryDuplicates , config . Clients [ c ] . ID , attrOIDCScopes , strJoinAnd ( duplicates ) ) )
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
if utils . IsStringSliceContainsAny ( [ ] string { oidc . ScopeOfflineAccess , oidc . ScopeOffline } , config . Clients [ c ] . Scopes ) &&
! utils . IsStringSliceContainsAny ( validOIDCClientResponseTypesRefreshToken , config . Clients [ c ] . ResponseTypes ) {
errDeprecatedFunc ( )
val . PushWarning ( fmt . Errorf ( errFmtOIDCClientInvalidRefreshTokenOptionWithoutCodeResponseType ,
config . Clients [ c ] . ID , attrOIDCScopes ,
strJoinOr ( [ ] string { oidc . ScopeOfflineAccess , oidc . ScopeOffline } ) ,
strJoinOr ( validOIDCClientResponseTypesRefreshToken ) ) ,
)
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
func validateOIDCClientResponseTypes ( c int , config * schema . OpenIDConnectConfiguration , val * schema . StructValidator , errDeprecatedFunc func ( ) ) {
2022-11-23 23:16:23 +00:00
if len ( config . Clients [ c ] . ResponseTypes ) == 0 {
config . Clients [ c ] . ResponseTypes = schema . DefaultOpenIDConnectClientConfiguration . ResponseTypes
2023-04-13 10:58:18 +00:00
}
invalid , duplicates := validateList ( config . Clients [ c ] . ResponseTypes , validOIDCClientResponseTypes , true )
if len ( invalid ) != 0 {
val . PushWarning ( fmt . Errorf ( errFmtOIDCClientInvalidEntries , config . Clients [ c ] . ID , attrOIDCResponseTypes , strJoinOr ( validOIDCClientResponseTypes ) , strJoinAnd ( invalid ) ) )
}
if len ( duplicates ) != 0 {
errDeprecatedFunc ( )
val . PushWarning ( fmt . Errorf ( errFmtOIDCClientInvalidEntryDuplicates , config . Clients [ c ] . ID , attrOIDCResponseTypes , strJoinAnd ( duplicates ) ) )
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
func validateOIDCClientResponseModes ( c int , config * schema . OpenIDConnectConfiguration , val * schema . StructValidator , errDeprecatedFunc func ( ) ) {
2022-11-23 23:16:23 +00:00
if len ( config . Clients [ c ] . ResponseModes ) == 0 {
config . Clients [ c ] . ResponseModes = schema . DefaultOpenIDConnectClientConfiguration . ResponseModes
2023-04-13 10:58:18 +00:00
for _ , responseType := range config . Clients [ c ] . ResponseTypes {
switch responseType {
case oidc . ResponseTypeAuthorizationCodeFlow :
if ! utils . IsStringInSlice ( oidc . ResponseModeQuery , config . Clients [ c ] . ResponseModes ) {
config . Clients [ c ] . ResponseModes = append ( config . Clients [ c ] . ResponseModes , oidc . ResponseModeQuery )
}
case oidc . ResponseTypeImplicitFlowIDToken , oidc . ResponseTypeImplicitFlowToken , oidc . ResponseTypeImplicitFlowBoth ,
oidc . ResponseTypeHybridFlowIDToken , oidc . ResponseTypeHybridFlowToken , oidc . ResponseTypeHybridFlowBoth :
if ! utils . IsStringInSlice ( oidc . ResponseModeFragment , config . Clients [ c ] . ResponseModes ) {
config . Clients [ c ] . ResponseModes = append ( config . Clients [ c ] . ResponseModes , oidc . ResponseModeFragment )
}
}
}
}
invalid , duplicates := validateList ( config . Clients [ c ] . ResponseModes , validOIDCClientResponseModes , true )
if len ( invalid ) != 0 {
val . Push ( fmt . Errorf ( errFmtOIDCClientInvalidEntries , config . Clients [ c ] . ID , attrOIDCResponseModes , strJoinOr ( validOIDCClientResponseModes ) , strJoinAnd ( invalid ) ) )
}
if len ( duplicates ) != 0 {
errDeprecatedFunc ( )
val . PushWarning ( fmt . Errorf ( errFmtOIDCClientInvalidEntryDuplicates , config . Clients [ c ] . ID , attrOIDCResponseModes , strJoinAnd ( duplicates ) ) )
}
}
func validateOIDCClientGrantTypes ( c int , config * schema . OpenIDConnectConfiguration , val * schema . StructValidator , errDeprecatedFunc func ( ) ) {
if len ( config . Clients [ c ] . GrantTypes ) == 0 {
validateOIDCClientGrantTypesSetDefaults ( c , config )
}
validateOIDCClientGrantTypesCheckRelated ( c , config , val , errDeprecatedFunc )
invalid , duplicates := validateList ( config . Clients [ c ] . GrantTypes , validOIDCClientGrantTypes , true )
if len ( invalid ) != 0 {
val . Push ( fmt . Errorf ( errFmtOIDCClientInvalidEntries , config . Clients [ c ] . ID , attrOIDCGrantTypes , strJoinOr ( validOIDCClientGrantTypes ) , strJoinAnd ( invalid ) ) )
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
if len ( duplicates ) != 0 {
errDeprecatedFunc ( )
val . PushWarning ( fmt . Errorf ( errFmtOIDCClientInvalidEntryDuplicates , config . Clients [ c ] . ID , attrOIDCGrantTypes , strJoinAnd ( duplicates ) ) )
}
}
func validateOIDCClientGrantTypesSetDefaults ( c int , config * schema . OpenIDConnectConfiguration ) {
for _ , responseType := range config . Clients [ c ] . ResponseTypes {
switch responseType {
case oidc . ResponseTypeAuthorizationCodeFlow :
if ! utils . IsStringInSlice ( oidc . GrantTypeAuthorizationCode , config . Clients [ c ] . GrantTypes ) {
config . Clients [ c ] . GrantTypes = append ( config . Clients [ c ] . GrantTypes , oidc . GrantTypeAuthorizationCode )
}
case oidc . ResponseTypeImplicitFlowIDToken , oidc . ResponseTypeImplicitFlowToken , oidc . ResponseTypeImplicitFlowBoth :
if ! utils . IsStringInSlice ( oidc . GrantTypeImplicit , config . Clients [ c ] . GrantTypes ) {
config . Clients [ c ] . GrantTypes = append ( config . Clients [ c ] . GrantTypes , oidc . GrantTypeImplicit )
}
case oidc . ResponseTypeHybridFlowIDToken , oidc . ResponseTypeHybridFlowToken , oidc . ResponseTypeHybridFlowBoth :
if ! utils . IsStringInSlice ( oidc . GrantTypeAuthorizationCode , config . Clients [ c ] . GrantTypes ) {
config . Clients [ c ] . GrantTypes = append ( config . Clients [ c ] . GrantTypes , oidc . GrantTypeAuthorizationCode )
}
if ! utils . IsStringInSlice ( oidc . GrantTypeImplicit , config . Clients [ c ] . GrantTypes ) {
config . Clients [ c ] . GrantTypes = append ( config . Clients [ c ] . GrantTypes , oidc . GrantTypeImplicit )
}
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
func validateOIDCClientGrantTypesCheckRelated ( c int , config * schema . OpenIDConnectConfiguration , val * schema . StructValidator , errDeprecatedFunc func ( ) ) {
for _ , grantType := range config . Clients [ c ] . GrantTypes {
switch grantType {
case oidc . GrantTypeImplicit :
if ! utils . IsStringSliceContainsAny ( validOIDCClientResponseTypesImplicitFlow , config . Clients [ c ] . ResponseTypes ) && ! utils . IsStringSliceContainsAny ( validOIDCClientResponseTypesHybridFlow , config . Clients [ c ] . ResponseTypes ) {
errDeprecatedFunc ( )
val . PushWarning ( fmt . Errorf ( errFmtOIDCClientInvalidGrantTypeMatch , config . Clients [ c ] . ID , grantType , "for either the implicit or hybrid flow" , strJoinOr ( append ( append ( [ ] string { } , validOIDCClientResponseTypesImplicitFlow ... ) , validOIDCClientResponseTypesHybridFlow ... ) ) , strJoinAnd ( config . Clients [ c ] . ResponseTypes ) ) )
}
case oidc . GrantTypeAuthorizationCode :
if ! utils . IsStringInSlice ( oidc . ResponseTypeAuthorizationCodeFlow , config . Clients [ c ] . ResponseTypes ) && ! utils . IsStringSliceContainsAny ( validOIDCClientResponseTypesHybridFlow , config . Clients [ c ] . ResponseTypes ) {
errDeprecatedFunc ( )
val . PushWarning ( fmt . Errorf ( errFmtOIDCClientInvalidGrantTypeMatch , config . Clients [ c ] . ID , grantType , "for either the authorization code or hybrid flow" , strJoinOr ( append ( [ ] string { oidc . ResponseTypeAuthorizationCodeFlow } , validOIDCClientResponseTypesHybridFlow ... ) ) , strJoinAnd ( config . Clients [ c ] . ResponseTypes ) ) )
}
case oidc . GrantTypeRefreshToken :
if ! utils . IsStringSliceContainsAny ( [ ] string { oidc . ScopeOfflineAccess , oidc . ScopeOffline } , config . Clients [ c ] . Scopes ) {
errDeprecatedFunc ( )
val . PushWarning ( fmt . Errorf ( errFmtOIDCClientInvalidGrantTypeRefresh , config . Clients [ c ] . ID ) )
}
if ! utils . IsStringSliceContainsAny ( validOIDCClientResponseTypesRefreshToken , config . Clients [ c ] . ResponseTypes ) {
errDeprecatedFunc ( )
val . PushWarning ( fmt . Errorf ( errFmtOIDCClientInvalidRefreshTokenOptionWithoutCodeResponseType ,
config . Clients [ c ] . ID , attrOIDCGrantTypes ,
strJoinOr ( [ ] string { oidc . GrantTypeRefreshToken } ) ,
strJoinOr ( validOIDCClientResponseTypesRefreshToken ) ) ,
)
}
}
2021-07-10 04:56:33 +00:00
}
}
2023-04-13 10:58:18 +00:00
func validateOIDCClientRedirectURIs ( c int , config * schema . OpenIDConnectConfiguration , val * schema . StructValidator , errDeprecatedFunc func ( ) ) {
var (
parsedRedirectURI * url . URL
err error
)
for _ , redirectURI := range config . Clients [ c ] . RedirectURIs {
2021-07-15 11:02:03 +00:00
if redirectURI == oauth2InstalledApp {
2023-04-13 10:58:18 +00:00
if config . Clients [ c ] . Public {
2021-07-15 11:02:03 +00:00
continue
}
2023-04-13 10:58:18 +00:00
val . Push ( fmt . Errorf ( errFmtOIDCClientRedirectURIPublic , config . Clients [ c ] . ID , oauth2InstalledApp ) )
2021-05-04 22:06:05 +00:00
2021-07-15 11:02:03 +00:00
continue
}
2023-04-13 10:58:18 +00:00
if parsedRedirectURI , err = url . Parse ( redirectURI ) ; err != nil {
val . Push ( fmt . Errorf ( errFmtOIDCClientRedirectURICantBeParsed , config . Clients [ c ] . ID , redirectURI , err ) )
2021-07-15 11:02:03 +00:00
continue
}
2023-04-13 10:58:18 +00:00
if ! parsedRedirectURI . IsAbs ( ) || ( ! config . Clients [ c ] . Public && parsedRedirectURI . Scheme == "" ) {
val . Push ( fmt . Errorf ( errFmtOIDCClientRedirectURIAbsolute , config . Clients [ c ] . ID , redirectURI ) )
2021-07-15 11:02:03 +00:00
return
2021-05-04 22:06:05 +00:00
}
}
2023-04-13 10:58:18 +00:00
_ , duplicates := validateList ( config . Clients [ c ] . RedirectURIs , nil , true )
if len ( duplicates ) != 0 {
errDeprecatedFunc ( )
val . PushWarning ( fmt . Errorf ( errFmtOIDCClientInvalidEntryDuplicates , config . Clients [ c ] . ID , attrOIDCRedirectURIs , strJoinAnd ( duplicates ) ) )
}
}
2023-04-15 10:55:38 +00:00
func validateOIDCClientTokenEndpointAuth ( c int , config * schema . OpenIDConnectConfiguration , val * schema . StructValidator ) {
2023-04-13 10:58:18 +00:00
implcit := len ( config . Clients [ c ] . ResponseTypes ) != 0 && utils . IsStringSliceContainsAll ( config . Clients [ c ] . ResponseTypes , validOIDCClientResponseTypesImplicitFlow )
switch {
case config . Clients [ c ] . TokenEndpointAuthMethod == "" :
break
case ! utils . IsStringInSlice ( config . Clients [ c ] . TokenEndpointAuthMethod , validOIDCClientTokenEndpointAuthMethods ) :
val . Push ( fmt . Errorf ( errFmtOIDCClientInvalidValue ,
config . Clients [ c ] . ID , attrOIDCTokenAuthMethod , strJoinOr ( validOIDCClientTokenEndpointAuthMethods ) , config . Clients [ c ] . TokenEndpointAuthMethod ) )
case config . Clients [ c ] . TokenEndpointAuthMethod == oidc . ClientAuthMethodNone && ! config . Clients [ c ] . Public && ! implcit :
val . Push ( fmt . Errorf ( errFmtOIDCClientInvalidTokenEndpointAuthMethod ,
config . Clients [ c ] . ID , strJoinOr ( validOIDCClientTokenEndpointAuthMethodsConfidential ) , strJoinAnd ( validOIDCClientResponseTypesImplicitFlow ) , config . Clients [ c ] . TokenEndpointAuthMethod ) )
case config . Clients [ c ] . TokenEndpointAuthMethod != oidc . ClientAuthMethodNone && config . Clients [ c ] . Public :
val . Push ( fmt . Errorf ( errFmtOIDCClientInvalidTokenEndpointAuthMethodPublic ,
config . Clients [ c ] . ID , config . Clients [ c ] . TokenEndpointAuthMethod ) )
}
2023-04-15 10:55:38 +00:00
switch config . Clients [ c ] . TokenEndpointAuthMethod {
case "" :
break
case oidc . ClientAuthMethodClientSecretJWT :
switch {
case config . Clients [ c ] . TokenEndpointAuthSigningAlg == "" :
config . Clients [ c ] . TokenEndpointAuthSigningAlg = oidc . SigningAlgHMACUsingSHA256
case ! utils . IsStringInSlice ( config . Clients [ c ] . TokenEndpointAuthSigningAlg , validOIDCClientTokenEndpointAuthSigAlgs ) :
val . Push ( fmt . Errorf ( errFmtOIDCClientInvalidTokenEndpointAuthSigAlg , config . Clients [ c ] . ID , strJoinOr ( validOIDCClientTokenEndpointAuthSigAlgs ) , config . Clients [ c ] . TokenEndpointAuthMethod ) )
}
}
2023-04-13 10:58:18 +00:00
}
2023-04-19 04:24:05 +00:00
func validateOIDDClientSigningAlgs ( c int , config * schema . OpenIDConnectConfiguration , val * schema . StructValidator ) {
if config . Clients [ c ] . UserinfoSigningAlg == "" {
config . Clients [ c ] . UserinfoSigningAlg = schema . DefaultOpenIDConnectClientConfiguration . UserinfoSigningAlg
} else if config . Clients [ c ] . UserinfoSigningAlg != oidc . SigningAlgNone && ! utils . IsStringInSlice ( config . Clients [ c ] . UserinfoSigningAlg , config . Discovery . RegisteredJWKSigningAlgs ) {
val . Push ( fmt . Errorf ( errFmtOIDCClientInvalidValue ,
config . Clients [ c ] . ID , attrOIDCUsrSigAlg , strJoinOr ( append ( config . Discovery . RegisteredJWKSigningAlgs , oidc . SigningAlgNone ) ) , config . Clients [ c ] . UserinfoSigningAlg ) )
2023-04-13 10:58:18 +00:00
}
2023-04-19 04:24:05 +00:00
if config . Clients [ c ] . IDTokenSigningAlg == "" {
config . Clients [ c ] . IDTokenSigningAlg = schema . DefaultOpenIDConnectClientConfiguration . IDTokenSigningAlg
} else if ! utils . IsStringInSlice ( config . Clients [ c ] . IDTokenSigningAlg , config . Discovery . RegisteredJWKSigningAlgs ) {
2023-04-13 10:58:18 +00:00
val . Push ( fmt . Errorf ( errFmtOIDCClientInvalidValue ,
2023-04-19 04:24:05 +00:00
config . Clients [ c ] . ID , attrOIDCIDTokenSigAlg , strJoinOr ( config . Discovery . RegisteredJWKSigningAlgs ) , config . Clients [ c ] . IDTokenSigningAlg ) )
2023-04-13 10:58:18 +00:00
}
2021-05-04 22:06:05 +00:00
}