feat(oidc): hashed client secrets (#4026)
Allow use of hashed OpenID Connect client secrets.pull/4211/head
parent
3aaca0604f
commit
248f1d49d4
|
@ -12,6 +12,7 @@ weight: 320
|
||||||
toc: true
|
toc: true
|
||||||
aliases:
|
aliases:
|
||||||
- /docs/contributing/commitmsg-guidelines.html
|
- /docs/contributing/commitmsg-guidelines.html
|
||||||
|
- /contributing/development/guidelines-commit-message/
|
||||||
---
|
---
|
||||||
|
|
||||||
The reasons for these conventions are as follows:
|
The reasons for these conventions are as follows:
|
||||||
|
|
|
@ -118,7 +118,7 @@ identity_providers:
|
||||||
clients:
|
clients:
|
||||||
- id: myapp
|
- id: myapp
|
||||||
description: My Application
|
description: My Application
|
||||||
secret: this_is_a_secret
|
secret: '$plaintext$this_is_a_secret'
|
||||||
sector_identifier: ''
|
sector_identifier: ''
|
||||||
public: false
|
public: false
|
||||||
authorization_policy: two_factor
|
authorization_policy: two_factor
|
||||||
|
@ -352,7 +352,9 @@ A friendly description for this client shown in the UI. This defaults to the sam
|
||||||
{{< confkey type="string" required="situational" >}}
|
{{< confkey type="string" required="situational" >}}
|
||||||
|
|
||||||
The shared secret between Authelia and the application consuming this client. This secret must match the secret
|
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.
|
configured in the application. This can either be stored in plain text (by prefixing the plain text secret with
|
||||||
|
`$plaintext$` or can be a hashed password generated with
|
||||||
|
[authelia crypto hash](../../reference/cli/authelia/authelia_hash-password.md).
|
||||||
|
|
||||||
This secret must be generated by the administrator and can be done by following the
|
This secret must be generated by the administrator and can be done by following the
|
||||||
[Generating a Random Alphanumeric String](../miscellaneous/guides.md#generating-a-random-alphanumeric-string) guide.
|
[Generating a Random Alphanumeric String](../miscellaneous/guides.md#generating-a-random-alphanumeric-string) guide.
|
||||||
|
|
|
@ -12,6 +12,7 @@ weight: 320
|
||||||
toc: true
|
toc: true
|
||||||
aliases:
|
aliases:
|
||||||
- /docs/contributing/commitmsg-guidelines.html
|
- /docs/contributing/commitmsg-guidelines.html
|
||||||
|
- /contributing/development/guidelines-commit-message/
|
||||||
---
|
---
|
||||||
|
|
||||||
The reasons for these conventions are as follows:
|
The reasons for these conventions are as follows:
|
||||||
|
|
|
@ -11,7 +11,7 @@ menu:
|
||||||
weight: 320
|
weight: 320
|
||||||
toc: true
|
toc: true
|
||||||
aliases:
|
aliases:
|
||||||
- /contributing/development/pull-request/
|
- /contributing/development/guidelines-pull-request/
|
||||||
---
|
---
|
||||||
|
|
||||||
[Pull Request] guidelines are in place in order to maintain consistency and clearly communicate our process for
|
[Pull Request] guidelines are in place in order to maintain consistency and clearly communicate our process for
|
||||||
|
|
|
@ -59,7 +59,7 @@ The following YAML configuration is an example __Authelia__
|
||||||
```yaml
|
```yaml
|
||||||
- id: guacamole
|
- id: guacamole
|
||||||
description: Apache Guacamole
|
description: Apache Guacamole
|
||||||
secret: guacamole_client_secret
|
secret: '$plaintext$guacamole_client_secret'
|
||||||
public: false
|
public: false
|
||||||
authorization_policy: two_factor
|
authorization_policy: two_factor
|
||||||
redirect_uris:
|
redirect_uris:
|
||||||
|
|
|
@ -62,7 +62,7 @@ which will operate with the above example:
|
||||||
```yaml
|
```yaml
|
||||||
- id: argocd
|
- id: argocd
|
||||||
description: Argo CD
|
description: Argo CD
|
||||||
secret: argocd_client_secret
|
secret: '$plaintext$argocd_client_secret'
|
||||||
public: false
|
public: false
|
||||||
authorization_policy: two_factor
|
authorization_policy: two_factor
|
||||||
redirect_uris:
|
redirect_uris:
|
||||||
|
|
|
@ -64,7 +64,7 @@ which will operate with the above example:
|
||||||
```yaml
|
```yaml
|
||||||
- id: bookstack
|
- id: bookstack
|
||||||
description: BookStack
|
description: BookStack
|
||||||
secret: bookstack_client_secret
|
secret: '$plaintext$bookstack_client_secret'
|
||||||
public: false
|
public: false
|
||||||
authorization_policy: two_factor
|
authorization_policy: two_factor
|
||||||
redirect_uris:
|
redirect_uris:
|
||||||
|
|
|
@ -72,7 +72,7 @@ which will operate with the above example:
|
||||||
```yaml
|
```yaml
|
||||||
- id: cloudflare
|
- id: cloudflare
|
||||||
description: Cloudflare ZeroTrust
|
description: Cloudflare ZeroTrust
|
||||||
secret: cloudflare_client_secret
|
secret: '$plaintext$cloudflare_client_secret'
|
||||||
public: false
|
public: false
|
||||||
authorization_policy: two_factor
|
authorization_policy: two_factor
|
||||||
redirect_uris:
|
redirect_uris:
|
||||||
|
|
|
@ -79,7 +79,7 @@ will operate with the above example:
|
||||||
```yaml
|
```yaml
|
||||||
- id: gitea
|
- id: gitea
|
||||||
description: Gitea
|
description: Gitea
|
||||||
secret: gitea_client_secret
|
secret: '$plaintext$gitea_client_secret'
|
||||||
public: false
|
public: false
|
||||||
authorization_policy: two_factor
|
authorization_policy: two_factor
|
||||||
redirect_uris:
|
redirect_uris:
|
||||||
|
|
|
@ -75,7 +75,7 @@ which will operate with the above example:
|
||||||
```yaml
|
```yaml
|
||||||
- id: gitlab
|
- id: gitlab
|
||||||
description: GitLab
|
description: GitLab
|
||||||
secret: gitlab_client_secret
|
secret: '$plaintext$gitlab_client_secret'
|
||||||
public: false
|
public: false
|
||||||
authorization_policy: two_factor
|
authorization_policy: two_factor
|
||||||
redirect_uris:
|
redirect_uris:
|
||||||
|
|
|
@ -93,7 +93,7 @@ which will operate with the above example:
|
||||||
```yaml
|
```yaml
|
||||||
- id: grafana
|
- id: grafana
|
||||||
description: Grafana
|
description: Grafana
|
||||||
secret: grafana_client_secret
|
secret: '$plaintext$grafana_client_secret'
|
||||||
public: false
|
public: false
|
||||||
authorization_policy: two_factor
|
authorization_policy: two_factor
|
||||||
redirect_uris:
|
redirect_uris:
|
||||||
|
|
|
@ -66,7 +66,7 @@ which will operate with the above example:
|
||||||
```yaml
|
```yaml
|
||||||
- id: harbor
|
- id: harbor
|
||||||
description: Harbor
|
description: Harbor
|
||||||
secret: harbor_client_secret
|
secret: '$plaintext$harbor_client_secret'
|
||||||
public: false
|
public: false
|
||||||
authorization_policy: two_factor
|
authorization_policy: two_factor
|
||||||
redirect_uris:
|
redirect_uris:
|
||||||
|
|
|
@ -49,7 +49,7 @@ which will operate with the above example:
|
||||||
```yaml
|
```yaml
|
||||||
- id: vault
|
- id: vault
|
||||||
description: HashiCorp Vault
|
description: HashiCorp Vault
|
||||||
secret: vault_client_secret
|
secret: '$plaintext$vault_client_secret'
|
||||||
public: false
|
public: false
|
||||||
authorization_policy: two_factor
|
authorization_policy: two_factor
|
||||||
redirect_uris:
|
redirect_uris:
|
||||||
|
|
|
@ -71,7 +71,7 @@ which will operate with the above example:
|
||||||
```yaml
|
```yaml
|
||||||
- id: komga
|
- id: komga
|
||||||
description: Komga
|
description: Komga
|
||||||
secret: komga_client_secret
|
secret: '$plaintext$komga_client_secret'
|
||||||
public: false
|
public: false
|
||||||
authorization_policy: two_factor
|
authorization_policy: two_factor
|
||||||
redirect_uris:
|
redirect_uris:
|
||||||
|
|
|
@ -87,7 +87,7 @@ which will operate with the above example:
|
||||||
```yaml
|
```yaml
|
||||||
- id: nextcloud
|
- id: nextcloud
|
||||||
description: NextCloud
|
description: NextCloud
|
||||||
secret: nextcloud_client_secret
|
secret: '$plaintext$nextcloud_client_secret'
|
||||||
public: false
|
public: false
|
||||||
authorization_policy: two_factor
|
authorization_policy: two_factor
|
||||||
redirect_uris:
|
redirect_uris:
|
||||||
|
|
|
@ -66,7 +66,7 @@ which will operate with the above example:
|
||||||
```yaml
|
```yaml
|
||||||
- id: outline
|
- id: outline
|
||||||
description: Outline
|
description: Outline
|
||||||
secret: outline_client_secret
|
secret: '$plaintext$outline_client_secret'
|
||||||
public: false
|
public: false
|
||||||
authorization_policy: two_factor
|
authorization_policy: two_factor
|
||||||
redirect_uris:
|
redirect_uris:
|
||||||
|
|
|
@ -67,7 +67,7 @@ which will operate with the above example:
|
||||||
```yaml
|
```yaml
|
||||||
- id: portainer
|
- id: portainer
|
||||||
description: Portainer
|
description: Portainer
|
||||||
secret: portainer_client_secret
|
secret: '$plaintext$portainer_client_secret'
|
||||||
public: false
|
public: false
|
||||||
authorization_policy: two_factor
|
authorization_policy: two_factor
|
||||||
redirect_uris:
|
redirect_uris:
|
||||||
|
|
|
@ -69,7 +69,7 @@ which will operate with the above example:
|
||||||
```yaml
|
```yaml
|
||||||
- id: proxmox
|
- id: proxmox
|
||||||
description: Proxmox
|
description: Proxmox
|
||||||
secret: proxmox_client_secret
|
secret: '$plaintext$proxmox_client_secret'
|
||||||
public: false
|
public: false
|
||||||
authorization_policy: two_factor
|
authorization_policy: two_factor
|
||||||
redirect_uris:
|
redirect_uris:
|
||||||
|
|
|
@ -75,7 +75,7 @@ which will operate with the above example:
|
||||||
```yaml
|
```yaml
|
||||||
- id: seafile
|
- id: seafile
|
||||||
description: Seafile
|
description: Seafile
|
||||||
secret: seafile_client_secret
|
secret: '$plaintext$seafile_client_secret'
|
||||||
public: false
|
public: false
|
||||||
authorization_policy: two_factor
|
authorization_policy: two_factor
|
||||||
redirect_uris:
|
redirect_uris:
|
||||||
|
|
|
@ -69,7 +69,7 @@ which will operate with the above example:
|
||||||
```yaml
|
```yaml
|
||||||
- id: synapse
|
- id: synapse
|
||||||
description: Synapse
|
description: Synapse
|
||||||
secret: synapse_client_secret
|
secret: '$plaintext$synapse_client_secret'
|
||||||
public: false
|
public: false
|
||||||
authorization_policy: two_factor
|
authorization_policy: two_factor
|
||||||
redirect_uris:
|
redirect_uris:
|
||||||
|
|
|
@ -2,7 +2,7 @@
|
||||||
title: "Access Control Rule Guide"
|
title: "Access Control Rule Guide"
|
||||||
description: "A reference guide on access control rule operators"
|
description: "A reference guide on access control rule operators"
|
||||||
lead: "This section contains a reference guide on access control rule operators."
|
lead: "This section contains a reference guide on access control rule operators."
|
||||||
date: 2022-09-09T15:44:23+10:00
|
date: 2022-10-19T14:09:22+11:00
|
||||||
draft: false
|
draft: false
|
||||||
images: []
|
images: []
|
||||||
menu:
|
menu:
|
||||||
|
|
|
@ -9,8 +9,10 @@ import (
|
||||||
"net/url"
|
"net/url"
|
||||||
"reflect"
|
"reflect"
|
||||||
"regexp"
|
"regexp"
|
||||||
|
"strings"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
|
"github.com/go-crypt/crypt"
|
||||||
"github.com/mitchellh/mapstructure"
|
"github.com/mitchellh/mapstructure"
|
||||||
|
|
||||||
"github.com/authelia/authelia/v4/internal/configuration/schema"
|
"github.com/authelia/authelia/v4/internal/configuration/schema"
|
||||||
|
@ -412,3 +414,53 @@ func StringToPrivateKeyHookFunc() mapstructure.DecodeHookFuncType {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// StringToPasswordDigestHookFunc decodes a string into a crypt.Digest.
|
||||||
|
func StringToPasswordDigestHookFunc(plaintext bool) mapstructure.DecodeHookFuncType {
|
||||||
|
return func(f reflect.Type, t reflect.Type, data interface{}) (value interface{}, err error) {
|
||||||
|
var ptr bool
|
||||||
|
|
||||||
|
if f.Kind() != reflect.String {
|
||||||
|
return data, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
prefixType := ""
|
||||||
|
|
||||||
|
if t.Kind() == reflect.Ptr {
|
||||||
|
ptr = true
|
||||||
|
prefixType = "*"
|
||||||
|
}
|
||||||
|
|
||||||
|
expectedType := reflect.TypeOf(schema.PasswordDigest{})
|
||||||
|
|
||||||
|
if ptr && t.Elem() != expectedType {
|
||||||
|
return data, nil
|
||||||
|
} else if !ptr && t != expectedType {
|
||||||
|
return data, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
dataStr := data.(string)
|
||||||
|
|
||||||
|
var result *schema.PasswordDigest
|
||||||
|
|
||||||
|
if !strings.HasPrefix(dataStr, "$") {
|
||||||
|
dataStr = fmt.Sprintf(crypt.StorageFormatSimple, crypt.AlgorithmPrefixPlainText, dataStr)
|
||||||
|
}
|
||||||
|
|
||||||
|
if dataStr != "" {
|
||||||
|
if result, err = schema.NewPasswordDigest(dataStr, plaintext); err != nil {
|
||||||
|
return nil, fmt.Errorf(errFmtDecodeHookCouldNotParse, dataStr, prefixType, expectedType.String(), err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if ptr {
|
||||||
|
return result, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
if result == nil {
|
||||||
|
return nil, fmt.Errorf(errFmtDecodeHookCouldNotParseEmptyValue, prefixType, expectedType.String(), errDecodeNonPtrMustHaveValue)
|
||||||
|
}
|
||||||
|
|
||||||
|
return *result, nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -64,6 +64,7 @@ func unmarshal(ko *koanf.Koanf, val *schema.StructValidator, path string, o any)
|
||||||
StringToX509CertificateHookFunc(),
|
StringToX509CertificateHookFunc(),
|
||||||
StringToX509CertificateChainHookFunc(),
|
StringToX509CertificateChainHookFunc(),
|
||||||
StringToPrivateKeyHookFunc(),
|
StringToPrivateKeyHookFunc(),
|
||||||
|
StringToPasswordDigestHookFunc(true),
|
||||||
ToTimeDurationHookFunc(),
|
ToTimeDurationHookFunc(),
|
||||||
),
|
),
|
||||||
Metadata: nil,
|
Metadata: nil,
|
||||||
|
|
|
@ -45,7 +45,7 @@ type OpenIDConnectCORSConfiguration struct {
|
||||||
type OpenIDConnectClientConfiguration struct {
|
type OpenIDConnectClientConfiguration struct {
|
||||||
ID string `koanf:"id"`
|
ID string `koanf:"id"`
|
||||||
Description string `koanf:"description"`
|
Description string `koanf:"description"`
|
||||||
Secret string `koanf:"secret"`
|
Secret *PasswordDigest `koanf:"secret"`
|
||||||
SectorIdentifier url.URL `koanf:"sector_identifier"`
|
SectorIdentifier url.URL `koanf:"sector_identifier"`
|
||||||
Public bool `koanf:"public"`
|
Public bool `koanf:"public"`
|
||||||
|
|
||||||
|
|
|
@ -13,6 +13,8 @@ import (
|
||||||
"strconv"
|
"strconv"
|
||||||
"strings"
|
"strings"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
|
"github.com/go-crypt/crypt"
|
||||||
)
|
)
|
||||||
|
|
||||||
// NewAddressFromString returns an *Address and error depending on the ability to parse the string as an Address.
|
// NewAddressFromString returns an *Address and error depending on the ability to parse the string as an Address.
|
||||||
|
@ -107,6 +109,29 @@ func (a Address) Listener() (net.Listener, error) {
|
||||||
return net.Listen(a.Scheme, a.HostPort())
|
return net.Listen(a.Scheme, a.HostPort())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// NewPasswordDigest returns a new PasswordDigest.
|
||||||
|
func NewPasswordDigest(value string, plaintext bool) (digest *PasswordDigest, err error) {
|
||||||
|
var d crypt.Digest
|
||||||
|
|
||||||
|
switch {
|
||||||
|
case plaintext:
|
||||||
|
d, err = crypt.DecodeWithPlainText(value)
|
||||||
|
default:
|
||||||
|
d, err = crypt.Decode(value)
|
||||||
|
}
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return &PasswordDigest{d}, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// PasswordDigest is a configuration type for the crypt.Digest.
|
||||||
|
type PasswordDigest struct {
|
||||||
|
crypt.Digest
|
||||||
|
}
|
||||||
|
|
||||||
// NewX509CertificateChain creates a new *X509CertificateChain from a given string, parsing each PEM block one by one.
|
// NewX509CertificateChain creates a new *X509CertificateChain from a given string, parsing each PEM block one by one.
|
||||||
func NewX509CertificateChain(in string) (chain *X509CertificateChain, err error) {
|
func NewX509CertificateChain(in string) (chain *X509CertificateChain, err error) {
|
||||||
if in == "" {
|
if in == "" {
|
||||||
|
|
|
@ -132,6 +132,6 @@ identity_providers:
|
||||||
- https://example.com
|
- https://example.com
|
||||||
clients:
|
clients:
|
||||||
- id: abc
|
- id: abc
|
||||||
secret: 123
|
secret: '123'
|
||||||
consent_mode: explicit
|
consent_mode: explicit
|
||||||
...
|
...
|
||||||
|
|
|
@ -161,11 +161,11 @@ func validateOIDCClients(config *schema.OpenIDConnectConfiguration, validator *s
|
||||||
}
|
}
|
||||||
|
|
||||||
if client.Public {
|
if client.Public {
|
||||||
if client.Secret != "" {
|
if client.Secret != nil {
|
||||||
validator.Push(fmt.Errorf(errFmtOIDCClientPublicInvalidSecret, client.ID))
|
validator.Push(fmt.Errorf(errFmtOIDCClientPublicInvalidSecret, client.ID))
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
if client.Secret == "" {
|
if client.Secret == nil {
|
||||||
validator.Push(fmt.Errorf(errFmtOIDCClientInvalidSecret, client.ID))
|
validator.Push(fmt.Errorf(errFmtOIDCClientInvalidSecret, client.ID))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -46,7 +46,7 @@ func TestShouldNotRaiseErrorWhenCORSEndpointsValid(t *testing.T) {
|
||||||
Clients: []schema.OpenIDConnectClientConfiguration{
|
Clients: []schema.OpenIDConnectClientConfiguration{
|
||||||
{
|
{
|
||||||
ID: "example",
|
ID: "example",
|
||||||
Secret: "example",
|
Secret: MustDecodeSecret("$plaintext$example"),
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
@ -69,7 +69,7 @@ func TestShouldRaiseErrorWhenCORSEndpointsNotValid(t *testing.T) {
|
||||||
Clients: []schema.OpenIDConnectClientConfiguration{
|
Clients: []schema.OpenIDConnectClientConfiguration{
|
||||||
{
|
{
|
||||||
ID: "example",
|
ID: "example",
|
||||||
Secret: "example",
|
Secret: MustDecodeSecret("$plaintext$example"),
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
@ -114,7 +114,7 @@ func TestShouldRaiseErrorWhenOIDCCORSOriginsHasInvalidValues(t *testing.T) {
|
||||||
Clients: []schema.OpenIDConnectClientConfiguration{
|
Clients: []schema.OpenIDConnectClientConfiguration{
|
||||||
{
|
{
|
||||||
ID: "myclient",
|
ID: "myclient",
|
||||||
Secret: "jk12nb3klqwmnelqkwenm",
|
Secret: MustDecodeSecret("$plaintext$jk12nb3klqwmnelqkwenm"),
|
||||||
Policy: "two_factor",
|
Policy: "two_factor",
|
||||||
RedirectURIs: []string{"https://example.com/oauth2_callback", "https://localhost:566/callback", "http://an.example.com/callback", "file://a/file"},
|
RedirectURIs: []string{"https://example.com/oauth2_callback", "https://localhost:566/callback", "http://an.example.com/callback", "file://a/file"},
|
||||||
},
|
},
|
||||||
|
@ -173,7 +173,7 @@ func TestShouldRaiseErrorWhenOIDCServerClientBadValues(t *testing.T) {
|
||||||
Clients: []schema.OpenIDConnectClientConfiguration{
|
Clients: []schema.OpenIDConnectClientConfiguration{
|
||||||
{
|
{
|
||||||
ID: "",
|
ID: "",
|
||||||
Secret: "",
|
Secret: nil,
|
||||||
Policy: "",
|
Policy: "",
|
||||||
RedirectURIs: []string{},
|
RedirectURIs: []string{},
|
||||||
},
|
},
|
||||||
|
@ -188,7 +188,7 @@ func TestShouldRaiseErrorWhenOIDCServerClientBadValues(t *testing.T) {
|
||||||
Clients: []schema.OpenIDConnectClientConfiguration{
|
Clients: []schema.OpenIDConnectClientConfiguration{
|
||||||
{
|
{
|
||||||
ID: "client-1",
|
ID: "client-1",
|
||||||
Secret: "a-secret",
|
Secret: MustDecodeSecret("$plaintext$a-secret"),
|
||||||
Policy: "a-policy",
|
Policy: "a-policy",
|
||||||
RedirectURIs: []string{
|
RedirectURIs: []string{
|
||||||
"https://google.com",
|
"https://google.com",
|
||||||
|
@ -202,13 +202,13 @@ func TestShouldRaiseErrorWhenOIDCServerClientBadValues(t *testing.T) {
|
||||||
Clients: []schema.OpenIDConnectClientConfiguration{
|
Clients: []schema.OpenIDConnectClientConfiguration{
|
||||||
{
|
{
|
||||||
ID: "client-x",
|
ID: "client-x",
|
||||||
Secret: "a-secret",
|
Secret: MustDecodeSecret("$plaintext$a-secret"),
|
||||||
Policy: policyTwoFactor,
|
Policy: policyTwoFactor,
|
||||||
RedirectURIs: []string{},
|
RedirectURIs: []string{},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
ID: "client-x",
|
ID: "client-x",
|
||||||
Secret: "a-secret",
|
Secret: MustDecodeSecret("$plaintext$a-secret"),
|
||||||
Policy: policyTwoFactor,
|
Policy: policyTwoFactor,
|
||||||
RedirectURIs: []string{},
|
RedirectURIs: []string{},
|
||||||
},
|
},
|
||||||
|
@ -220,7 +220,7 @@ func TestShouldRaiseErrorWhenOIDCServerClientBadValues(t *testing.T) {
|
||||||
Clients: []schema.OpenIDConnectClientConfiguration{
|
Clients: []schema.OpenIDConnectClientConfiguration{
|
||||||
{
|
{
|
||||||
ID: "client-check-uri-parse",
|
ID: "client-check-uri-parse",
|
||||||
Secret: "a-secret",
|
Secret: MustDecodeSecret("$plaintext$a-secret"),
|
||||||
Policy: policyTwoFactor,
|
Policy: policyTwoFactor,
|
||||||
RedirectURIs: []string{
|
RedirectURIs: []string{
|
||||||
"http://abc@%two",
|
"http://abc@%two",
|
||||||
|
@ -236,7 +236,7 @@ func TestShouldRaiseErrorWhenOIDCServerClientBadValues(t *testing.T) {
|
||||||
Clients: []schema.OpenIDConnectClientConfiguration{
|
Clients: []schema.OpenIDConnectClientConfiguration{
|
||||||
{
|
{
|
||||||
ID: "client-check-uri-abs",
|
ID: "client-check-uri-abs",
|
||||||
Secret: "a-secret",
|
Secret: MustDecodeSecret("$plaintext$a-secret"),
|
||||||
Policy: policyTwoFactor,
|
Policy: policyTwoFactor,
|
||||||
RedirectURIs: []string{
|
RedirectURIs: []string{
|
||||||
"google.com",
|
"google.com",
|
||||||
|
@ -252,7 +252,7 @@ func TestShouldRaiseErrorWhenOIDCServerClientBadValues(t *testing.T) {
|
||||||
Clients: []schema.OpenIDConnectClientConfiguration{
|
Clients: []schema.OpenIDConnectClientConfiguration{
|
||||||
{
|
{
|
||||||
ID: "client-valid-sector",
|
ID: "client-valid-sector",
|
||||||
Secret: "a-secret",
|
Secret: MustDecodeSecret("$plaintext$a-secret"),
|
||||||
Policy: policyTwoFactor,
|
Policy: policyTwoFactor,
|
||||||
RedirectURIs: []string{
|
RedirectURIs: []string{
|
||||||
"https://google.com",
|
"https://google.com",
|
||||||
|
@ -266,7 +266,7 @@ func TestShouldRaiseErrorWhenOIDCServerClientBadValues(t *testing.T) {
|
||||||
Clients: []schema.OpenIDConnectClientConfiguration{
|
Clients: []schema.OpenIDConnectClientConfiguration{
|
||||||
{
|
{
|
||||||
ID: "client-valid-sector",
|
ID: "client-valid-sector",
|
||||||
Secret: "a-secret",
|
Secret: MustDecodeSecret("$plaintext$a-secret"),
|
||||||
Policy: policyTwoFactor,
|
Policy: policyTwoFactor,
|
||||||
RedirectURIs: []string{
|
RedirectURIs: []string{
|
||||||
"https://google.com",
|
"https://google.com",
|
||||||
|
@ -280,7 +280,7 @@ func TestShouldRaiseErrorWhenOIDCServerClientBadValues(t *testing.T) {
|
||||||
Clients: []schema.OpenIDConnectClientConfiguration{
|
Clients: []schema.OpenIDConnectClientConfiguration{
|
||||||
{
|
{
|
||||||
ID: "client-invalid-sector",
|
ID: "client-invalid-sector",
|
||||||
Secret: "a-secret",
|
Secret: MustDecodeSecret("$plaintext$a-secret"),
|
||||||
Policy: policyTwoFactor,
|
Policy: policyTwoFactor,
|
||||||
RedirectURIs: []string{
|
RedirectURIs: []string{
|
||||||
"https://google.com",
|
"https://google.com",
|
||||||
|
@ -302,7 +302,7 @@ func TestShouldRaiseErrorWhenOIDCServerClientBadValues(t *testing.T) {
|
||||||
Clients: []schema.OpenIDConnectClientConfiguration{
|
Clients: []schema.OpenIDConnectClientConfiguration{
|
||||||
{
|
{
|
||||||
ID: "client-invalid-sector",
|
ID: "client-invalid-sector",
|
||||||
Secret: "a-secret",
|
Secret: MustDecodeSecret("$plaintext$a-secret"),
|
||||||
Policy: policyTwoFactor,
|
Policy: policyTwoFactor,
|
||||||
RedirectURIs: []string{
|
RedirectURIs: []string{
|
||||||
"https://google.com",
|
"https://google.com",
|
||||||
|
@ -350,7 +350,7 @@ func TestShouldRaiseErrorWhenOIDCClientConfiguredWithBadScopes(t *testing.T) {
|
||||||
Clients: []schema.OpenIDConnectClientConfiguration{
|
Clients: []schema.OpenIDConnectClientConfiguration{
|
||||||
{
|
{
|
||||||
ID: "good_id",
|
ID: "good_id",
|
||||||
Secret: "good_secret",
|
Secret: MustDecodeSecret("$plaintext$good_secret"),
|
||||||
Policy: "two_factor",
|
Policy: "two_factor",
|
||||||
Scopes: []string{"openid", "bad_scope"},
|
Scopes: []string{"openid", "bad_scope"},
|
||||||
RedirectURIs: []string{
|
RedirectURIs: []string{
|
||||||
|
@ -376,7 +376,7 @@ func TestShouldRaiseErrorWhenOIDCClientConfiguredWithBadGrantTypes(t *testing.T)
|
||||||
Clients: []schema.OpenIDConnectClientConfiguration{
|
Clients: []schema.OpenIDConnectClientConfiguration{
|
||||||
{
|
{
|
||||||
ID: "good_id",
|
ID: "good_id",
|
||||||
Secret: "good_secret",
|
Secret: MustDecodeSecret("$plaintext$good_secret"),
|
||||||
Policy: "two_factor",
|
Policy: "two_factor",
|
||||||
GrantTypes: []string{"bad_grant_type"},
|
GrantTypes: []string{"bad_grant_type"},
|
||||||
RedirectURIs: []string{
|
RedirectURIs: []string{
|
||||||
|
@ -403,7 +403,7 @@ func TestShouldNotErrorOnCertificateValid(t *testing.T) {
|
||||||
Clients: []schema.OpenIDConnectClientConfiguration{
|
Clients: []schema.OpenIDConnectClientConfiguration{
|
||||||
{
|
{
|
||||||
ID: "good_id",
|
ID: "good_id",
|
||||||
Secret: "good_secret",
|
Secret: MustDecodeSecret("$plaintext$good_secret"),
|
||||||
Policy: "two_factor",
|
Policy: "two_factor",
|
||||||
RedirectURIs: []string{
|
RedirectURIs: []string{
|
||||||
"https://google.com/callback",
|
"https://google.com/callback",
|
||||||
|
@ -429,7 +429,7 @@ func TestShouldRaiseErrorOnCertificateNotValid(t *testing.T) {
|
||||||
Clients: []schema.OpenIDConnectClientConfiguration{
|
Clients: []schema.OpenIDConnectClientConfiguration{
|
||||||
{
|
{
|
||||||
ID: "good_id",
|
ID: "good_id",
|
||||||
Secret: "good_secret",
|
Secret: MustDecodeSecret("$plaintext$good_secret"),
|
||||||
Policy: "two_factor",
|
Policy: "two_factor",
|
||||||
RedirectURIs: []string{
|
RedirectURIs: []string{
|
||||||
"https://google.com/callback",
|
"https://google.com/callback",
|
||||||
|
@ -456,7 +456,7 @@ func TestShouldRaiseErrorOnKeySizeTooSmall(t *testing.T) {
|
||||||
Clients: []schema.OpenIDConnectClientConfiguration{
|
Clients: []schema.OpenIDConnectClientConfiguration{
|
||||||
{
|
{
|
||||||
ID: "good_id",
|
ID: "good_id",
|
||||||
Secret: "good_secret",
|
Secret: MustDecodeSecret("$plaintext$good_secret"),
|
||||||
Policy: "two_factor",
|
Policy: "two_factor",
|
||||||
RedirectURIs: []string{
|
RedirectURIs: []string{
|
||||||
"https://google.com/callback",
|
"https://google.com/callback",
|
||||||
|
@ -483,7 +483,7 @@ func TestShouldRaiseErrorWhenOIDCClientConfiguredWithBadResponseModes(t *testing
|
||||||
Clients: []schema.OpenIDConnectClientConfiguration{
|
Clients: []schema.OpenIDConnectClientConfiguration{
|
||||||
{
|
{
|
||||||
ID: "good_id",
|
ID: "good_id",
|
||||||
Secret: "good_secret",
|
Secret: MustDecodeSecret("$plaintext$good_secret"),
|
||||||
Policy: "two_factor",
|
Policy: "two_factor",
|
||||||
ResponseModes: []string{"bad_responsemode"},
|
ResponseModes: []string{"bad_responsemode"},
|
||||||
RedirectURIs: []string{
|
RedirectURIs: []string{
|
||||||
|
@ -509,7 +509,7 @@ func TestShouldRaiseErrorWhenOIDCClientConfiguredWithBadUserinfoAlg(t *testing.T
|
||||||
Clients: []schema.OpenIDConnectClientConfiguration{
|
Clients: []schema.OpenIDConnectClientConfiguration{
|
||||||
{
|
{
|
||||||
ID: "good_id",
|
ID: "good_id",
|
||||||
Secret: "good_secret",
|
Secret: MustDecodeSecret("$plaintext$good_secret"),
|
||||||
Policy: "two_factor",
|
Policy: "two_factor",
|
||||||
UserinfoSigningAlgorithm: "rs256",
|
UserinfoSigningAlgorithm: "rs256",
|
||||||
RedirectURIs: []string{
|
RedirectURIs: []string{
|
||||||
|
@ -536,7 +536,7 @@ func TestValidateIdentityProvidersShouldRaiseWarningOnSecurityIssue(t *testing.T
|
||||||
Clients: []schema.OpenIDConnectClientConfiguration{
|
Clients: []schema.OpenIDConnectClientConfiguration{
|
||||||
{
|
{
|
||||||
ID: "good_id",
|
ID: "good_id",
|
||||||
Secret: "good_secret",
|
Secret: MustDecodeSecret("$plaintext$good_secret"),
|
||||||
Policy: "two_factor",
|
Policy: "two_factor",
|
||||||
RedirectURIs: []string{
|
RedirectURIs: []string{
|
||||||
"https://google.com/callback",
|
"https://google.com/callback",
|
||||||
|
@ -563,7 +563,7 @@ func TestValidateIdentityProvidersShouldRaiseErrorsOnInvalidClientTypes(t *testi
|
||||||
Clients: []schema.OpenIDConnectClientConfiguration{
|
Clients: []schema.OpenIDConnectClientConfiguration{
|
||||||
{
|
{
|
||||||
ID: "client-with-invalid-secret",
|
ID: "client-with-invalid-secret",
|
||||||
Secret: "a-secret",
|
Secret: MustDecodeSecret("$plaintext$a-secret"),
|
||||||
Public: true,
|
Public: true,
|
||||||
Policy: "two_factor",
|
Policy: "two_factor",
|
||||||
RedirectURIs: []string{
|
RedirectURIs: []string{
|
||||||
|
@ -572,7 +572,7 @@ func TestValidateIdentityProvidersShouldRaiseErrorsOnInvalidClientTypes(t *testi
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
ID: "client-with-bad-redirect-uri",
|
ID: "client-with-bad-redirect-uri",
|
||||||
Secret: "a-secret",
|
Secret: MustDecodeSecret("$plaintext$a-secret"),
|
||||||
Public: false,
|
Public: false,
|
||||||
Policy: "two_factor",
|
Policy: "two_factor",
|
||||||
RedirectURIs: []string{
|
RedirectURIs: []string{
|
||||||
|
@ -642,7 +642,7 @@ func TestValidateIdentityProvidersShouldSetDefaultValues(t *testing.T) {
|
||||||
Clients: []schema.OpenIDConnectClientConfiguration{
|
Clients: []schema.OpenIDConnectClientConfiguration{
|
||||||
{
|
{
|
||||||
ID: "a-client",
|
ID: "a-client",
|
||||||
Secret: "a-client-secret",
|
Secret: MustDecodeSecret("$plaintext$a-client-secret"),
|
||||||
RedirectURIs: []string{
|
RedirectURIs: []string{
|
||||||
"https://google.com",
|
"https://google.com",
|
||||||
},
|
},
|
||||||
|
@ -650,7 +650,7 @@ func TestValidateIdentityProvidersShouldSetDefaultValues(t *testing.T) {
|
||||||
{
|
{
|
||||||
ID: "b-client",
|
ID: "b-client",
|
||||||
Description: "Normal Description",
|
Description: "Normal Description",
|
||||||
Secret: "b-client-secret",
|
Secret: MustDecodeSecret("$plaintext$b-client-secret"),
|
||||||
Policy: policyOneFactor,
|
Policy: policyOneFactor,
|
||||||
UserinfoSigningAlgorithm: "RS256",
|
UserinfoSigningAlgorithm: "RS256",
|
||||||
RedirectURIs: []string{
|
RedirectURIs: []string{
|
||||||
|
@ -775,6 +775,14 @@ func TestValidateOIDCClientRedirectURIsSupportingPrivateUseURISchemes(t *testing
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func MustDecodeSecret(value string) *schema.PasswordDigest {
|
||||||
|
if secret, err := schema.NewPasswordDigest(value, true); err != nil {
|
||||||
|
panic(err)
|
||||||
|
} else {
|
||||||
|
return secret
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func MustParseRSAPrivateKey(data string) *rsa.PrivateKey {
|
func MustParseRSAPrivateKey(data string) *rsa.PrivateKey {
|
||||||
block, _ := pem.Decode([]byte(data))
|
block, _ := pem.Decode([]byte(data))
|
||||||
if block == nil || block.Bytes == nil || len(block.Bytes) == 0 {
|
if block == nil || block.Bytes == nil || len(block.Bytes) == 0 {
|
||||||
|
|
|
@ -14,7 +14,7 @@ func NewClient(config schema.OpenIDConnectClientConfiguration) (client *Client)
|
||||||
client = &Client{
|
client = &Client{
|
||||||
ID: config.ID,
|
ID: config.ID,
|
||||||
Description: config.Description,
|
Description: config.Description,
|
||||||
Secret: []byte(config.Secret),
|
Secret: config.Secret,
|
||||||
SectorIdentifier: config.SectorIdentifier.String(),
|
SectorIdentifier: config.SectorIdentifier.String(),
|
||||||
Public: config.Public,
|
Public: config.Public,
|
||||||
|
|
||||||
|
@ -76,7 +76,11 @@ func (c *Client) GetConsentResponseBody(consent *model.OAuth2ConsentSession) Con
|
||||||
|
|
||||||
// GetHashedSecret returns the Secret.
|
// GetHashedSecret returns the Secret.
|
||||||
func (c *Client) GetHashedSecret() []byte {
|
func (c *Client) GetHashedSecret() []byte {
|
||||||
return c.Secret
|
if c.Secret == nil {
|
||||||
|
return []byte(nil)
|
||||||
|
}
|
||||||
|
|
||||||
|
return []byte(c.Secret.Encode())
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetRedirectURIs returns the RedirectURIs.
|
// GetRedirectURIs returns the RedirectURIs.
|
||||||
|
|
|
@ -26,7 +26,7 @@ func TestNewClient(t *testing.T) {
|
||||||
ID: "myapp",
|
ID: "myapp",
|
||||||
Description: "My App",
|
Description: "My App",
|
||||||
Policy: "two_factor",
|
Policy: "two_factor",
|
||||||
Secret: "abcdef",
|
Secret: MustDecodeSecret("$plaintext$abcdef"),
|
||||||
RedirectURIs: []string{"https://google.com/callback"},
|
RedirectURIs: []string{"https://google.com/callback"},
|
||||||
Scopes: schema.DefaultOpenIDConnectClientConfiguration.Scopes,
|
Scopes: schema.DefaultOpenIDConnectClientConfiguration.Scopes,
|
||||||
ResponseTypes: schema.DefaultOpenIDConnectClientConfiguration.ResponseTypes,
|
ResponseTypes: schema.DefaultOpenIDConnectClientConfiguration.ResponseTypes,
|
||||||
|
@ -68,7 +68,7 @@ func TestIsAuthenticationLevelSufficient(t *testing.T) {
|
||||||
assert.False(t, c.IsAuthenticationLevelSufficient(authentication.TwoFactor))
|
assert.False(t, c.IsAuthenticationLevelSufficient(authentication.TwoFactor))
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestInternalClient_GetConsentResponseBody(t *testing.T) {
|
func TestClient_GetConsentResponseBody(t *testing.T) {
|
||||||
c := Client{}
|
c := Client{}
|
||||||
|
|
||||||
consentRequestBody := c.GetConsentResponseBody(nil)
|
consentRequestBody := c.GetConsentResponseBody(nil)
|
||||||
|
@ -95,7 +95,7 @@ func TestInternalClient_GetConsentResponseBody(t *testing.T) {
|
||||||
assert.Equal(t, expectedAudiences, consentRequestBody.Audience)
|
assert.Equal(t, expectedAudiences, consentRequestBody.Audience)
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestInternalClient_GetAudience(t *testing.T) {
|
func TestClient_GetAudience(t *testing.T) {
|
||||||
c := Client{}
|
c := Client{}
|
||||||
|
|
||||||
audience := c.GetAudience()
|
audience := c.GetAudience()
|
||||||
|
@ -108,7 +108,7 @@ func TestInternalClient_GetAudience(t *testing.T) {
|
||||||
assert.Equal(t, "https://example.com", audience[0])
|
assert.Equal(t, "https://example.com", audience[0])
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestInternalClient_GetScopes(t *testing.T) {
|
func TestClient_GetScopes(t *testing.T) {
|
||||||
c := Client{}
|
c := Client{}
|
||||||
|
|
||||||
scopes := c.GetScopes()
|
scopes := c.GetScopes()
|
||||||
|
@ -121,7 +121,7 @@ func TestInternalClient_GetScopes(t *testing.T) {
|
||||||
assert.Equal(t, "openid", scopes[0])
|
assert.Equal(t, "openid", scopes[0])
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestInternalClient_GetGrantTypes(t *testing.T) {
|
func TestClient_GetGrantTypes(t *testing.T) {
|
||||||
c := Client{}
|
c := Client{}
|
||||||
|
|
||||||
grantTypes := c.GetGrantTypes()
|
grantTypes := c.GetGrantTypes()
|
||||||
|
@ -135,19 +135,30 @@ func TestInternalClient_GetGrantTypes(t *testing.T) {
|
||||||
assert.Equal(t, "device_code", grantTypes[0])
|
assert.Equal(t, "device_code", grantTypes[0])
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestInternalClient_GetHashedSecret(t *testing.T) {
|
func TestClient_Hashing(t *testing.T) {
|
||||||
c := Client{}
|
c := Client{}
|
||||||
|
|
||||||
hashedSecret := c.GetHashedSecret()
|
hashedSecret := c.GetHashedSecret()
|
||||||
assert.Equal(t, []byte(nil), hashedSecret)
|
assert.Equal(t, []byte(nil), hashedSecret)
|
||||||
|
|
||||||
c.Secret = []byte("a_bad_secret")
|
c.Secret = MustDecodeSecret("$plaintext$a_bad_secret")
|
||||||
|
|
||||||
hashedSecret = c.GetHashedSecret()
|
assert.True(t, c.Secret.MatchBytes([]byte("a_bad_secret")))
|
||||||
assert.Equal(t, []byte("a_bad_secret"), hashedSecret)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestInternalClient_GetID(t *testing.T) {
|
func TestClient_GetHashedSecret(t *testing.T) {
|
||||||
|
c := Client{}
|
||||||
|
|
||||||
|
hashedSecret := c.GetHashedSecret()
|
||||||
|
assert.Equal(t, []byte(nil), hashedSecret)
|
||||||
|
|
||||||
|
c.Secret = MustDecodeSecret("$plaintext$a_bad_secret")
|
||||||
|
|
||||||
|
hashedSecret = c.GetHashedSecret()
|
||||||
|
assert.Equal(t, []byte("$plaintext$a_bad_secret"), hashedSecret)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestClient_GetID(t *testing.T) {
|
||||||
c := Client{}
|
c := Client{}
|
||||||
|
|
||||||
id := c.GetID()
|
id := c.GetID()
|
||||||
|
@ -159,7 +170,7 @@ func TestInternalClient_GetID(t *testing.T) {
|
||||||
assert.Equal(t, "myid", id)
|
assert.Equal(t, "myid", id)
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestInternalClient_GetRedirectURIs(t *testing.T) {
|
func TestClient_GetRedirectURIs(t *testing.T) {
|
||||||
c := Client{}
|
c := Client{}
|
||||||
|
|
||||||
redirectURIs := c.GetRedirectURIs()
|
redirectURIs := c.GetRedirectURIs()
|
||||||
|
@ -172,7 +183,7 @@ func TestInternalClient_GetRedirectURIs(t *testing.T) {
|
||||||
assert.Equal(t, "https://example.com/oauth2/callback", redirectURIs[0])
|
assert.Equal(t, "https://example.com/oauth2/callback", redirectURIs[0])
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestInternalClient_GetResponseModes(t *testing.T) {
|
func TestClient_GetResponseModes(t *testing.T) {
|
||||||
c := Client{}
|
c := Client{}
|
||||||
|
|
||||||
responseModes := c.GetResponseModes()
|
responseModes := c.GetResponseModes()
|
||||||
|
@ -191,7 +202,7 @@ func TestInternalClient_GetResponseModes(t *testing.T) {
|
||||||
assert.Equal(t, fosite.ResponseModeFragment, responseModes[3])
|
assert.Equal(t, fosite.ResponseModeFragment, responseModes[3])
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestInternalClient_GetResponseTypes(t *testing.T) {
|
func TestClient_GetResponseTypes(t *testing.T) {
|
||||||
c := Client{}
|
c := Client{}
|
||||||
|
|
||||||
responseTypes := c.GetResponseTypes()
|
responseTypes := c.GetResponseTypes()
|
||||||
|
@ -206,7 +217,7 @@ func TestInternalClient_GetResponseTypes(t *testing.T) {
|
||||||
assert.Equal(t, "id_token", responseTypes[1])
|
assert.Equal(t, "id_token", responseTypes[1])
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestInternalClient_IsPublic(t *testing.T) {
|
func TestClient_IsPublic(t *testing.T) {
|
||||||
c := Client{}
|
c := Client{}
|
||||||
|
|
||||||
assert.False(t, c.IsPublic())
|
assert.False(t, c.IsPublic())
|
||||||
|
@ -214,3 +225,11 @@ func TestInternalClient_IsPublic(t *testing.T) {
|
||||||
c.Public = true
|
c.Public = true
|
||||||
assert.True(t, c.IsPublic())
|
assert.True(t, c.IsPublic())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func MustDecodeSecret(value string) *schema.PasswordDigest {
|
||||||
|
if secret, err := schema.NewPasswordDigest(value, true); err != nil {
|
||||||
|
panic(err)
|
||||||
|
} else {
|
||||||
|
return secret
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -2,19 +2,26 @@ package oidc
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
"crypto/subtle"
|
|
||||||
|
"github.com/go-crypt/crypt"
|
||||||
)
|
)
|
||||||
|
|
||||||
// Compare compares the hash with the data and returns an error if they don't match.
|
// Compare compares the hash with the data and returns an error if they don't match.
|
||||||
func (h PlainTextHasher) Compare(_ context.Context, hash, data []byte) (err error) {
|
func (h AdaptiveHasher) Compare(_ context.Context, hash, data []byte) (err error) {
|
||||||
if subtle.ConstantTimeCompare(hash, data) == 0 {
|
var digest crypt.Digest
|
||||||
return errPasswordsDoNotMatch
|
|
||||||
|
if digest, err = crypt.DecodeWithPlainText(string(hash)); err != nil {
|
||||||
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if digest.MatchBytes(data) {
|
||||||
return nil
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
return errPasswordsDoNotMatch
|
||||||
}
|
}
|
||||||
|
|
||||||
// Hash creates a new hash from data.
|
// Hash creates a new hash from data.
|
||||||
func (h PlainTextHasher) Hash(_ context.Context, data []byte) (hash []byte, err error) {
|
func (h AdaptiveHasher) Hash(_ context.Context, data []byte) (hash []byte, err error) {
|
||||||
return data, nil
|
return data, nil
|
||||||
}
|
}
|
||||||
|
|
|
@ -8,9 +8,9 @@ import (
|
||||||
)
|
)
|
||||||
|
|
||||||
func TestShouldNotRaiseErrorOnEqualPasswordsPlainText(t *testing.T) {
|
func TestShouldNotRaiseErrorOnEqualPasswordsPlainText(t *testing.T) {
|
||||||
hasher := PlainTextHasher{}
|
hasher := AdaptiveHasher{}
|
||||||
|
|
||||||
a := []byte("abc")
|
a := []byte("$plaintext$abc")
|
||||||
b := []byte("abc")
|
b := []byte("abc")
|
||||||
|
|
||||||
ctx := context.Background()
|
ctx := context.Background()
|
||||||
|
@ -21,9 +21,9 @@ func TestShouldNotRaiseErrorOnEqualPasswordsPlainText(t *testing.T) {
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestShouldRaiseErrorOnNonEqualPasswordsPlainText(t *testing.T) {
|
func TestShouldRaiseErrorOnNonEqualPasswordsPlainText(t *testing.T) {
|
||||||
hasher := PlainTextHasher{}
|
hasher := AdaptiveHasher{}
|
||||||
|
|
||||||
a := []byte("abc")
|
a := []byte("$plaintext$abc")
|
||||||
b := []byte("abcd")
|
b := []byte("abcd")
|
||||||
|
|
||||||
ctx := context.Background()
|
ctx := context.Background()
|
||||||
|
@ -34,7 +34,7 @@ func TestShouldRaiseErrorOnNonEqualPasswordsPlainText(t *testing.T) {
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestShouldHashPassword(t *testing.T) {
|
func TestShouldHashPassword(t *testing.T) {
|
||||||
hasher := PlainTextHasher{}
|
hasher := AdaptiveHasher{}
|
||||||
|
|
||||||
data := []byte("abc")
|
data := []byte("abc")
|
||||||
|
|
||||||
|
|
|
@ -69,7 +69,7 @@ func NewOpenIDConnectProvider(config *schema.OpenIDConnectConfiguration, store s
|
||||||
cconfig,
|
cconfig,
|
||||||
provider.Store,
|
provider.Store,
|
||||||
strategy,
|
strategy,
|
||||||
PlainTextHasher{},
|
AdaptiveHasher{},
|
||||||
|
|
||||||
/*
|
/*
|
||||||
These are the OAuth2 and OpenIDConnect factories. Order is important (the OAuth2 factories at the top must
|
These are the OAuth2 and OpenIDConnect factories. Order is important (the OAuth2 factories at the top must
|
||||||
|
|
|
@ -31,7 +31,7 @@ func TestNewOpenIDConnectProvider_ShouldEnableOptionalDiscoveryValues(t *testing
|
||||||
Clients: []schema.OpenIDConnectClientConfiguration{
|
Clients: []schema.OpenIDConnectClientConfiguration{
|
||||||
{
|
{
|
||||||
ID: "a-client",
|
ID: "a-client",
|
||||||
Secret: "a-client-secret",
|
Secret: MustDecodeSecret("$plaintext$a-client-secret"),
|
||||||
SectorIdentifier: url.URL{Host: "google.com"},
|
SectorIdentifier: url.URL{Host: "google.com"},
|
||||||
Policy: "one_factor",
|
Policy: "one_factor",
|
||||||
RedirectURIs: []string{
|
RedirectURIs: []string{
|
||||||
|
@ -62,7 +62,7 @@ func TestOpenIDConnectProvider_NewOpenIDConnectProvider_GoodConfiguration(t *tes
|
||||||
Clients: []schema.OpenIDConnectClientConfiguration{
|
Clients: []schema.OpenIDConnectClientConfiguration{
|
||||||
{
|
{
|
||||||
ID: "a-client",
|
ID: "a-client",
|
||||||
Secret: "a-client-secret",
|
Secret: MustDecodeSecret("$plaintext$a-client-secret"),
|
||||||
Policy: "one_factor",
|
Policy: "one_factor",
|
||||||
RedirectURIs: []string{
|
RedirectURIs: []string{
|
||||||
"https://google.com",
|
"https://google.com",
|
||||||
|
@ -71,7 +71,7 @@ func TestOpenIDConnectProvider_NewOpenIDConnectProvider_GoodConfiguration(t *tes
|
||||||
{
|
{
|
||||||
ID: "b-client",
|
ID: "b-client",
|
||||||
Description: "Normal Description",
|
Description: "Normal Description",
|
||||||
Secret: "b-client-secret",
|
Secret: MustDecodeSecret("$plaintext$b-client-secret"),
|
||||||
Policy: "two_factor",
|
Policy: "two_factor",
|
||||||
RedirectURIs: []string{
|
RedirectURIs: []string{
|
||||||
"https://google.com",
|
"https://google.com",
|
||||||
|
@ -102,7 +102,7 @@ func TestOpenIDConnectProvider_NewOpenIDConnectProvider_GetOpenIDConnectWellKnow
|
||||||
Clients: []schema.OpenIDConnectClientConfiguration{
|
Clients: []schema.OpenIDConnectClientConfiguration{
|
||||||
{
|
{
|
||||||
ID: "a-client",
|
ID: "a-client",
|
||||||
Secret: "a-client-secret",
|
Secret: MustDecodeSecret("$plaintext$a-client-secret"),
|
||||||
Policy: "one_factor",
|
Policy: "one_factor",
|
||||||
RedirectURIs: []string{
|
RedirectURIs: []string{
|
||||||
"https://google.com",
|
"https://google.com",
|
||||||
|
@ -192,7 +192,7 @@ func TestOpenIDConnectProvider_NewOpenIDConnectProvider_GetOAuth2WellKnownConfig
|
||||||
Clients: []schema.OpenIDConnectClientConfiguration{
|
Clients: []schema.OpenIDConnectClientConfiguration{
|
||||||
{
|
{
|
||||||
ID: "a-client",
|
ID: "a-client",
|
||||||
Secret: "a-client-secret",
|
Secret: MustDecodeSecret("$plaintext$a-client-secret"),
|
||||||
Policy: "one_factor",
|
Policy: "one_factor",
|
||||||
RedirectURIs: []string{
|
RedirectURIs: []string{
|
||||||
"https://google.com",
|
"https://google.com",
|
||||||
|
@ -271,7 +271,7 @@ func TestOpenIDConnectProvider_NewOpenIDConnectProvider_GetOpenIDConnectWellKnow
|
||||||
Clients: []schema.OpenIDConnectClientConfiguration{
|
Clients: []schema.OpenIDConnectClientConfiguration{
|
||||||
{
|
{
|
||||||
ID: "a-client",
|
ID: "a-client",
|
||||||
Secret: "a-client-secret",
|
Secret: MustDecodeSecret("$plaintext$a-client-secret"),
|
||||||
Policy: "one_factor",
|
Policy: "one_factor",
|
||||||
RedirectURIs: []string{
|
RedirectURIs: []string{
|
||||||
"https://google.com",
|
"https://google.com",
|
||||||
|
|
|
@ -21,14 +21,14 @@ func TestOpenIDConnectStore_GetClientPolicy(t *testing.T) {
|
||||||
Description: "myclient desc",
|
Description: "myclient desc",
|
||||||
Policy: "one_factor",
|
Policy: "one_factor",
|
||||||
Scopes: []string{ScopeOpenID, ScopeProfile},
|
Scopes: []string{ScopeOpenID, ScopeProfile},
|
||||||
Secret: "mysecret",
|
Secret: MustDecodeSecret("$plaintext$mysecret"),
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
ID: "myotherclient",
|
ID: "myotherclient",
|
||||||
Description: "myclient desc",
|
Description: "myclient desc",
|
||||||
Policy: "two_factor",
|
Policy: "two_factor",
|
||||||
Scopes: []string{ScopeOpenID, ScopeProfile},
|
Scopes: []string{ScopeOpenID, ScopeProfile},
|
||||||
Secret: "mysecret",
|
Secret: MustDecodeSecret("$plaintext$mysecret"),
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
}, nil)
|
}, nil)
|
||||||
|
@ -53,7 +53,7 @@ func TestOpenIDConnectStore_GetInternalClient(t *testing.T) {
|
||||||
Description: "myclient desc",
|
Description: "myclient desc",
|
||||||
Policy: "one_factor",
|
Policy: "one_factor",
|
||||||
Scopes: []string{ScopeOpenID, ScopeProfile},
|
Scopes: []string{ScopeOpenID, ScopeProfile},
|
||||||
Secret: "mysecret",
|
Secret: MustDecodeSecret("$plaintext$mysecret"),
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
}, nil)
|
}, nil)
|
||||||
|
@ -74,7 +74,7 @@ func TestOpenIDConnectStore_GetInternalClient_ValidClient(t *testing.T) {
|
||||||
Description: "myclient desc",
|
Description: "myclient desc",
|
||||||
Policy: "one_factor",
|
Policy: "one_factor",
|
||||||
Scopes: []string{ScopeOpenID, ScopeProfile},
|
Scopes: []string{ScopeOpenID, ScopeProfile},
|
||||||
Secret: "mysecret",
|
Secret: MustDecodeSecret("$plaintext$mysecret"),
|
||||||
}
|
}
|
||||||
|
|
||||||
s := NewOpenIDConnectStore(&schema.OpenIDConnectConfiguration{
|
s := NewOpenIDConnectStore(&schema.OpenIDConnectConfiguration{
|
||||||
|
@ -93,7 +93,7 @@ func TestOpenIDConnectStore_GetInternalClient_ValidClient(t *testing.T) {
|
||||||
assert.Equal(t, client.ResponseTypes, c1.ResponseTypes)
|
assert.Equal(t, client.ResponseTypes, c1.ResponseTypes)
|
||||||
assert.Equal(t, client.RedirectURIs, c1.RedirectURIs)
|
assert.Equal(t, client.RedirectURIs, c1.RedirectURIs)
|
||||||
assert.Equal(t, client.Policy, authorization.OneFactor)
|
assert.Equal(t, client.Policy, authorization.OneFactor)
|
||||||
assert.Equal(t, client.Secret, []byte(c1.Secret))
|
assert.Equal(t, client.Secret.Encode(), "$plaintext$mysecret")
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestOpenIDConnectStore_GetInternalClient_InvalidClient(t *testing.T) {
|
func TestOpenIDConnectStore_GetInternalClient_InvalidClient(t *testing.T) {
|
||||||
|
@ -102,7 +102,7 @@ func TestOpenIDConnectStore_GetInternalClient_InvalidClient(t *testing.T) {
|
||||||
Description: "myclient desc",
|
Description: "myclient desc",
|
||||||
Policy: "one_factor",
|
Policy: "one_factor",
|
||||||
Scopes: []string{ScopeOpenID, ScopeProfile},
|
Scopes: []string{ScopeOpenID, ScopeProfile},
|
||||||
Secret: "mysecret",
|
Secret: MustDecodeSecret("$plaintext$mysecret"),
|
||||||
}
|
}
|
||||||
|
|
||||||
s := NewOpenIDConnectStore(&schema.OpenIDConnectConfiguration{
|
s := NewOpenIDConnectStore(&schema.OpenIDConnectConfiguration{
|
||||||
|
@ -126,7 +126,7 @@ func TestOpenIDConnectStore_IsValidClientID(t *testing.T) {
|
||||||
Description: "myclient desc",
|
Description: "myclient desc",
|
||||||
Policy: "one_factor",
|
Policy: "one_factor",
|
||||||
Scopes: []string{ScopeOpenID, ScopeProfile},
|
Scopes: []string{ScopeOpenID, ScopeProfile},
|
||||||
Secret: "mysecret",
|
Secret: MustDecodeSecret("$plaintext$mysecret"),
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
}, nil)
|
}, nil)
|
||||||
|
|
|
@ -4,6 +4,7 @@ import (
|
||||||
"net/url"
|
"net/url"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
|
"github.com/go-crypt/crypt"
|
||||||
"github.com/ory/fosite"
|
"github.com/ory/fosite"
|
||||||
"github.com/ory/fosite/handler/openid"
|
"github.com/ory/fosite/handler/openid"
|
||||||
"github.com/ory/fosite/token/jwt"
|
"github.com/ory/fosite/token/jwt"
|
||||||
|
@ -101,7 +102,7 @@ type Store struct {
|
||||||
type Client struct {
|
type Client struct {
|
||||||
ID string
|
ID string
|
||||||
Description string
|
Description string
|
||||||
Secret []byte
|
Secret crypt.Digest
|
||||||
SectorIdentifier string
|
SectorIdentifier string
|
||||||
Public bool
|
Public bool
|
||||||
|
|
||||||
|
@ -182,8 +183,8 @@ type KeyManager struct {
|
||||||
jwks *jose.JSONWebKeySet
|
jwks *jose.JSONWebKeySet
|
||||||
}
|
}
|
||||||
|
|
||||||
// PlainTextHasher implements the fosite.Hasher interface without an actual hashing algo.
|
// AdaptiveHasher implements the fosite.Hasher interface without an actual hashing algo.
|
||||||
type PlainTextHasher struct{}
|
type AdaptiveHasher struct{}
|
||||||
|
|
||||||
// ConsentGetResponseBody schema of the response body of the consent GET endpoint.
|
// ConsentGetResponseBody schema of the response body of the consent GET endpoint.
|
||||||
type ConsentGetResponseBody struct {
|
type ConsentGetResponseBody struct {
|
||||||
|
|
Loading…
Reference in New Issue