feat(oidc): implement client type public (#2171)
This implements the public option for clients which allows using Authelia as an OpenID Connect Provider for cli applications and SPA's where the client secret cannot be considered secure.pull/2187/head
parent
0da770d900
commit
8342a46ba1
|
@ -595,7 +595,7 @@ notifier:
|
|||
## Enables additional debug messages.
|
||||
# enable_client_debug_messages: false
|
||||
|
||||
## SECURITY NOTICE: It's not recommended to change this option, and highly discouraged to have it below 8 for
|
||||
## SECURITY NOTICE: It's not recommended changing this option, and highly discouraged to have it below 8 for
|
||||
## security reasons.
|
||||
# minimum_parameter_entropy: 8
|
||||
|
||||
|
@ -611,36 +611,42 @@ notifier:
|
|||
## The client secret is a shared secret between Authelia and the consumer of this client.
|
||||
# secret: this_is_a_secret
|
||||
|
||||
## Sets the client to public. This should typically not be set, please see the documentation for usage.
|
||||
# public: false
|
||||
|
||||
## The policy to require for this client; one_factor or two_factor.
|
||||
# authorization_policy: two_factor
|
||||
|
||||
## Audience this client is allowed to request.
|
||||
# audience: []
|
||||
|
||||
## Scopes this client is allowed to request.
|
||||
# scopes:
|
||||
# - openid
|
||||
# - groups
|
||||
# - email
|
||||
# - profile
|
||||
|
||||
## Redirect URI's specifies a list of valid case-sensitive callbacks for this client.
|
||||
# redirect_uris:
|
||||
# - https://oidc.example.com:8080/oauth2/callback
|
||||
|
||||
## Scopes defines the valid scopes this client can request
|
||||
# scopes:
|
||||
# - openid
|
||||
# - groups
|
||||
# - email
|
||||
# - profile
|
||||
|
||||
## Grant Types configures which grants this client can obtain.
|
||||
## It's not recommended to define this unless you know what you're doing.
|
||||
# grant_types:
|
||||
# - refresh_token
|
||||
# - authorization_code
|
||||
# - refresh_token
|
||||
# - authorization_code
|
||||
|
||||
## Response Types configures which responses this client can be sent.
|
||||
## It's not recommended to define this unless you know what you're doing.
|
||||
# response_types:
|
||||
# - code
|
||||
# - code
|
||||
|
||||
## Response Modes configures which response modes this client supports.
|
||||
# response_modes:
|
||||
# - form_post
|
||||
# - query
|
||||
# - fragment
|
||||
# - form_post
|
||||
# - query
|
||||
# - fragment
|
||||
|
||||
## The algorithm used to sign userinfo endpoint responses for this client, either none or RS256.
|
||||
# userinfo_signing_algorithm: none
|
||||
|
|
|
@ -34,7 +34,7 @@ for which stage will have each feature, and may evolve over time:
|
|||
</thead>
|
||||
<tbody>
|
||||
<tr>
|
||||
<td rowspan="7" class="tbl-header tbl-beta-stage">beta1</td>
|
||||
<td rowspan="8" class="tbl-header tbl-beta-stage">beta1 (4.29.0)</td>
|
||||
<td><a href="https://openid.net/specs/openid-connect-core-1_0.html#Consent" target="_blank" rel="noopener noreferrer">User Consent</a></td>
|
||||
</tr>
|
||||
<tr>
|
||||
|
@ -56,9 +56,27 @@ for which stage will have each feature, and may evolve over time:
|
|||
<td class="tbl-beta-stage">Per Client List of Valid Redirection URI's</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td rowspan="1" class="tbl-header tbl-beta-stage">beta2 <sup>1</sup></td>
|
||||
<td class="tbl-beta-stage"><a href="https://datatracker.ietf.org/doc/html/rfc6749#section-2.1" target="_blank"rel="noopener noreferrer">Confidential Client Type</a></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td rowspan="6" class="tbl-header tbl-beta-stage">beta2 (4.30.0) <sup>1</sup></td>
|
||||
<td class="tbl-beta-stage"><a href="https://openid.net/specs/openid-connect-core-1_0.html#UserInfo" target="_blank" rel="noopener noreferrer">Userinfo Endpoint</a> (missed in beta1)</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td class="tbl-beta-stage">Parameter Entropy Configuration</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td class="tbl-beta-stage">Token/Code Lifespan Configuration</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td class="tbl-beta-stage">Client Debug Messages</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td class="tbl-beta-stage">Client Audience</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td class="tbl-beta-stage"><a href="https://datatracker.ietf.org/doc/html/rfc6749#section-2.1" target="_blank"rel="noopener noreferrer">Public Client Type</a></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td rowspan="2" class="tbl-header tbl-beta-stage">beta3 <sup>1</sup></td>
|
||||
<td>Token Storage</td>
|
||||
|
@ -117,20 +135,22 @@ identity_providers:
|
|||
access_token_lifespan: 1h
|
||||
authorize_code_lifespan: 1m
|
||||
id_token_lifespan: 1h
|
||||
refresh_token_lifespan: 720h
|
||||
refresh_token_lifespan: 90m
|
||||
enable_client_debug_messages: false
|
||||
clients:
|
||||
- id: myapp
|
||||
description: My Application
|
||||
secret: this_is_a_secret
|
||||
public: false
|
||||
authorization_policy: two_factor
|
||||
redirect_uris:
|
||||
- https://oidc.example.com:8080/oauth2/callback
|
||||
audience: []
|
||||
scopes:
|
||||
- openid
|
||||
- groups
|
||||
- email
|
||||
- profile
|
||||
redirect_uris:
|
||||
- https://oidc.example.com:8080/oauth2/callback
|
||||
grant_types:
|
||||
- refresh_token
|
||||
- authorization_code
|
||||
|
@ -222,7 +242,7 @@ The maximum lifetime of an ID token. For more information read these docs about
|
|||
<div markdown="1">
|
||||
type: string
|
||||
{: .label .label-config .label-purple }
|
||||
default: 30d
|
||||
default: 90m
|
||||
{: .label .label-config .label-blue }
|
||||
required: no
|
||||
{: .label .label-config .label-green }
|
||||
|
@ -232,6 +252,11 @@ The maximum lifetime of a refresh token. The
|
|||
refresh token can be used to obtain new refresh tokens as well as access tokens or id tokens with an
|
||||
up-to-date expiration. For more information read these docs about [token lifespan].
|
||||
|
||||
A good starting point is 50% more or 30 minutes more (which ever is less) time than the highest lifespan out of the
|
||||
[access token lifespan](#access_token_lifespan), the [authorize code lifespan](#authorize_code_lifespan), and the
|
||||
[id token lifespan](#id_token_lifespan). For instance the default for all of these is 60 minutes, so the default refresh
|
||||
token lifespan is 90 minutes.
|
||||
|
||||
### enable_client_debug_messages
|
||||
|
||||
<div markdown="1">
|
||||
|
@ -296,14 +321,35 @@ A friendly description for this client shown in the UI. This defaults to the sam
|
|||
<div markdown="1">
|
||||
type: string
|
||||
{: .label .label-config .label-purple }
|
||||
required: yes
|
||||
{: .label .label-config .label-red }
|
||||
required: situational
|
||||
{: .label .label-config .label-yellow }
|
||||
</div>
|
||||
|
||||
The shared secret between Authelia and the application consuming this client. This secret must
|
||||
match the secret configured in the application. Currently this is stored in plain text.
|
||||
You must [generate this option yourself](#generating-a-random-secret).
|
||||
|
||||
This must be provided when the client is a confidential client type, and must be blank when using the public client
|
||||
type. To set the client type to public see the [public](#public) configuration option.
|
||||
|
||||
#### public
|
||||
|
||||
<div markdown="1">
|
||||
type: bool
|
||||
{: .label .label-config .label-purple }
|
||||
default: false
|
||||
{: .label .label-config .label-blue }
|
||||
required: no
|
||||
{: .label .label-config .label-green }
|
||||
</div>
|
||||
|
||||
This enables the public client type for this client. This is for clients that are not capable of maintaining
|
||||
confidentiality of credentials, you can read more about client types in [RFC6749](https://datatracker.ietf.org/doc/html/rfc6749#section-2.1).
|
||||
This is particularly useful for SPA's and CLI tools. This option requires setting the [client secret](#secret) to a
|
||||
blank string.
|
||||
|
||||
In addition to the standard rules for redirect URIs, public clients can use the `urn:ietf:wg:oauth:2.0:oob` redirect URI.
|
||||
|
||||
#### authorization_policy
|
||||
|
||||
<div markdown="1">
|
||||
|
@ -317,18 +363,16 @@ required: no
|
|||
|
||||
The authorization policy for this client: either `one_factor` or `two_factor`.
|
||||
|
||||
#### redirect_uris
|
||||
#### audience
|
||||
|
||||
<div markdown="1">
|
||||
type: list(string)
|
||||
{: .label .label-config .label-purple }
|
||||
required: yes
|
||||
{: .label .label-config .label-red }
|
||||
required: no
|
||||
{: .label .label-config .label-green }
|
||||
</div>
|
||||
|
||||
A list of valid callback URL´s this client will redirect to. All other callbacks will be considered
|
||||
unsafe. The URL's are case-sensitive and they differ from application to application - the community has
|
||||
provided [a list of URL´s for common applications](../../community/oidc-integrations.md).
|
||||
A list of audiences this client is allowed to request.
|
||||
|
||||
#### scopes
|
||||
|
||||
|
@ -345,6 +389,28 @@ A list of scopes to allow this client to consume. See [scope definitions](#scope
|
|||
information. The documentation for the application you want to use with Authelia will most-likely provide
|
||||
you with the scopes to allow.
|
||||
|
||||
#### redirect_uris
|
||||
|
||||
<div markdown="1">
|
||||
type: list(string)
|
||||
{: .label .label-config .label-purple }
|
||||
required: yes
|
||||
{: .label .label-config .label-red }
|
||||
</div>
|
||||
|
||||
A list of valid callback URIs this client will redirect to. All other callbacks will be considered
|
||||
unsafe. The URIs are case-sensitive and they differ from application to application - the community has
|
||||
provided [a list of URL´s for common applications](../../community/oidc-integrations.md).
|
||||
|
||||
Some restrictions that have been placed on clients and
|
||||
their redirect URIs are as follows:
|
||||
|
||||
1. If a client attempts to authorize with Authelia and its redirect URI is not listed in the client configuration the
|
||||
attempt to authorize wil fail and an error will be generated.
|
||||
2. The redirect URIs are case-sensitive.
|
||||
3. The URI must include a scheme and that scheme must be one of `http` or `https`.
|
||||
4. The client can ignore rule 3 and use `urn:ietf:wg:oauth:2.0:oob` if it is a [public](#public) client type.
|
||||
|
||||
#### grant_types
|
||||
|
||||
<div markdown="1">
|
||||
|
|
|
@ -595,7 +595,7 @@ notifier:
|
|||
## Enables additional debug messages.
|
||||
# enable_client_debug_messages: false
|
||||
|
||||
## SECURITY NOTICE: It's not recommended to change this option, and highly discouraged to have it below 8 for
|
||||
## SECURITY NOTICE: It's not recommended changing this option, and highly discouraged to have it below 8 for
|
||||
## security reasons.
|
||||
# minimum_parameter_entropy: 8
|
||||
|
||||
|
@ -611,36 +611,42 @@ notifier:
|
|||
## The client secret is a shared secret between Authelia and the consumer of this client.
|
||||
# secret: this_is_a_secret
|
||||
|
||||
## Sets the client to public. This should typically not be set, please see the documentation for usage.
|
||||
# public: false
|
||||
|
||||
## The policy to require for this client; one_factor or two_factor.
|
||||
# authorization_policy: two_factor
|
||||
|
||||
## Audience this client is allowed to request.
|
||||
# audience: []
|
||||
|
||||
## Scopes this client is allowed to request.
|
||||
# scopes:
|
||||
# - openid
|
||||
# - groups
|
||||
# - email
|
||||
# - profile
|
||||
|
||||
## Redirect URI's specifies a list of valid case-sensitive callbacks for this client.
|
||||
# redirect_uris:
|
||||
# - https://oidc.example.com:8080/oauth2/callback
|
||||
|
||||
## Scopes defines the valid scopes this client can request
|
||||
# scopes:
|
||||
# - openid
|
||||
# - groups
|
||||
# - email
|
||||
# - profile
|
||||
|
||||
## Grant Types configures which grants this client can obtain.
|
||||
## It's not recommended to define this unless you know what you're doing.
|
||||
# grant_types:
|
||||
# - refresh_token
|
||||
# - authorization_code
|
||||
# - refresh_token
|
||||
# - authorization_code
|
||||
|
||||
## Response Types configures which responses this client can be sent.
|
||||
## It's not recommended to define this unless you know what you're doing.
|
||||
# response_types:
|
||||
# - code
|
||||
# - code
|
||||
|
||||
## Response Modes configures which response modes this client supports.
|
||||
# response_modes:
|
||||
# - form_post
|
||||
# - query
|
||||
# - fragment
|
||||
# - form_post
|
||||
# - query
|
||||
# - fragment
|
||||
|
||||
## The algorithm used to sign userinfo endpoint responses for this client, either none or RS256.
|
||||
# userinfo_signing_algorithm: none
|
||||
|
|
|
@ -25,12 +25,16 @@ type OpenIDConnectConfiguration struct {
|
|||
|
||||
// OpenIDConnectClientConfiguration configuration for an OpenID Connect client.
|
||||
type OpenIDConnectClientConfiguration struct {
|
||||
ID string `mapstructure:"id"`
|
||||
Description string `mapstructure:"description"`
|
||||
Secret string `mapstructure:"secret"`
|
||||
RedirectURIs []string `mapstructure:"redirect_uris"`
|
||||
Policy string `mapstructure:"authorization_policy"`
|
||||
ID string `mapstructure:"id"`
|
||||
Description string `mapstructure:"description"`
|
||||
Secret string `mapstructure:"secret"`
|
||||
Public bool `mapstructure:"public"`
|
||||
|
||||
Policy string `mapstructure:"authorization_policy"`
|
||||
|
||||
Audience []string `mapstructure:"audience"`
|
||||
Scopes []string `mapstructure:"scopes"`
|
||||
RedirectURIs []string `mapstructure:"redirect_uris"`
|
||||
GrantTypes []string `mapstructure:"grant_types"`
|
||||
ResponseTypes []string `mapstructure:"response_types"`
|
||||
ResponseModes []string `mapstructure:"response_modes"`
|
||||
|
|
|
@ -192,7 +192,7 @@ func TestShouldReturnCorrectResultsForValidNetworkGroups(t *testing.T) {
|
|||
}
|
||||
|
||||
validNetwork := IsNetworkGroupValid(config, "internal")
|
||||
invalidNetwork := IsNetworkGroupValid(config, "127.0.0.1")
|
||||
invalidNetwork := IsNetworkGroupValid(config, loopback)
|
||||
|
||||
assert.True(t, validNetwork)
|
||||
assert.False(t, invalidNetwork)
|
||||
|
|
|
@ -443,7 +443,7 @@ func (suite *LDAPAuthenticationBackendSuite) TestShouldHelpDetectNoInputPlacehol
|
|||
}
|
||||
|
||||
func (suite *LDAPAuthenticationBackendSuite) TestShouldAdaptLDAPURL() {
|
||||
suite.Assert().Equal("", validateLDAPURLSimple("127.0.0.1", suite.validator))
|
||||
suite.Assert().Equal("", validateLDAPURLSimple(loopback, suite.validator))
|
||||
|
||||
suite.Assert().False(suite.validator.HasWarnings())
|
||||
suite.Require().Len(suite.validator.Errors(), 1)
|
||||
|
|
|
@ -12,7 +12,7 @@ import (
|
|||
|
||||
func newDefaultConfig() schema.Configuration {
|
||||
config := schema.Configuration{}
|
||||
config.Host = "127.0.0.1"
|
||||
config.Host = loopback
|
||||
config.Port = 9090
|
||||
config.Logging.Level = "info"
|
||||
config.Logging.Format = "text"
|
||||
|
|
|
@ -1,5 +1,10 @@
|
|||
package validator
|
||||
|
||||
const (
|
||||
loopback = "127.0.0.1"
|
||||
oauth2InstalledApp = "urn:ietf:wg:oauth:2.0:oob"
|
||||
)
|
||||
|
||||
const (
|
||||
errFmtDeprecatedConfigurationKey = "[DEPRECATED] The %s configuration option is deprecated and will be " +
|
||||
"removed in %s, please use %s instead"
|
||||
|
@ -14,11 +19,16 @@ const (
|
|||
|
||||
errFmtOIDCServerClientRedirectURI = "OIDC client with ID '%s' redirect URI %s has an invalid scheme '%s', " +
|
||||
"should be http or https"
|
||||
errFmtOIDCClientRedirectURIPublic = "openid connect provider: client with ID '%s' redirect URI '%s' is " +
|
||||
"only valid for the public client type, not the confidential client type"
|
||||
errFmtOIDCClientRedirectURIAbsolute = "openid connect provider: client with ID '%s' redirect URI '%s' is invalid " +
|
||||
"because it has no scheme when it should be http or https"
|
||||
errFmtOIDCServerClientRedirectURICantBeParsed = "OIDC client with ID '%s' has an invalid redirect URI '%s' " +
|
||||
"could not be parsed: %v"
|
||||
errFmtOIDCServerClientInvalidPolicy = "OIDC client with ID '%s' has an invalid policy '%s', " +
|
||||
"should be either 'one_factor' or 'two_factor'"
|
||||
errFmtOIDCServerClientInvalidSecret = "OIDC client with ID '%s' has an empty secret" //nolint:gosec
|
||||
errFmtOIDCServerClientInvalidSecret = "OIDC client with ID '%s' has an empty secret" //nolint:gosec
|
||||
errFmtOIDCClientPublicInvalidSecret = "openid connect provider: client with ID '%s' is public but does not have an empty secret" //nolint:gosec
|
||||
errFmtOIDCServerClientInvalidScope = "OIDC client with ID '%s' has an invalid scope '%s', " +
|
||||
"must be one of: '%s'"
|
||||
errFmtOIDCServerClientInvalidGrantType = "OIDC client with ID '%s' has an invalid grant type '%s', " +
|
||||
|
|
|
@ -68,8 +68,14 @@ func validateOIDCClients(configuration *schema.OpenIDConnectConfiguration, valid
|
|||
ids = append(ids, client.ID)
|
||||
}
|
||||
|
||||
if client.Secret == "" {
|
||||
validator.Push(fmt.Errorf(errFmtOIDCServerClientInvalidSecret, client.ID))
|
||||
if client.Public {
|
||||
if client.Secret != "" {
|
||||
validator.Push(fmt.Errorf(errFmtOIDCClientPublicInvalidSecret, client.ID))
|
||||
}
|
||||
} else {
|
||||
if client.Secret == "" {
|
||||
validator.Push(fmt.Errorf(errFmtOIDCServerClientInvalidSecret, client.ID))
|
||||
}
|
||||
}
|
||||
|
||||
if client.Policy == "" {
|
||||
|
@ -163,15 +169,29 @@ func validateOIDDClientUserinfoAlgorithm(c int, configuration *schema.OpenIDConn
|
|||
|
||||
func validateOIDCClientRedirectURIs(client schema.OpenIDConnectClientConfiguration, validator *schema.StructValidator) {
|
||||
for _, redirectURI := range client.RedirectURIs {
|
||||
parsedURI, err := url.Parse(redirectURI)
|
||||
if redirectURI == oauth2InstalledApp {
|
||||
if client.Public {
|
||||
continue
|
||||
}
|
||||
|
||||
if err != nil {
|
||||
validator.Push(fmt.Errorf(errFmtOIDCServerClientRedirectURICantBeParsed, client.ID, redirectURI, err))
|
||||
break
|
||||
validator.Push(fmt.Errorf(errFmtOIDCClientRedirectURIPublic, client.ID, redirectURI))
|
||||
|
||||
continue
|
||||
}
|
||||
|
||||
if parsedURI.Scheme != schemeHTTPS && parsedURI.Scheme != schemeHTTP {
|
||||
validator.Push(fmt.Errorf(errFmtOIDCServerClientRedirectURI, client.ID, redirectURI, parsedURI.Scheme))
|
||||
parsedURL, err := url.Parse(redirectURI)
|
||||
if err != nil {
|
||||
validator.Push(fmt.Errorf(errFmtOIDCServerClientRedirectURICantBeParsed, client.ID, redirectURI, err))
|
||||
continue
|
||||
}
|
||||
|
||||
if !parsedURL.IsAbs() {
|
||||
validator.Push(fmt.Errorf(errFmtOIDCClientRedirectURIAbsolute, client.ID, redirectURI))
|
||||
return
|
||||
}
|
||||
|
||||
if parsedURL.Scheme != schemeHTTPS && parsedURL.Scheme != schemeHTTP {
|
||||
validator.Push(fmt.Errorf(errFmtOIDCServerClientRedirectURI, client.ID, redirectURI, parsedURL.Scheme))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -84,13 +84,21 @@ func TestShouldRaiseErrorWhenOIDCServerClientBadValues(t *testing.T) {
|
|||
"http://abc@%two",
|
||||
},
|
||||
},
|
||||
{
|
||||
ID: "client-check-uri-abs",
|
||||
Secret: "a-secret",
|
||||
Policy: twoFactorPolicy,
|
||||
RedirectURIs: []string{
|
||||
"google.com",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
ValidateIdentityProviders(config, validator)
|
||||
|
||||
require.Len(t, validator.Errors(), 7)
|
||||
require.Len(t, validator.Errors(), 8)
|
||||
|
||||
assert.Equal(t, schema.DefaultOpenIDConnectClientConfiguration.Policy, config.OIDC.Clients[0].Policy)
|
||||
assert.EqualError(t, validator.Errors()[0], fmt.Sprintf(errFmtOIDCServerClientInvalidSecret, ""))
|
||||
|
@ -98,8 +106,9 @@ func TestShouldRaiseErrorWhenOIDCServerClientBadValues(t *testing.T) {
|
|||
assert.EqualError(t, validator.Errors()[2], fmt.Sprintf(errFmtOIDCServerClientInvalidPolicy, "a-client", "a-policy"))
|
||||
assert.EqualError(t, validator.Errors()[3], fmt.Sprintf(errFmtOIDCServerClientInvalidPolicy, "a-client", "a-policy"))
|
||||
assert.EqualError(t, validator.Errors()[4], fmt.Sprintf(errFmtOIDCServerClientRedirectURICantBeParsed, "client-check-uri-parse", "http://abc@%two", errors.New("parse \"http://abc@%two\": invalid URL escape \"%tw\"")))
|
||||
assert.EqualError(t, validator.Errors()[5], "OIDC Server has one or more clients with an empty ID")
|
||||
assert.EqualError(t, validator.Errors()[6], "OIDC Server has clients with duplicate ID's")
|
||||
assert.EqualError(t, validator.Errors()[5], fmt.Sprintf(errFmtOIDCClientRedirectURIAbsolute, "client-check-uri-abs", "google.com"))
|
||||
assert.EqualError(t, validator.Errors()[6], "OIDC Server has one or more clients with an empty ID")
|
||||
assert.EqualError(t, validator.Errors()[7], "OIDC Server has clients with duplicate ID's")
|
||||
}
|
||||
|
||||
func TestShouldRaiseErrorWhenOIDCClientConfiguredWithBadScopes(t *testing.T) {
|
||||
|
@ -239,6 +248,85 @@ func TestValidateIdentityProvidersShouldRaiseWarningOnSecurityIssue(t *testing.T
|
|||
assert.EqualError(t, validator.Warnings()[0], "SECURITY ISSUE: OIDC minimum parameter entropy is configured to an unsafe value, it should be above 8 but it's configured to 1.")
|
||||
}
|
||||
|
||||
func TestValidateIdentityProvidersShouldRaiseErrorsOnInvalidClientTypes(t *testing.T) {
|
||||
validator := schema.NewStructValidator()
|
||||
config := &schema.IdentityProvidersConfiguration{
|
||||
OIDC: &schema.OpenIDConnectConfiguration{
|
||||
HMACSecret: "hmac1",
|
||||
IssuerPrivateKey: "key2",
|
||||
Clients: []schema.OpenIDConnectClientConfiguration{
|
||||
{
|
||||
ID: "client-with-invalid-secret",
|
||||
Secret: "a-secret",
|
||||
Public: true,
|
||||
Policy: "two_factor",
|
||||
RedirectURIs: []string{
|
||||
"https://localhost",
|
||||
},
|
||||
},
|
||||
{
|
||||
ID: "client-with-bad-redirect-uri",
|
||||
Secret: "a-secret",
|
||||
Public: false,
|
||||
Policy: "two_factor",
|
||||
RedirectURIs: []string{
|
||||
oauth2InstalledApp,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
ValidateIdentityProviders(config, validator)
|
||||
|
||||
require.Len(t, validator.Errors(), 2)
|
||||
assert.Len(t, validator.Warnings(), 0)
|
||||
|
||||
assert.EqualError(t, validator.Errors()[0], fmt.Sprintf(errFmtOIDCClientPublicInvalidSecret, "client-with-invalid-secret"))
|
||||
assert.EqualError(t, validator.Errors()[1], fmt.Sprintf(errFmtOIDCClientRedirectURIPublic, "client-with-bad-redirect-uri", oauth2InstalledApp))
|
||||
}
|
||||
|
||||
func TestValidateIdentityProvidersShouldNotRaiseErrorsOnValidPublicClients(t *testing.T) {
|
||||
validator := schema.NewStructValidator()
|
||||
config := &schema.IdentityProvidersConfiguration{
|
||||
OIDC: &schema.OpenIDConnectConfiguration{
|
||||
HMACSecret: "hmac1",
|
||||
IssuerPrivateKey: "key2",
|
||||
Clients: []schema.OpenIDConnectClientConfiguration{
|
||||
{
|
||||
ID: "installed-app-client",
|
||||
Public: true,
|
||||
Policy: "two_factor",
|
||||
RedirectURIs: []string{
|
||||
oauth2InstalledApp,
|
||||
},
|
||||
},
|
||||
{
|
||||
ID: "client-with-https-scheme",
|
||||
Public: true,
|
||||
Policy: "two_factor",
|
||||
RedirectURIs: []string{
|
||||
"https://localhost:9000",
|
||||
},
|
||||
},
|
||||
{
|
||||
ID: "client-with-loopback",
|
||||
Public: true,
|
||||
Policy: "two_factor",
|
||||
RedirectURIs: []string{
|
||||
"http://127.0.0.1",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
ValidateIdentityProviders(config, validator)
|
||||
|
||||
assert.Len(t, validator.Errors(), 0)
|
||||
assert.Len(t, validator.Warnings(), 0)
|
||||
}
|
||||
|
||||
func TestValidateIdentityProvidersShouldSetDefaultValues(t *testing.T) {
|
||||
validator := schema.NewStructValidator()
|
||||
config := &schema.IdentityProvidersConfiguration{
|
||||
|
|
|
@ -12,20 +12,21 @@ import (
|
|||
// NewClient creates a new InternalClient.
|
||||
func NewClient(config schema.OpenIDConnectClientConfiguration) (client *InternalClient) {
|
||||
client = &InternalClient{
|
||||
ID: config.ID,
|
||||
Description: config.Description,
|
||||
Policy: authorization.PolicyToLevel(config.Policy),
|
||||
Secret: []byte(config.Secret),
|
||||
ID: config.ID,
|
||||
Description: config.Description,
|
||||
Secret: []byte(config.Secret),
|
||||
Public: config.Public,
|
||||
|
||||
Policy: authorization.PolicyToLevel(config.Policy),
|
||||
|
||||
Audience: config.Audience,
|
||||
Scopes: config.Scopes,
|
||||
RedirectURIs: config.RedirectURIs,
|
||||
GrantTypes: config.GrantTypes,
|
||||
ResponseTypes: config.ResponseTypes,
|
||||
Scopes: config.Scopes,
|
||||
ResponseModes: []fosite.ResponseModeType{fosite.ResponseModeDefault},
|
||||
|
||||
UserinfoSigningAlgorithm: config.UserinfoSigningAlgorithm,
|
||||
|
||||
ResponseModes: []fosite.ResponseModeType{
|
||||
fosite.ResponseModeDefault,
|
||||
},
|
||||
}
|
||||
|
||||
for _, mode := range config.ResponseModes {
|
||||
|
|
|
@ -33,21 +33,21 @@ type OpenIDConnectStore struct {
|
|||
|
||||
// InternalClient represents the client internally.
|
||||
type InternalClient struct {
|
||||
ID string `json:"id"`
|
||||
Description string `json:"-"`
|
||||
Secret []byte `json:"client_secret,omitempty"`
|
||||
RedirectURIs []string `json:"redirect_uris"`
|
||||
GrantTypes []string `json:"grant_types"`
|
||||
ResponseTypes []string `json:"response_types"`
|
||||
Scopes []string `json:"scopes"`
|
||||
Audience []string `json:"audience"`
|
||||
Public bool `json:"public"`
|
||||
ID string `json:"id"`
|
||||
Description string `json:"-"`
|
||||
Secret []byte `json:"client_secret,omitempty"`
|
||||
Public bool `json:"public"`
|
||||
|
||||
Policy authorization.Level `json:"-"`
|
||||
|
||||
Audience []string `json:"audience"`
|
||||
Scopes []string `json:"scopes"`
|
||||
RedirectURIs []string `json:"redirect_uris"`
|
||||
GrantTypes []string `json:"grant_types"`
|
||||
ResponseTypes []string `json:"response_types"`
|
||||
ResponseModes []fosite.ResponseModeType `json:"response_modes"`
|
||||
|
||||
UserinfoSigningAlgorithm string `json:"userinfo_signed_response_alg,omitempty"`
|
||||
|
||||
Policy authorization.Level `json:"-"`
|
||||
}
|
||||
|
||||
// KeyManager keeps track of all of the active/inactive rsa keys and provides them to services requiring them.
|
||||
|
|
Loading…
Reference in New Issue