authelia/internal/oidc/keys.go

184 lines
4.5 KiB
Go
Raw Normal View History

package oidc
import (
"context"
"crypto"
"crypto/rsa"
"errors"
"fmt"
"strings"
"github.com/ory/fosite/token/jwt"
"gopkg.in/square/go-jose.v2"
"github.com/authelia/authelia/v4/internal/configuration/schema"
)
// NewKeyManagerWithConfiguration when provided a schema.OpenIDConnectConfiguration creates a new KeyManager and adds an
// initial key to the manager.
func NewKeyManagerWithConfiguration(config *schema.OpenIDConnectConfiguration) (manager *KeyManager, err error) {
manager = NewKeyManager()
if _, err = manager.AddActiveJWK(config.IssuerCertificateChain, config.IssuerPrivateKey); err != nil {
return nil, err
}
return manager, nil
}
// NewKeyManager creates a new empty KeyManager.
func NewKeyManager() (manager *KeyManager) {
return &KeyManager{
jwks: &jose.JSONWebKeySet{},
}
}
// Strategy returns the fosite jwt.JWTStrategy.
refactor: merge master and fix missing rebinds (#4404) * build(deps): update module github.com/jackc/pgx/v5 to v5.1.0 (#4365) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> * docs: add smkent as a contributor for code, design, and ideas (#4367) * update README.md * update .all-contributorsrc Co-authored-by: allcontributors[bot] <46447321+allcontributors[bot]@users.noreply.github.com> * build(deps): update module github.com/ory/fosite to v0.43.0 (#4269) This updates fosite and refactors our usage out of compose. * refactor(cmd): restrict bootstrap pnpm tasks to dev environment (#4370) * build(deps): update alpine docker tag to v3.16.3 (#4362) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> * build(deps): update module github.com/ory/x to v0.0.514 (#4368) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> * refactor: sql formatting (#4371) * refactor: sql spacing * refactor editor config * docs: clarify cloudflare docs (#4373) * build(deps): update dependency @types/react-dom to v18.0.9 (#4379) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> * build(deps): update typescript-eslint monorepo to v5.43.0 (#4380) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> * build(deps): update dependency @types/jest to v29.2.3 (#4381) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> * build(deps): update dependency esbuild to v0.15.14 (#4383) * build(deps): update material-ui monorepo to v5.10.14 (#4385) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> * build(deps): update dependency vite to v3.2.4 (#4386) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> * build(deps): update font awesome to v6.2.1 (#4389) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> * build(deps): update dependency typescript to v4.9.3 (#4390) * docs: adjust issue templates (#4391) * docs: adjust issue templates * docs: adjust wording * build(deps): update dependency jest-watch-typeahead to v2.2.1 (#4392) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> * build(deps): update dependency i18next to v22.0.6 (#4395) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> * build(deps): update github.com/duosecurity/duo_api_golang digest to 091daa0 (#4396) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> * build(deps): update traefik docker tag to v2.9.5 (#4398) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> * build(deps): update module github.com/jackc/pgx/v5 to v5.1.1 (#4400) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> * build(deps): update mariadb docker tag to v10.10.2 (#4399) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> * build(deps): update dependency eslint-plugin-react to v7.31.11 (#4401) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> * build(deps): update dependency eslint to v8.28.0 (#4402) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> * fix(storage): schema inconsistency (#4262) * fix: missing pg rebinds * fix: refactoring issues * fix: refactoring issues Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> Co-authored-by: allcontributors[bot] <46447321+allcontributors[bot]@users.noreply.github.com> Co-authored-by: Amir Zarrinkafsh <nightah@me.com>
2022-11-19 06:42:03 +00:00
func (m *KeyManager) Strategy() (strategy jwt.Signer) {
if m.jwk == nil {
return nil
}
return m.jwk.Strategy()
}
// GetKeySet returns the joseJSONWebKeySet containing the rsa.PublicKey types.
func (m *KeyManager) GetKeySet() (jwks *jose.JSONWebKeySet) {
return m.jwks
}
// GetActiveJWK obtains the currently active jose.JSONWebKey.
func (m *KeyManager) GetActiveJWK() (jwk *jose.JSONWebKey, err error) {
if m.jwks == nil || m.jwk == nil {
return nil, errors.New("could not obtain the active JWK from an improperly configured key manager")
}
jwks := m.jwks.Key(m.jwk.id)
if len(jwks) == 1 {
return &jwks[0], nil
}
if len(jwks) == 0 {
return nil, errors.New("could not find a key with the active key id")
}
return nil, errors.New("multiple keys with the same key id")
}
// GetActiveKeyID returns the key id of the currently active key.
func (m *KeyManager) GetActiveKeyID() (keyID string) {
if m.jwk == nil {
return ""
}
return m.jwk.id
}
// GetActivePrivateKey returns the rsa.PrivateKey of the currently active key.
func (m *KeyManager) GetActivePrivateKey() (key *rsa.PrivateKey, err error) {
if m.jwk == nil {
return nil, errors.New("failed to retrieve active private key")
}
return m.jwk.key, nil
}
// AddActiveJWK is used to add a cert and key pair.
func (m *KeyManager) AddActiveJWK(chain schema.X509CertificateChain, key *rsa.PrivateKey) (jwk *JWK, err error) {
// TODO: Add a mutex when implementing key rotation to be utilized here and in methods which retrieve the JWK or JWKS.
if m.jwk, err = NewJWK(chain, key); err != nil {
return nil, err
}
m.jwks.Keys = append(m.jwks.Keys, *m.jwk.JSONWebKey())
return m.jwk, nil
}
// JWTStrategy is a decorator struct for the fosite jwt.JWTStrategy.
type JWTStrategy struct {
refactor: merge master and fix missing rebinds (#4404) * build(deps): update module github.com/jackc/pgx/v5 to v5.1.0 (#4365) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> * docs: add smkent as a contributor for code, design, and ideas (#4367) * update README.md * update .all-contributorsrc Co-authored-by: allcontributors[bot] <46447321+allcontributors[bot]@users.noreply.github.com> * build(deps): update module github.com/ory/fosite to v0.43.0 (#4269) This updates fosite and refactors our usage out of compose. * refactor(cmd): restrict bootstrap pnpm tasks to dev environment (#4370) * build(deps): update alpine docker tag to v3.16.3 (#4362) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> * build(deps): update module github.com/ory/x to v0.0.514 (#4368) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> * refactor: sql formatting (#4371) * refactor: sql spacing * refactor editor config * docs: clarify cloudflare docs (#4373) * build(deps): update dependency @types/react-dom to v18.0.9 (#4379) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> * build(deps): update typescript-eslint monorepo to v5.43.0 (#4380) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> * build(deps): update dependency @types/jest to v29.2.3 (#4381) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> * build(deps): update dependency esbuild to v0.15.14 (#4383) * build(deps): update material-ui monorepo to v5.10.14 (#4385) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> * build(deps): update dependency vite to v3.2.4 (#4386) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> * build(deps): update font awesome to v6.2.1 (#4389) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> * build(deps): update dependency typescript to v4.9.3 (#4390) * docs: adjust issue templates (#4391) * docs: adjust issue templates * docs: adjust wording * build(deps): update dependency jest-watch-typeahead to v2.2.1 (#4392) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> * build(deps): update dependency i18next to v22.0.6 (#4395) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> * build(deps): update github.com/duosecurity/duo_api_golang digest to 091daa0 (#4396) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> * build(deps): update traefik docker tag to v2.9.5 (#4398) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> * build(deps): update module github.com/jackc/pgx/v5 to v5.1.1 (#4400) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> * build(deps): update mariadb docker tag to v10.10.2 (#4399) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> * build(deps): update dependency eslint-plugin-react to v7.31.11 (#4401) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> * build(deps): update dependency eslint to v8.28.0 (#4402) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> * fix(storage): schema inconsistency (#4262) * fix: missing pg rebinds * fix: refactoring issues * fix: refactoring issues Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> Co-authored-by: allcontributors[bot] <46447321+allcontributors[bot]@users.noreply.github.com> Co-authored-by: Amir Zarrinkafsh <nightah@me.com>
2022-11-19 06:42:03 +00:00
jwt.Signer
id string
}
// KeyID returns the key id.
func (s *JWTStrategy) KeyID() (id string) {
return s.id
}
// GetPublicKeyID is a decorator func for the underlying fosite RS256JWTStrategy.
func (s *JWTStrategy) GetPublicKeyID(_ context.Context) (string, error) {
return s.id, nil
}
// NewJWK creates a new JWK.
func NewJWK(chain schema.X509CertificateChain, key *rsa.PrivateKey) (j *JWK, err error) {
if key == nil {
return nil, fmt.Errorf("JWK is not properly initialized: missing key")
}
j = &JWK{
key: key,
chain: chain,
}
jwk := &jose.JSONWebKey{
Algorithm: SigningAlgorithmRSAWithSHA256,
Use: "sig",
Key: &key.PublicKey,
}
var thumbprint []byte
if thumbprint, err = jwk.Thumbprint(crypto.SHA1); err != nil {
return nil, fmt.Errorf("failed to calculate SHA1 thumbprint for certificate: %w", err)
}
j.id = strings.ToLower(fmt.Sprintf("%x", thumbprint))
if len(j.id) >= 7 {
j.id = j.id[:6]
}
if len(j.id) >= 7 {
j.id = j.id[:6]
}
return j, nil
}
// JWK is a utility wrapper for JSON Web Key's.
type JWK struct {
id string
key *rsa.PrivateKey
chain schema.X509CertificateChain
}
// Strategy returns the relevant jwt.JWTStrategy for this JWT.
refactor: merge master and fix missing rebinds (#4404) * build(deps): update module github.com/jackc/pgx/v5 to v5.1.0 (#4365) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> * docs: add smkent as a contributor for code, design, and ideas (#4367) * update README.md * update .all-contributorsrc Co-authored-by: allcontributors[bot] <46447321+allcontributors[bot]@users.noreply.github.com> * build(deps): update module github.com/ory/fosite to v0.43.0 (#4269) This updates fosite and refactors our usage out of compose. * refactor(cmd): restrict bootstrap pnpm tasks to dev environment (#4370) * build(deps): update alpine docker tag to v3.16.3 (#4362) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> * build(deps): update module github.com/ory/x to v0.0.514 (#4368) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> * refactor: sql formatting (#4371) * refactor: sql spacing * refactor editor config * docs: clarify cloudflare docs (#4373) * build(deps): update dependency @types/react-dom to v18.0.9 (#4379) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> * build(deps): update typescript-eslint monorepo to v5.43.0 (#4380) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> * build(deps): update dependency @types/jest to v29.2.3 (#4381) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> * build(deps): update dependency esbuild to v0.15.14 (#4383) * build(deps): update material-ui monorepo to v5.10.14 (#4385) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> * build(deps): update dependency vite to v3.2.4 (#4386) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> * build(deps): update font awesome to v6.2.1 (#4389) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> * build(deps): update dependency typescript to v4.9.3 (#4390) * docs: adjust issue templates (#4391) * docs: adjust issue templates * docs: adjust wording * build(deps): update dependency jest-watch-typeahead to v2.2.1 (#4392) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> * build(deps): update dependency i18next to v22.0.6 (#4395) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> * build(deps): update github.com/duosecurity/duo_api_golang digest to 091daa0 (#4396) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> * build(deps): update traefik docker tag to v2.9.5 (#4398) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> * build(deps): update module github.com/jackc/pgx/v5 to v5.1.1 (#4400) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> * build(deps): update mariadb docker tag to v10.10.2 (#4399) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> * build(deps): update dependency eslint-plugin-react to v7.31.11 (#4401) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> * build(deps): update dependency eslint to v8.28.0 (#4402) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> * fix(storage): schema inconsistency (#4262) * fix: missing pg rebinds * fix: refactoring issues * fix: refactoring issues Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> Co-authored-by: allcontributors[bot] <46447321+allcontributors[bot]@users.noreply.github.com> Co-authored-by: Amir Zarrinkafsh <nightah@me.com>
2022-11-19 06:42:03 +00:00
func (j *JWK) Strategy() (strategy jwt.Signer) {
return &JWTStrategy{id: j.id, Signer: &jwt.DefaultSigner{GetPrivateKey: j.GetPrivateKey}}
}
func (j *JWK) GetPrivateKey(ctx context.Context) (key any, err error) {
return j.key, nil
}
// JSONWebKey returns the relevant *jose.JSONWebKey for this JWT.
func (j *JWK) JSONWebKey() (jwk *jose.JSONWebKey) {
jwk = &jose.JSONWebKey{
Key: &j.key.PublicKey,
KeyID: j.id,
Algorithm: "RS256",
Use: "sig",
Certificates: j.chain.Certificates(),
}
if len(jwk.Certificates) != 0 {
jwk.CertificateThumbprintSHA1, jwk.CertificateThumbprintSHA256 = j.chain.Thumbprint(crypto.SHA1), j.chain.Thumbprint(crypto.SHA256)
}
return jwk
}