consul/internal/go-sso/oidcauth/auth.go

133 lines
3.8 KiB
Go
Raw Normal View History

// Copyright (c) HashiCorp, Inc.
[COMPLIANCE] License changes (#18443) * Adding explicit MPL license for sub-package This directory and its subdirectories (packages) contain files licensed with the MPLv2 `LICENSE` file in this directory and are intentionally licensed separately from the BSL `LICENSE` file at the root of this repository. * Adding explicit MPL license for sub-package This directory and its subdirectories (packages) contain files licensed with the MPLv2 `LICENSE` file in this directory and are intentionally licensed separately from the BSL `LICENSE` file at the root of this repository. * Updating the license from MPL to Business Source License Going forward, this project will be licensed under the Business Source License v1.1. Please see our blog post for more details at <Blog URL>, FAQ at www.hashicorp.com/licensing-faq, and details of the license at www.hashicorp.com/bsl. * add missing license headers * Update copyright file headers to BUSL-1.1 * Update copyright file headers to BUSL-1.1 * Update copyright file headers to BUSL-1.1 * Update copyright file headers to BUSL-1.1 * Update copyright file headers to BUSL-1.1 * Update copyright file headers to BUSL-1.1 * Update copyright file headers to BUSL-1.1 * Update copyright file headers to BUSL-1.1 * Update copyright file headers to BUSL-1.1 * Update copyright file headers to BUSL-1.1 * Update copyright file headers to BUSL-1.1 * Update copyright file headers to BUSL-1.1 * Update copyright file headers to BUSL-1.1 * Update copyright file headers to BUSL-1.1 * Update copyright file headers to BUSL-1.1 --------- Co-authored-by: hashicorp-copywrite[bot] <110428419+hashicorp-copywrite[bot]@users.noreply.github.com>
2023-08-11 13:12:13 +00:00
// SPDX-License-Identifier: BUSL-1.1
2020-05-12 01:59:29 +00:00
// package oidcauth bundles up an opinionated approach to authentication using
// both the OIDC authorization code workflow and simple JWT decoding (via
// static keys, JWKS, and OIDC discovery).
//
// NOTE: This was roughly forked from hashicorp/vault-plugin-auth-jwt
// originally at commit 825c85535e3832d254a74253a8e9ae105357778b with later
// backports of behavior in 0e93b06cecb0477d6ee004e44b04832d110096cf
package oidcauth
import (
"context"
"fmt"
"net/http"
"sync"
2024-03-22 14:54:58 +00:00
"github.com/coreos/go-oidc/v3/oidc"
2020-05-12 01:59:29 +00:00
"github.com/hashicorp/go-hclog"
"github.com/patrickmn/go-cache"
)
// Claims represents a set of claims or assertions computed about a given
// authentication exchange.
type Claims struct {
// Values is a set of key/value string claims about the authentication
// exchange.
Values map[string]string
// Lists is a set of key/value string list claims about the authentication
// exchange.
Lists map[string][]string
}
// Authenticator allows for extracting a set of claims from either an OIDC
// authorization code exchange or a bare JWT.
type Authenticator struct {
config *Config
logger hclog.Logger
// parsedJWTPubKeys is the parsed form of config.JWTValidationPubKeys
parsedJWTPubKeys []interface{}
provider *oidc.Provider
keySet oidc.KeySet
// httpClient should be configured with all relevant root CA certs and be
// reused for all OIDC or JWKS operations. This will be nil for the static
// keys JWT configuration.
httpClient *http.Client
l sync.Mutex
oidcStates *cache.Cache
// backgroundCtx is a cancellable context primarily meant to be used for
// things that may spawn background goroutines and are not tied to a
// request/response lifecycle. Use backgroundCtxCancel to cancel this.
backgroundCtx context.Context
backgroundCtxCancel context.CancelFunc
}
// New creates an authenticator suitable for use with either an OIDC
// authorization code workflow or a bare JWT workflow depending upon the value
// of the config Type.
func New(c *Config, logger hclog.Logger) (*Authenticator, error) {
if err := c.Validate(); err != nil {
return nil, err
}
var parsedJWTPubKeys []interface{}
if c.Type == TypeJWT {
for _, v := range c.JWTValidationPubKeys {
key, err := parsePublicKeyPEM([]byte(v))
if err != nil {
// This shouldn't happen as the keys are already validated in Validate().
return nil, fmt.Errorf("error parsing public key: %v", err)
}
parsedJWTPubKeys = append(parsedJWTPubKeys, key)
}
}
a := &Authenticator{
config: c,
logger: logger,
parsedJWTPubKeys: parsedJWTPubKeys,
}
a.backgroundCtx, a.backgroundCtxCancel = context.WithCancel(context.Background())
if c.Type == TypeOIDC {
a.oidcStates = cache.New(oidcStateTimeout, oidcStateCleanupInterval)
}
var err error
switch c.authType() {
case authOIDCDiscovery, authOIDCFlow:
a.httpClient, err = createHTTPClient(a.config.OIDCDiscoveryCACert)
if err != nil {
return nil, fmt.Errorf("error parsing OIDCDiscoveryCACert: %v", err)
}
provider, err := oidc.NewProvider(
contextWithHttpClient(a.backgroundCtx, a.httpClient),
a.config.OIDCDiscoveryURL,
)
if err != nil {
return nil, fmt.Errorf("error creating provider: %v", err)
}
a.provider = provider
case authJWKS:
a.httpClient, err = createHTTPClient(a.config.JWKSCACert)
if err != nil {
return nil, fmt.Errorf("error parsing JWKSCACert: %v", err)
}
a.keySet = oidc.NewRemoteKeySet(
contextWithHttpClient(a.backgroundCtx, a.httpClient),
a.config.JWKSURL,
)
}
return a, nil
}
// Stop stops any background goroutines and does cleanup.
func (a *Authenticator) Stop() {
a.l.Lock()
defer a.l.Unlock()
if a.backgroundCtxCancel != nil {
a.backgroundCtxCancel()
a.backgroundCtxCancel = nil
}
}