1
0
mirror of https://github.com/oauth2-proxy/oauth2-proxy.git synced 2025-01-10 04:18:14 +02:00
oauth2-proxy/pkg/providers/oidc/provider_verifier.go
2022-02-19 15:37:56 +00:00

156 lines
4.9 KiB
Go

package oidc
import (
"context"
"errors"
"fmt"
"github.com/coreos/go-oidc/v3/oidc"
k8serrors "k8s.io/apimachinery/pkg/util/errors"
)
// ProviderVerifier represents the OIDC discovery and verification process
type ProviderVerifier interface {
DiscoveryEnabled() bool
Provider() DiscoveryProvider
Verifier() IDTokenVerifier
}
// ProviderVerifierOptions allows you to configure a ProviderVerifier
type ProviderVerifierOptions struct {
// AudienceClaim allows to define any claim that is verified against the client id
// By default `aud` claim is used for verification.
AudienceClaims []string
// ClientID is the OAuth Client ID that is defined in the provider
ClientID string
// ExtraAudiences is a list of additional audiences that are allowed
// to pass verification in addition to the client id.
ExtraAudiences []string
// IssuerURL is the OpenID Connect issuer URL
// eg: https://accounts.google.com
IssuerURL string
// JWKsURL is the OpenID Connect JWKS URL
// eg: https://www.googleapis.com/oauth2/v3/certs
JWKsURL string
// SkipDiscovery allows to skip OIDC discovery and use manually supplied Endpoints
SkipDiscovery bool
// SkipIssuerVerification skips verification of ID token issuers.
// When false, ID Token Issuers must match the OIDC discovery URL.
SkipIssuerVerification bool
}
// validate checks that the required options are present before attempting to create
// the ProviderVerifier.
func (p ProviderVerifierOptions) validate() error {
var errs []error
if p.IssuerURL == "" {
errs = append(errs, errors.New("missing required setting: issuer-url"))
}
if p.SkipDiscovery && p.JWKsURL == "" {
errs = append(errs, errors.New("missing required setting: jwks-url"))
}
if len(errs) > 0 {
return k8serrors.NewAggregate(errs)
}
return nil
}
// toVerificationOptions returns an IDTokenVerificationOptions based on the configured options.
func (p ProviderVerifierOptions) toVerificationOptions() IDTokenVerificationOptions {
return IDTokenVerificationOptions{
AudienceClaims: p.AudienceClaims,
ClientID: p.ClientID,
ExtraAudiences: p.ExtraAudiences,
}
}
// toOIDCConfig returns an oidc.Config based on the configured options.
func (p ProviderVerifierOptions) toOIDCConfig() *oidc.Config {
return &oidc.Config{
ClientID: p.ClientID,
SkipIssuerCheck: p.SkipIssuerVerification,
SkipClientIDCheck: true,
}
}
// NewProviderVerifier constructs a ProviderVerifier from the options given.
func NewProviderVerifier(ctx context.Context, opts ProviderVerifierOptions) (ProviderVerifier, error) {
if err := opts.validate(); err != nil {
return nil, fmt.Errorf("invalid provider verifier options: %v", err)
}
verifierBuilder, provider, err := getVerifierBuilder(ctx, opts)
if err != nil {
return nil, fmt.Errorf("could not get verifier builder: %v", err)
}
verifier := NewVerifier(verifierBuilder(opts.toOIDCConfig()), opts.toVerificationOptions())
if provider == nil {
// To avoid the possibility of nil pointers, always return an empty provider if discovery didn't occur.
// Users are expected to check whether discovery was enabled before using the provider.
provider = &discoveryProvider{}
}
return &providerVerifier{
discoveryEnabled: !opts.SkipDiscovery,
provider: provider,
verifier: verifier,
}, nil
}
type verifierBuilder func(*oidc.Config) *oidc.IDTokenVerifier
func getVerifierBuilder(ctx context.Context, opts ProviderVerifierOptions) (verifierBuilder, DiscoveryProvider, error) {
if opts.SkipDiscovery {
// Instead of discovering the JWKs URK, it needs to be specified in the opts already
return newVerifierBuilder(ctx, opts.IssuerURL, opts.JWKsURL), nil, nil
}
provider, err := NewProvider(ctx, opts.IssuerURL, opts.SkipIssuerVerification)
if err != nil {
return nil, nil, fmt.Errorf("error while discovery OIDC configuration: %v", err)
}
verifierBuilder := newVerifierBuilder(ctx, opts.IssuerURL, provider.Endpoints().JWKsURL)
return verifierBuilder, provider, nil
}
// newVerifierBuilder returns a function to create a IDToken verifier from an OIDC config.
func newVerifierBuilder(ctx context.Context, issuerURL, jwksURL string) verifierBuilder {
keySet := oidc.NewRemoteKeySet(ctx, jwksURL)
return func(oidcConfig *oidc.Config) *oidc.IDTokenVerifier {
return oidc.NewVerifier(issuerURL, keySet, oidcConfig)
}
}
// providerVerifier is an implementation of the ProviderVerifier interface
type providerVerifier struct {
discoveryEnabled bool
provider DiscoveryProvider
verifier IDTokenVerifier
}
// DiscoveryEnabled returns whether the provider verifier was constructed
// using the OIDC discovery process or whether it was manually discovered.
func (p *providerVerifier) DiscoveryEnabled() bool {
return p.discoveryEnabled
}
// Provider returns the OIDC discovery provider
func (p *providerVerifier) Provider() DiscoveryProvider {
return p.provider
}
// Verifier returns the ID token verifier
func (p *providerVerifier) Verifier() IDTokenVerifier {
return p.verifier
}