156 lines
		
	
	
		
			4.9 KiB
		
	
	
	
		
			Go
		
	
	
	
			
		
		
	
	
			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
 | |
| }
 |