k8s.io/apiserver@v0.31.1/plugin/pkg/authenticator/token/oidc/oidc.go (about)

     1  /*
     2  Copyright 2015 The Kubernetes Authors.
     3  
     4  Licensed under the Apache License, Version 2.0 (the "License");
     5  you may not use this file except in compliance with the License.
     6  You may obtain a copy of the License at
     7  
     8      http://www.apache.org/licenses/LICENSE-2.0
     9  
    10  Unless required by applicable law or agreed to in writing, software
    11  distributed under the License is distributed on an "AS IS" BASIS,
    12  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    13  See the License for the specific language governing permissions and
    14  limitations under the License.
    15  */
    16  
    17  /*
    18  oidc implements the authenticator.Token interface using the OpenID Connect protocol.
    19  
    20  	config := oidc.Options{
    21  		IssuerURL:     "https://accounts.google.com",
    22  		ClientID:      os.Getenv("GOOGLE_CLIENT_ID"),
    23  		UsernameClaim: "email",
    24  	}
    25  	tokenAuthenticator, err := oidc.New(config)
    26  */
    27  package oidc
    28  
    29  import (
    30  	"context"
    31  	"crypto/tls"
    32  	"crypto/x509"
    33  	"encoding/base64"
    34  	"encoding/json"
    35  	"fmt"
    36  	"io/ioutil"
    37  	"net/http"
    38  	"net/url"
    39  	"reflect"
    40  	"strings"
    41  	"sync"
    42  	"sync/atomic"
    43  	"time"
    44  
    45  	"github.com/coreos/go-oidc"
    46  	celgo "github.com/google/cel-go/cel"
    47  	"github.com/google/cel-go/common/types/ref"
    48  
    49  	authenticationv1 "k8s.io/api/authentication/v1"
    50  	"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
    51  	"k8s.io/apimachinery/pkg/runtime"
    52  	"k8s.io/apimachinery/pkg/util/net"
    53  	"k8s.io/apimachinery/pkg/util/sets"
    54  	"k8s.io/apimachinery/pkg/util/wait"
    55  	"k8s.io/apiserver/pkg/apis/apiserver"
    56  	apiservervalidation "k8s.io/apiserver/pkg/apis/apiserver/validation"
    57  	"k8s.io/apiserver/pkg/authentication/authenticator"
    58  	authenticationcel "k8s.io/apiserver/pkg/authentication/cel"
    59  	"k8s.io/apiserver/pkg/authentication/user"
    60  	certutil "k8s.io/client-go/util/cert"
    61  	"k8s.io/klog/v2"
    62  )
    63  
    64  var (
    65  	// synchronizeTokenIDVerifierForTest should be set to true to force a
    66  	// wait until the token ID verifiers are ready.
    67  	synchronizeTokenIDVerifierForTest = false
    68  )
    69  
    70  const (
    71  	wellKnownEndpointPath = "/.well-known/openid-configuration"
    72  )
    73  
    74  type Options struct {
    75  	// JWTAuthenticator is the authenticator that will be used to verify the JWT.
    76  	JWTAuthenticator apiserver.JWTAuthenticator
    77  
    78  	// Optional KeySet to allow for synchronous initialization instead of fetching from the remote issuer.
    79  	// Mutually exclusive with JWTAuthenticator.Issuer.DiscoveryURL.
    80  	KeySet oidc.KeySet
    81  
    82  	// PEM encoded root certificate contents of the provider.  Mutually exclusive with Client.
    83  	CAContentProvider CAContentProvider
    84  
    85  	// Optional http.Client used to make all requests to the remote issuer.  Mutually exclusive with CAContentProvider.
    86  	Client *http.Client
    87  
    88  	// SupportedSigningAlgs sets the accepted set of JOSE signing algorithms that
    89  	// can be used by the provider to sign tokens.
    90  	//
    91  	// https://tools.ietf.org/html/rfc7518#section-3.1
    92  	//
    93  	// This value defaults to RS256, the value recommended by the OpenID Connect
    94  	// spec:
    95  	//
    96  	// https://openid.net/specs/openid-connect-core-1_0.html#IDTokenValidation
    97  	SupportedSigningAlgs []string
    98  
    99  	DisallowedIssuers []string
   100  
   101  	// now is used for testing. It defaults to time.Now.
   102  	now func() time.Time
   103  }
   104  
   105  // Subset of dynamiccertificates.CAContentProvider that can be used to dynamically load root CAs.
   106  type CAContentProvider interface {
   107  	CurrentCABundleContent() []byte
   108  }
   109  
   110  // initVerifier creates a new ID token verifier for the given configuration and issuer URL.  On success, calls setVerifier with the
   111  // resulting verifier.
   112  func initVerifier(ctx context.Context, config *oidc.Config, iss string, audiences sets.Set[string]) (*idTokenVerifier, error) {
   113  	provider, err := oidc.NewProvider(ctx, iss)
   114  	if err != nil {
   115  		return nil, fmt.Errorf("init verifier failed: %v", err)
   116  	}
   117  	return &idTokenVerifier{provider.Verifier(config), audiences}, nil
   118  }
   119  
   120  // asyncIDTokenVerifier is an ID token verifier that allows async initialization
   121  // of the issuer check.  Must be passed by reference as it wraps sync.Mutex.
   122  type asyncIDTokenVerifier struct {
   123  	m sync.Mutex
   124  
   125  	// v is the ID token verifier initialized asynchronously.  It remains nil
   126  	// up until it is eventually initialized.
   127  	// Guarded by m
   128  	v *idTokenVerifier
   129  }
   130  
   131  // newAsyncIDTokenVerifier creates a new asynchronous token verifier.  The
   132  // verifier is available immediately, but may remain uninitialized for some time
   133  // after creation.
   134  func newAsyncIDTokenVerifier(ctx context.Context, c *oidc.Config, iss string, audiences sets.Set[string]) *asyncIDTokenVerifier {
   135  	t := &asyncIDTokenVerifier{}
   136  
   137  	sync := make(chan struct{})
   138  	// Polls indefinitely in an attempt to initialize the distributed claims
   139  	// verifier, or until context canceled.
   140  	initFn := func(ctx context.Context) (done bool, err error) {
   141  		klog.V(4).Infof("oidc authenticator: attempting init: iss=%v", iss)
   142  		v, err := initVerifier(ctx, c, iss, audiences)
   143  		if err != nil {
   144  			klog.Errorf("oidc authenticator: async token verifier for issuer: %q: %v", iss, err)
   145  			return false, nil
   146  		}
   147  		t.m.Lock()
   148  		defer t.m.Unlock()
   149  		t.v = v
   150  		close(sync)
   151  		return true, nil
   152  	}
   153  
   154  	go func() {
   155  		_ = wait.PollUntilContextCancel(ctx, 10*time.Second, true, initFn)
   156  	}()
   157  
   158  	if synchronizeTokenIDVerifierForTest {
   159  		select {
   160  		case <-sync:
   161  		case <-ctx.Done():
   162  		}
   163  	}
   164  
   165  	return t
   166  }
   167  
   168  // verifier returns the underlying ID token verifier, or nil if one is not yet initialized.
   169  func (a *asyncIDTokenVerifier) verifier() *idTokenVerifier {
   170  	a.m.Lock()
   171  	defer a.m.Unlock()
   172  	return a.v
   173  }
   174  
   175  type jwtAuthenticator struct {
   176  	jwtAuthenticator apiserver.JWTAuthenticator
   177  
   178  	// Contains an *oidc.IDTokenVerifier. Do not access directly use the
   179  	// idTokenVerifier method.
   180  	verifier atomic.Value
   181  
   182  	// resolver is used to resolve distributed claims.
   183  	resolver *claimResolver
   184  
   185  	// celMapper contains the compiled CEL expressions for
   186  	// username, groups, uid, extra, claimMapping and claimValidation
   187  	celMapper authenticationcel.CELMapper
   188  
   189  	// requiredClaims contains the list of claims that must be present in the token.
   190  	requiredClaims map[string]string
   191  
   192  	healthCheck atomic.Pointer[errorHolder]
   193  }
   194  
   195  // idTokenVerifier is a wrapper around oidc.IDTokenVerifier. It uses the oidc.IDTokenVerifier
   196  // to verify the raw ID token and then performs audience validation locally.
   197  type idTokenVerifier struct {
   198  	verifier  *oidc.IDTokenVerifier
   199  	audiences sets.Set[string]
   200  }
   201  
   202  func (a *jwtAuthenticator) setVerifier(v *idTokenVerifier) {
   203  	a.verifier.Store(v)
   204  	if v != nil {
   205  		// this must be done after the verifier has been stored so that a nil error
   206  		// from HealthCheck always means that the authenticator is ready for use.
   207  		a.healthCheck.Store(&errorHolder{})
   208  	}
   209  }
   210  
   211  func (a *jwtAuthenticator) idTokenVerifier() (*idTokenVerifier, bool) {
   212  	if v := a.verifier.Load(); v != nil {
   213  		return v.(*idTokenVerifier), true
   214  	}
   215  	return nil, false
   216  }
   217  
   218  func AllValidSigningAlgorithms() []string {
   219  	return sets.List(sets.KeySet(allowedSigningAlgs))
   220  }
   221  
   222  // allowlist of signing algorithms to ensure users don't mistakenly pass something goofy.
   223  var allowedSigningAlgs = map[string]bool{
   224  	oidc.RS256: true,
   225  	oidc.RS384: true,
   226  	oidc.RS512: true,
   227  	oidc.ES256: true,
   228  	oidc.ES384: true,
   229  	oidc.ES512: true,
   230  	oidc.PS256: true,
   231  	oidc.PS384: true,
   232  	oidc.PS512: true,
   233  }
   234  
   235  type AuthenticatorTokenWithHealthCheck interface {
   236  	authenticator.Token
   237  	HealthCheck() error
   238  }
   239  
   240  // New returns an authenticator that is asynchronously initialized when opts.KeySet is not set.
   241  // The input lifecycleCtx is used to:
   242  // - terminate background goroutines that are needed for asynchronous initialization
   243  // - as the base context for any requests that are made (i.e. for key fetching)
   244  // Thus, once the lifecycleCtx is canceled, the authenticator must not be used.
   245  // A caller may check if the authenticator is healthy by calling the HealthCheck method.
   246  func New(lifecycleCtx context.Context, opts Options) (AuthenticatorTokenWithHealthCheck, error) {
   247  	celMapper, fieldErr := apiservervalidation.CompileAndValidateJWTAuthenticator(opts.JWTAuthenticator, opts.DisallowedIssuers)
   248  	if err := fieldErr.ToAggregate(); err != nil {
   249  		return nil, err
   250  	}
   251  
   252  	supportedSigningAlgs := opts.SupportedSigningAlgs
   253  	if len(supportedSigningAlgs) == 0 {
   254  		// RS256 is the default recommended by OpenID Connect and an 'alg' value
   255  		// providers are required to implement.
   256  		supportedSigningAlgs = []string{oidc.RS256}
   257  	}
   258  	for _, alg := range supportedSigningAlgs {
   259  		if !allowedSigningAlgs[alg] {
   260  			return nil, fmt.Errorf("oidc: unsupported signing alg: %q", alg)
   261  		}
   262  	}
   263  
   264  	if opts.Client != nil && opts.CAContentProvider != nil {
   265  		return nil, fmt.Errorf("oidc: Client and CAContentProvider are mutually exclusive")
   266  	}
   267  
   268  	client := opts.Client
   269  
   270  	if client == nil {
   271  		var roots *x509.CertPool
   272  		var err error
   273  		if opts.CAContentProvider != nil {
   274  			// TODO(enj): make this reload CA data dynamically
   275  			roots, err = certutil.NewPoolFromBytes(opts.CAContentProvider.CurrentCABundleContent())
   276  			if err != nil {
   277  				return nil, fmt.Errorf("Failed to read the CA contents: %v", err)
   278  			}
   279  		} else {
   280  			klog.Info("OIDC: No x509 certificates provided, will use host's root CA set")
   281  		}
   282  
   283  		// Copied from http.DefaultTransport.
   284  		tr := net.SetTransportDefaults(&http.Transport{
   285  			// According to golang's doc, if RootCAs is nil,
   286  			// TLS uses the host's root CA set.
   287  			TLSClientConfig: &tls.Config{RootCAs: roots},
   288  		})
   289  
   290  		client = &http.Client{Transport: tr, Timeout: 30 * time.Second}
   291  	}
   292  
   293  	// If the discovery URL is set in authentication configuration, we set up a
   294  	// roundTripper to rewrite the {url}/.well-known/openid-configuration to
   295  	// the discovery URL. This is useful for self-hosted providers, for example,
   296  	// providers that run on top of Kubernetes itself.
   297  	if len(opts.JWTAuthenticator.Issuer.DiscoveryURL) > 0 {
   298  		if opts.KeySet != nil {
   299  			return nil, fmt.Errorf("oidc: KeySet and DiscoveryURL are mutually exclusive")
   300  		}
   301  
   302  		discoveryURL, err := url.Parse(opts.JWTAuthenticator.Issuer.DiscoveryURL)
   303  		if err != nil {
   304  			return nil, fmt.Errorf("oidc: invalid discovery URL: %w", err)
   305  		}
   306  
   307  		clientWithDiscoveryURL := *client
   308  		baseTransport := clientWithDiscoveryURL.Transport
   309  		if baseTransport == nil {
   310  			baseTransport = http.DefaultTransport
   311  		}
   312  		// This matches the url construction in oidc.NewProvider as of go-oidc v2.2.1.
   313  		// xref: https://github.com/coreos/go-oidc/blob/40cd342c4a2076195294612a834d11df23c1b25a/oidc.go#L114
   314  		urlToRewrite := strings.TrimSuffix(opts.JWTAuthenticator.Issuer.URL, "/") + wellKnownEndpointPath
   315  		clientWithDiscoveryURL.Transport = &discoveryURLRoundTripper{baseTransport, discoveryURL, urlToRewrite}
   316  		client = &clientWithDiscoveryURL
   317  	}
   318  
   319  	lifecycleCtx = oidc.ClientContext(lifecycleCtx, client)
   320  
   321  	now := opts.now
   322  	if now == nil {
   323  		now = time.Now
   324  	}
   325  
   326  	audiences := sets.New[string](opts.JWTAuthenticator.Issuer.Audiences...)
   327  	verifierConfig := &oidc.Config{
   328  		ClientID:             opts.JWTAuthenticator.Issuer.Audiences[0],
   329  		SupportedSigningAlgs: supportedSigningAlgs,
   330  		Now:                  now,
   331  	}
   332  	if audiences.Len() > 1 {
   333  		verifierConfig.ClientID = ""
   334  		// SkipClientIDCheck is set to true because we want to support multiple audiences
   335  		// in the authentication configuration.
   336  		// The go oidc library does not support validating
   337  		// multiple audiences, so we have to skip the client ID check and do it ourselves.
   338  		// xref: https://github.com/coreos/go-oidc/issues/397
   339  		verifierConfig.SkipClientIDCheck = true
   340  	}
   341  
   342  	var resolver *claimResolver
   343  	groupsClaim := opts.JWTAuthenticator.ClaimMappings.Groups.Claim
   344  	if groupsClaim != "" {
   345  		resolver = newClaimResolver(lifecycleCtx, groupsClaim, client, verifierConfig, audiences)
   346  	}
   347  
   348  	requiredClaims := make(map[string]string)
   349  	for _, claimValidationRule := range opts.JWTAuthenticator.ClaimValidationRules {
   350  		if len(claimValidationRule.Claim) > 0 {
   351  			requiredClaims[claimValidationRule.Claim] = claimValidationRule.RequiredValue
   352  		}
   353  	}
   354  
   355  	authn := &jwtAuthenticator{
   356  		jwtAuthenticator: opts.JWTAuthenticator,
   357  		resolver:         resolver,
   358  		celMapper:        celMapper,
   359  		requiredClaims:   requiredClaims,
   360  	}
   361  	authn.healthCheck.Store(&errorHolder{
   362  		err: fmt.Errorf("oidc: authenticator for issuer %q is not initialized", authn.jwtAuthenticator.Issuer.URL),
   363  	})
   364  
   365  	issuerURL := opts.JWTAuthenticator.Issuer.URL
   366  	if opts.KeySet != nil {
   367  		// We already have a key set, synchronously initialize the verifier.
   368  		authn.setVerifier(&idTokenVerifier{
   369  			oidc.NewVerifier(issuerURL, opts.KeySet, verifierConfig),
   370  			audiences,
   371  		})
   372  	} else {
   373  		// Asynchronously attempt to initialize the authenticator. This enables
   374  		// self-hosted providers, providers that run on top of Kubernetes itself.
   375  		go func() {
   376  			// we ignore any errors from polling because they can only come from the context being canceled
   377  			_ = wait.PollUntilContextCancel(lifecycleCtx, 10*time.Second, true, func(_ context.Context) (done bool, err error) {
   378  				// this must always use lifecycleCtx because NewProvider uses that context for future key set fetching.
   379  				// this also means that there is no correct way to control the timeout of the discovery request made by NewProvider.
   380  				// the global timeout of the http.Client is still honored.
   381  				provider, err := oidc.NewProvider(lifecycleCtx, issuerURL)
   382  				if err != nil {
   383  					klog.Errorf("oidc authenticator: initializing plugin: %v", err)
   384  					authn.healthCheck.Store(&errorHolder{err: err})
   385  					return false, nil
   386  				}
   387  
   388  				verifier := provider.Verifier(verifierConfig)
   389  				authn.setVerifier(&idTokenVerifier{verifier, audiences})
   390  				return true, nil
   391  			})
   392  		}()
   393  	}
   394  
   395  	return newInstrumentedAuthenticator(issuerURL, authn), nil
   396  }
   397  
   398  type errorHolder struct {
   399  	err error
   400  }
   401  
   402  // discoveryURLRoundTripper is a http.RoundTripper that rewrites the
   403  // {url}/.well-known/openid-configuration to the discovery URL.
   404  type discoveryURLRoundTripper struct {
   405  	base http.RoundTripper
   406  	// discoveryURL is the URL to use to fetch the openid configuration
   407  	discoveryURL *url.URL
   408  	// urlToRewrite is the URL to rewrite to the discovery URL
   409  	urlToRewrite string
   410  }
   411  
   412  func (t *discoveryURLRoundTripper) RoundTrip(req *http.Request) (*http.Response, error) {
   413  	if req.Method == http.MethodGet && req.URL.String() == t.urlToRewrite {
   414  		clone := req.Clone(req.Context())
   415  		clone.Host = ""
   416  		clone.URL = t.discoveryURL
   417  		return t.base.RoundTrip(clone)
   418  	}
   419  	return t.base.RoundTrip(req)
   420  }
   421  
   422  // untrustedIssuer extracts an untrusted "iss" claim from the given JWT token,
   423  // or returns an error if the token can not be parsed.  Since the JWT is not
   424  // verified, the returned issuer should not be trusted.
   425  func untrustedIssuer(token string) (string, error) {
   426  	if strings.HasPrefix(strings.TrimSpace(token), "{") {
   427  		return "", fmt.Errorf("token is not compact JWT")
   428  	}
   429  	parts := strings.Split(token, ".")
   430  	if len(parts) != 3 {
   431  		return "", fmt.Errorf("malformed token")
   432  	}
   433  	payload, err := base64.RawURLEncoding.DecodeString(parts[1])
   434  	if err != nil {
   435  		return "", fmt.Errorf("error decoding token: %v", err)
   436  	}
   437  	claims := struct {
   438  		// WARNING: this JWT is not verified. Do not trust these claims.
   439  		Issuer string `json:"iss"`
   440  	}{}
   441  	if err := json.Unmarshal(payload, &claims); err != nil {
   442  		return "", fmt.Errorf("while unmarshaling token: %v", err)
   443  	}
   444  	// Coalesce the legacy GoogleIss with the new one.
   445  	//
   446  	// http://openid.net/specs/openid-connect-core-1_0.html#GoogleIss
   447  	if claims.Issuer == "accounts.google.com" {
   448  		return "https://accounts.google.com", nil
   449  	}
   450  	return claims.Issuer, nil
   451  }
   452  
   453  func hasCorrectIssuer(iss, tokenData string) bool {
   454  	uiss, err := untrustedIssuer(tokenData)
   455  	if err != nil {
   456  		return false
   457  	}
   458  	if uiss != iss {
   459  		return false
   460  	}
   461  	return true
   462  }
   463  
   464  // endpoint represents an OIDC distributed claims endpoint.
   465  type endpoint struct {
   466  	// URL to use to request the distributed claim.  This URL is expected to be
   467  	// prefixed by one of the known issuer URLs.
   468  	URL string `json:"endpoint,omitempty"`
   469  	// AccessToken is the bearer token to use for access.  If empty, it is
   470  	// not used.  Access token is optional per the OIDC distributed claims
   471  	// specification.
   472  	// See: http://openid.net/specs/openid-connect-core-1_0.html#DistributedExample
   473  	AccessToken string `json:"access_token,omitempty"`
   474  	// JWT is the container for aggregated claims.  Not supported at the moment.
   475  	// See: http://openid.net/specs/openid-connect-core-1_0.html#AggregatedExample
   476  	JWT string `json:"JWT,omitempty"`
   477  }
   478  
   479  // claimResolver expands distributed claims by calling respective claim source
   480  // endpoints.
   481  type claimResolver struct {
   482  	ctx context.Context
   483  
   484  	// claim is the distributed claim that may be resolved.
   485  	claim string
   486  
   487  	// audiences is the set of acceptable audiences the JWT must be issued to.
   488  	// At least one of the entries must match the "aud" claim in presented JWTs.
   489  	audiences sets.Set[string]
   490  
   491  	// client is the to use for resolving distributed claims
   492  	client *http.Client
   493  
   494  	// config is the OIDC configuration used for resolving distributed claims.
   495  	config *oidc.Config
   496  
   497  	// verifierPerIssuer contains, for each issuer, the appropriate verifier to use
   498  	// for this claim.  It is assumed that there will be very few entries in
   499  	// this map.
   500  	// Guarded by m.
   501  	verifierPerIssuer map[string]*asyncIDTokenVerifier
   502  
   503  	m sync.Mutex
   504  }
   505  
   506  // newClaimResolver creates a new resolver for distributed claims.
   507  // the input ctx is retained and is used as the base context for background requests such as key fetching.
   508  func newClaimResolver(ctx context.Context, claim string, client *http.Client, config *oidc.Config, audiences sets.Set[string]) *claimResolver {
   509  	return &claimResolver{
   510  		ctx:               ctx,
   511  		claim:             claim,
   512  		audiences:         audiences,
   513  		client:            client,
   514  		config:            config,
   515  		verifierPerIssuer: map[string]*asyncIDTokenVerifier{},
   516  	}
   517  }
   518  
   519  // Verifier returns either the verifier for the specified issuer, or error.
   520  func (r *claimResolver) Verifier(iss string) (*idTokenVerifier, error) {
   521  	r.m.Lock()
   522  	av := r.verifierPerIssuer[iss]
   523  	if av == nil {
   524  		// This lazy init should normally be very quick.
   525  		ctx := oidc.ClientContext(r.ctx, r.client)
   526  		av = newAsyncIDTokenVerifier(ctx, r.config, iss, r.audiences)
   527  		r.verifierPerIssuer[iss] = av
   528  	}
   529  	r.m.Unlock()
   530  
   531  	v := av.verifier()
   532  	if v == nil {
   533  		return nil, fmt.Errorf("verifier not initialized for issuer: %q", iss)
   534  	}
   535  	return v, nil
   536  }
   537  
   538  // expand extracts the distributed claims from claim names and claim sources.
   539  // The extracted claim value is pulled up into the supplied claims.
   540  //
   541  // Distributed claims are of the form as seen below, and are defined in the
   542  // OIDC Connect Core 1.0, section 5.6.2.
   543  // See: https://openid.net/specs/openid-connect-core-1_0.html#AggregatedDistributedClaims
   544  //
   545  //	{
   546  //	  ... (other normal claims)...
   547  //	  "_claim_names": {
   548  //	    "groups": "src1"
   549  //	  },
   550  //	  "_claim_sources": {
   551  //	    "src1": {
   552  //	      "endpoint": "https://www.example.com",
   553  //	      "access_token": "f005ba11"
   554  //	    },
   555  //	  },
   556  //	}
   557  func (r *claimResolver) expand(ctx context.Context, c claims) error {
   558  	const (
   559  		// The claim containing a map of endpoint references per claim.
   560  		// OIDC Connect Core 1.0, section 5.6.2.
   561  		claimNamesKey = "_claim_names"
   562  		// The claim containing endpoint specifications.
   563  		// OIDC Connect Core 1.0, section 5.6.2.
   564  		claimSourcesKey = "_claim_sources"
   565  	)
   566  
   567  	_, ok := c[r.claim]
   568  	if ok {
   569  		// There already is a normal claim, skip resolving.
   570  		return nil
   571  	}
   572  	names, ok := c[claimNamesKey]
   573  	if !ok {
   574  		// No _claim_names, no keys to look up.
   575  		return nil
   576  	}
   577  
   578  	claimToSource := map[string]string{}
   579  	if err := json.Unmarshal([]byte(names), &claimToSource); err != nil {
   580  		return fmt.Errorf("oidc: error parsing distributed claim names: %v", err)
   581  	}
   582  
   583  	rawSources, ok := c[claimSourcesKey]
   584  	if !ok {
   585  		// Having _claim_names claim,  but no _claim_sources is not an expected
   586  		// state.
   587  		return fmt.Errorf("oidc: no claim sources")
   588  	}
   589  
   590  	var sources map[string]endpoint
   591  	if err := json.Unmarshal([]byte(rawSources), &sources); err != nil {
   592  		// The claims sources claim is malformed, this is not an expected state.
   593  		return fmt.Errorf("oidc: could not parse claim sources: %v", err)
   594  	}
   595  
   596  	src, ok := claimToSource[r.claim]
   597  	if !ok {
   598  		// No distributed claim present.
   599  		return nil
   600  	}
   601  	ep, ok := sources[src]
   602  	if !ok {
   603  		return fmt.Errorf("id token _claim_names contained a source %s missing in _claims_sources", src)
   604  	}
   605  	if ep.URL == "" {
   606  		// This is maybe an aggregated claim (ep.JWT != "").
   607  		return nil
   608  	}
   609  	return r.resolve(ctx, ep, c)
   610  }
   611  
   612  // resolve requests distributed claims from all endpoints passed in,
   613  // and inserts the lookup results into allClaims.
   614  func (r *claimResolver) resolve(ctx context.Context, endpoint endpoint, allClaims claims) error {
   615  	// TODO: cache resolved claims.
   616  	jwt, err := getClaimJWT(ctx, r.client, endpoint.URL, endpoint.AccessToken)
   617  	if err != nil {
   618  		return fmt.Errorf("while getting distributed claim %q: %v", r.claim, err)
   619  	}
   620  	untrustedIss, err := untrustedIssuer(jwt)
   621  	if err != nil {
   622  		return fmt.Errorf("getting untrusted issuer from endpoint %v failed for claim %q: %v", endpoint.URL, r.claim, err)
   623  	}
   624  	v, err := r.Verifier(untrustedIss)
   625  	if err != nil {
   626  		return fmt.Errorf("verifying untrusted issuer %v failed: %v", untrustedIss, err)
   627  	}
   628  	t, err := v.Verify(ctx, jwt)
   629  	if err != nil {
   630  		return fmt.Errorf("verify distributed claim token: %v", err)
   631  	}
   632  	var distClaims claims
   633  	if err := t.Claims(&distClaims); err != nil {
   634  		return fmt.Errorf("could not parse distributed claims for claim %v: %v", r.claim, err)
   635  	}
   636  	value, ok := distClaims[r.claim]
   637  	if !ok {
   638  		return fmt.Errorf("jwt returned by distributed claim endpoint %q did not contain claim: %v", endpoint.URL, r.claim)
   639  	}
   640  	allClaims[r.claim] = value
   641  	return nil
   642  }
   643  
   644  func (v *idTokenVerifier) Verify(ctx context.Context, rawIDToken string) (*oidc.IDToken, error) {
   645  	t, err := v.verifier.Verify(ctx, rawIDToken)
   646  	if err != nil {
   647  		return nil, err
   648  	}
   649  	if err := v.verifyAudience(t); err != nil {
   650  		return nil, err
   651  	}
   652  	return t, nil
   653  }
   654  
   655  // verifyAudience verifies the audience field in the ID token matches the expected audience.
   656  // This is added based on https://github.com/coreos/go-oidc/blob/b203e58c24394ddf5e816706a7645f01280245c7/oidc/verify.go#L275-L281
   657  // with the difference that we allow multiple audiences.
   658  //
   659  // AuthenticationConfiguration has a audienceMatchPolicy field, but the only supported value now is "MatchAny".
   660  // So, The default match behavior is to match at least one of the audiences in the ID token.
   661  func (v *idTokenVerifier) verifyAudience(t *oidc.IDToken) error {
   662  	// We validate audience field is not empty in the authentication configuration.
   663  	// This check ensures callers of "Verify" using idTokenVerifier are not passing
   664  	// an empty audience.
   665  	if v.audiences.Len() == 0 {
   666  		return fmt.Errorf("oidc: invalid configuration, audiences cannot be empty")
   667  	}
   668  	if v.audiences.HasAny(t.Audience...) {
   669  		return nil
   670  	}
   671  
   672  	return fmt.Errorf("oidc: expected audience in %q got %q", sets.List(v.audiences), t.Audience)
   673  }
   674  
   675  func (a *jwtAuthenticator) AuthenticateToken(ctx context.Context, token string) (*authenticator.Response, bool, error) {
   676  	if !hasCorrectIssuer(a.jwtAuthenticator.Issuer.URL, token) {
   677  		return nil, false, nil
   678  	}
   679  
   680  	verifier, ok := a.idTokenVerifier()
   681  	if !ok {
   682  		return nil, false, fmt.Errorf("oidc: authenticator not initialized")
   683  	}
   684  
   685  	idToken, err := verifier.Verify(ctx, token)
   686  	if err != nil {
   687  		return nil, false, fmt.Errorf("oidc: verify token: %v", err)
   688  	}
   689  
   690  	var c claims
   691  	if err := idToken.Claims(&c); err != nil {
   692  		return nil, false, fmt.Errorf("oidc: parse claims: %v", err)
   693  	}
   694  	if a.resolver != nil {
   695  		if err := a.resolver.expand(ctx, c); err != nil {
   696  			return nil, false, fmt.Errorf("oidc: could not expand distributed claims: %v", err)
   697  		}
   698  	}
   699  
   700  	var claimsUnstructured *unstructured.Unstructured
   701  	// Convert the claims to unstructured so that we can evaluate the CEL expressions
   702  	// against the claims. This is done once here so that we don't have to convert
   703  	// the claims to unstructured multiple times in the CEL mapper for each mapping.
   704  	// Only perform this conversion if any of the mapping or validation rules contain
   705  	// CEL expressions.
   706  	// TODO(aramase): In the future when we look into making distributed claims work,
   707  	// we should see if we can skip this function and use a dynamic type resolver for
   708  	// both json.RawMessage and the distributed claim fetching.
   709  	if a.celMapper.Username != nil || a.celMapper.Groups != nil || a.celMapper.UID != nil || a.celMapper.Extra != nil || a.celMapper.ClaimValidationRules != nil {
   710  		if claimsUnstructured, err = convertObjectToUnstructured(&c); err != nil {
   711  			return nil, false, fmt.Errorf("oidc: could not convert claims to unstructured: %w", err)
   712  		}
   713  	}
   714  
   715  	var username string
   716  	if username, err = a.getUsername(ctx, c, claimsUnstructured); err != nil {
   717  		return nil, false, err
   718  	}
   719  
   720  	info := &user.DefaultInfo{Name: username}
   721  	if info.Groups, err = a.getGroups(ctx, c, claimsUnstructured); err != nil {
   722  		return nil, false, err
   723  	}
   724  
   725  	if info.UID, err = a.getUID(ctx, c, claimsUnstructured); err != nil {
   726  		return nil, false, err
   727  	}
   728  
   729  	extra, err := a.getExtra(ctx, claimsUnstructured)
   730  	if err != nil {
   731  		return nil, false, err
   732  	}
   733  	if len(extra) > 0 {
   734  		info.Extra = extra
   735  	}
   736  
   737  	// check to ensure all required claims are present in the ID token and have matching values.
   738  	for claim, value := range a.requiredClaims {
   739  		if !c.hasClaim(claim) {
   740  			return nil, false, fmt.Errorf("oidc: required claim %s not present in ID token", claim)
   741  		}
   742  
   743  		// NOTE: Only string values are supported as valid required claim values.
   744  		var claimValue string
   745  		if err := c.unmarshalClaim(claim, &claimValue); err != nil {
   746  			return nil, false, fmt.Errorf("oidc: parse claim %s: %w", claim, err)
   747  		}
   748  		if claimValue != value {
   749  			return nil, false, fmt.Errorf("oidc: required claim %s value does not match. Got = %s, want = %s", claim, claimValue, value)
   750  		}
   751  	}
   752  
   753  	if a.celMapper.ClaimValidationRules != nil {
   754  		evalResult, err := a.celMapper.ClaimValidationRules.EvalClaimMappings(ctx, claimsUnstructured)
   755  		if err != nil {
   756  			return nil, false, fmt.Errorf("oidc: error evaluating claim validation expression: %w", err)
   757  		}
   758  		if err := checkValidationRulesEvaluation(evalResult, func(a authenticationcel.ExpressionAccessor) (string, error) {
   759  			claimValidationCondition, ok := a.(*authenticationcel.ClaimValidationCondition)
   760  			if !ok {
   761  				return "", fmt.Errorf("invalid type conversion, expected ClaimValidationCondition")
   762  			}
   763  			return claimValidationCondition.Message, nil
   764  		}); err != nil {
   765  			return nil, false, fmt.Errorf("oidc: error evaluating claim validation expression: %w", err)
   766  		}
   767  	}
   768  
   769  	if a.celMapper.UserValidationRules != nil {
   770  		// Convert the user info to unstructured so that we can evaluate the CEL expressions
   771  		// against the user info. This is done once here so that we don't have to convert
   772  		// the user info to unstructured multiple times in the CEL mapper for each mapping.
   773  		userInfoUnstructured, err := convertUserInfoToUnstructured(info)
   774  		if err != nil {
   775  			return nil, false, fmt.Errorf("oidc: could not convert user info to unstructured: %w", err)
   776  		}
   777  
   778  		evalResult, err := a.celMapper.UserValidationRules.EvalUser(ctx, userInfoUnstructured)
   779  		if err != nil {
   780  			return nil, false, fmt.Errorf("oidc: error evaluating user info validation rule: %w", err)
   781  		}
   782  		if err := checkValidationRulesEvaluation(evalResult, func(a authenticationcel.ExpressionAccessor) (string, error) {
   783  			userValidationCondition, ok := a.(*authenticationcel.UserValidationCondition)
   784  			if !ok {
   785  				return "", fmt.Errorf("invalid type conversion, expected UserValidationCondition")
   786  			}
   787  			return userValidationCondition.Message, nil
   788  		}); err != nil {
   789  			return nil, false, fmt.Errorf("oidc: error evaluating user info validation rule: %w", err)
   790  		}
   791  	}
   792  
   793  	return &authenticator.Response{User: info}, true, nil
   794  }
   795  
   796  func (a *jwtAuthenticator) HealthCheck() error {
   797  	if holder := *a.healthCheck.Load(); holder.err != nil {
   798  		return fmt.Errorf("oidc: authenticator for issuer %q is not healthy: %w", a.jwtAuthenticator.Issuer.URL, holder.err)
   799  	}
   800  
   801  	return nil
   802  }
   803  
   804  func (a *jwtAuthenticator) getUsername(ctx context.Context, c claims, claimsUnstructured *unstructured.Unstructured) (string, error) {
   805  	if a.celMapper.Username != nil {
   806  		evalResult, err := a.celMapper.Username.EvalClaimMapping(ctx, claimsUnstructured)
   807  		if err != nil {
   808  			return "", fmt.Errorf("oidc: error evaluating username claim expression: %w", err)
   809  		}
   810  		if evalResult.EvalResult.Type() != celgo.StringType {
   811  			return "", fmt.Errorf("oidc: error evaluating username claim expression: %w", fmt.Errorf("username claim expression must return a string"))
   812  		}
   813  
   814  		username := evalResult.EvalResult.Value().(string)
   815  
   816  		if len(username) == 0 {
   817  			return "", fmt.Errorf("oidc: empty username via CEL expression is not allowed")
   818  		}
   819  
   820  		return username, nil
   821  	}
   822  
   823  	var username string
   824  	usernameClaim := a.jwtAuthenticator.ClaimMappings.Username.Claim
   825  	if err := c.unmarshalClaim(usernameClaim, &username); err != nil {
   826  		return "", fmt.Errorf("oidc: parse username claims %q: %v", usernameClaim, err)
   827  	}
   828  
   829  	if usernameClaim == "email" {
   830  		// If the email_verified claim is present, ensure the email is valid.
   831  		// https://openid.net/specs/openid-connect-core-1_0.html#StandardClaims
   832  		if hasEmailVerified := c.hasClaim("email_verified"); hasEmailVerified {
   833  			var emailVerified bool
   834  			if err := c.unmarshalClaim("email_verified", &emailVerified); err != nil {
   835  				return "", fmt.Errorf("oidc: parse 'email_verified' claim: %v", err)
   836  			}
   837  
   838  			// If the email_verified claim is present we have to verify it is set to `true`.
   839  			if !emailVerified {
   840  				return "", fmt.Errorf("oidc: email not verified")
   841  			}
   842  		}
   843  	}
   844  
   845  	userNamePrefix := a.jwtAuthenticator.ClaimMappings.Username.Prefix
   846  	if userNamePrefix != nil && *userNamePrefix != "" {
   847  		return *userNamePrefix + username, nil
   848  	}
   849  	return username, nil
   850  }
   851  
   852  func (a *jwtAuthenticator) getGroups(ctx context.Context, c claims, claimsUnstructured *unstructured.Unstructured) ([]string, error) {
   853  	groupsClaim := a.jwtAuthenticator.ClaimMappings.Groups.Claim
   854  	if len(groupsClaim) > 0 {
   855  		if _, ok := c[groupsClaim]; ok {
   856  			// Some admins want to use string claims like "role" as the group value.
   857  			// Allow the group claim to be a single string instead of an array.
   858  			//
   859  			// See: https://github.com/kubernetes/kubernetes/issues/33290
   860  			var groups stringOrArray
   861  			if err := c.unmarshalClaim(groupsClaim, &groups); err != nil {
   862  				return nil, fmt.Errorf("oidc: parse groups claim %q: %w", groupsClaim, err)
   863  			}
   864  
   865  			prefix := a.jwtAuthenticator.ClaimMappings.Groups.Prefix
   866  			if prefix != nil && *prefix != "" {
   867  				for i, group := range groups {
   868  					groups[i] = *prefix + group
   869  				}
   870  			}
   871  
   872  			return []string(groups), nil
   873  		}
   874  	}
   875  
   876  	if a.celMapper.Groups == nil {
   877  		return nil, nil
   878  	}
   879  
   880  	evalResult, err := a.celMapper.Groups.EvalClaimMapping(ctx, claimsUnstructured)
   881  	if err != nil {
   882  		return nil, fmt.Errorf("oidc: error evaluating group claim expression: %w", err)
   883  	}
   884  
   885  	groups, err := convertCELValueToStringList(evalResult.EvalResult)
   886  	if err != nil {
   887  		return nil, fmt.Errorf("oidc: error evaluating group claim expression: %w", err)
   888  	}
   889  	return groups, nil
   890  }
   891  
   892  func (a *jwtAuthenticator) getUID(ctx context.Context, c claims, claimsUnstructured *unstructured.Unstructured) (string, error) {
   893  	uidClaim := a.jwtAuthenticator.ClaimMappings.UID.Claim
   894  	if len(uidClaim) > 0 {
   895  		var uid string
   896  		if err := c.unmarshalClaim(uidClaim, &uid); err != nil {
   897  			return "", fmt.Errorf("oidc: parse uid claim %q: %w", uidClaim, err)
   898  		}
   899  		return uid, nil
   900  	}
   901  
   902  	if a.celMapper.UID == nil {
   903  		return "", nil
   904  	}
   905  
   906  	evalResult, err := a.celMapper.UID.EvalClaimMapping(ctx, claimsUnstructured)
   907  	if err != nil {
   908  		return "", fmt.Errorf("oidc: error evaluating uid claim expression: %w", err)
   909  	}
   910  	if evalResult.EvalResult.Type() != celgo.StringType {
   911  		return "", fmt.Errorf("oidc: error evaluating uid claim expression: %w", fmt.Errorf("uid claim expression must return a string"))
   912  	}
   913  
   914  	return evalResult.EvalResult.Value().(string), nil
   915  }
   916  
   917  func (a *jwtAuthenticator) getExtra(ctx context.Context, claimsUnstructured *unstructured.Unstructured) (map[string][]string, error) {
   918  	if a.celMapper.Extra == nil {
   919  		return nil, nil
   920  	}
   921  
   922  	evalResult, err := a.celMapper.Extra.EvalClaimMappings(ctx, claimsUnstructured)
   923  	if err != nil {
   924  		return nil, err
   925  	}
   926  
   927  	extra := make(map[string][]string, len(evalResult))
   928  	for _, result := range evalResult {
   929  		extraMapping, ok := result.ExpressionAccessor.(*authenticationcel.ExtraMappingExpression)
   930  		if !ok {
   931  			return nil, fmt.Errorf("oidc: error evaluating extra claim expression: %w", fmt.Errorf("invalid type conversion, expected ExtraMappingCondition"))
   932  		}
   933  
   934  		extraValues, err := convertCELValueToStringList(result.EvalResult)
   935  		if err != nil {
   936  			return nil, fmt.Errorf("oidc: error evaluating extra claim expression: %s: %w", extraMapping.Expression, err)
   937  		}
   938  
   939  		if len(extraValues) == 0 {
   940  			continue
   941  		}
   942  
   943  		extra[extraMapping.Key] = extraValues
   944  	}
   945  
   946  	return extra, nil
   947  }
   948  
   949  // getClaimJWT gets a distributed claim JWT from url, using the supplied access
   950  // token as bearer token.  If the access token is "", the authorization header
   951  // will not be set.
   952  // TODO: Allow passing in JSON hints to the IDP.
   953  func getClaimJWT(ctx context.Context, client *http.Client, url, accessToken string) (string, error) {
   954  	// TODO: Allow passing request body with configurable information.
   955  	req, err := http.NewRequest("GET", url, nil)
   956  	if err != nil {
   957  		return "", fmt.Errorf("while calling %v: %v", url, err)
   958  	}
   959  	if accessToken != "" {
   960  		req.Header.Set("Authorization", fmt.Sprintf("Bearer %v", accessToken))
   961  	}
   962  	req = req.WithContext(ctx)
   963  	response, err := client.Do(req)
   964  	if err != nil {
   965  		return "", err
   966  	}
   967  	defer response.Body.Close()
   968  	// Report non-OK status code as an error.
   969  	if response.StatusCode < http.StatusOK || response.StatusCode > http.StatusIMUsed {
   970  		return "", fmt.Errorf("error while getting distributed claim JWT: %v", response.Status)
   971  	}
   972  	responseBytes, err := ioutil.ReadAll(response.Body)
   973  	if err != nil {
   974  		return "", fmt.Errorf("could not decode distributed claim response")
   975  	}
   976  	return string(responseBytes), nil
   977  }
   978  
   979  type stringOrArray []string
   980  
   981  func (s *stringOrArray) UnmarshalJSON(b []byte) error {
   982  	var a []string
   983  	if err := json.Unmarshal(b, &a); err == nil {
   984  		*s = a
   985  		return nil
   986  	}
   987  	var str string
   988  	if err := json.Unmarshal(b, &str); err != nil {
   989  		return err
   990  	}
   991  	*s = []string{str}
   992  	return nil
   993  }
   994  
   995  type claims map[string]json.RawMessage
   996  
   997  func (c claims) unmarshalClaim(name string, v interface{}) error {
   998  	val, ok := c[name]
   999  	if !ok {
  1000  		return fmt.Errorf("claim not present")
  1001  	}
  1002  	return json.Unmarshal([]byte(val), v)
  1003  }
  1004  
  1005  func (c claims) hasClaim(name string) bool {
  1006  	if _, ok := c[name]; !ok {
  1007  		return false
  1008  	}
  1009  	return true
  1010  }
  1011  
  1012  // convertCELValueToStringList converts the CEL value to a string list.
  1013  // The CEL value needs to be either a string or a list of strings.
  1014  // "", [] are treated as not being present and will return nil.
  1015  // Empty string in a list of strings is treated as not being present and will be filtered out.
  1016  func convertCELValueToStringList(val ref.Val) ([]string, error) {
  1017  	switch val.Type().TypeName() {
  1018  	case celgo.StringType.TypeName():
  1019  		out := val.Value().(string)
  1020  		if len(out) == 0 {
  1021  			return nil, nil
  1022  		}
  1023  		return []string{out}, nil
  1024  
  1025  	case celgo.ListType(nil).TypeName():
  1026  		var result []string
  1027  		switch val.Value().(type) {
  1028  		case []interface{}:
  1029  			for _, v := range val.Value().([]interface{}) {
  1030  				out, ok := v.(string)
  1031  				if !ok {
  1032  					return nil, fmt.Errorf("expression must return a string or a list of strings")
  1033  				}
  1034  				if len(out) == 0 {
  1035  					continue
  1036  				}
  1037  				result = append(result, out)
  1038  			}
  1039  		case []ref.Val:
  1040  			for _, v := range val.Value().([]ref.Val) {
  1041  				out, ok := v.Value().(string)
  1042  				if !ok {
  1043  					return nil, fmt.Errorf("expression must return a string or a list of strings")
  1044  				}
  1045  				if len(out) == 0 {
  1046  					continue
  1047  				}
  1048  				result = append(result, out)
  1049  			}
  1050  		default:
  1051  			return nil, fmt.Errorf("expression must return a string or a list of strings")
  1052  		}
  1053  
  1054  		if len(result) == 0 {
  1055  			return nil, nil
  1056  		}
  1057  
  1058  		return result, nil
  1059  	case celgo.NullType.TypeName():
  1060  		return nil, nil
  1061  	default:
  1062  		return nil, fmt.Errorf("expression must return a string or a list of strings")
  1063  	}
  1064  }
  1065  
  1066  // messageFunc is a function that returns a message for a validation rule.
  1067  type messageFunc func(authenticationcel.ExpressionAccessor) (string, error)
  1068  
  1069  // checkValidationRulesEvaluation checks if the validation rules evaluation results
  1070  // are valid. If the validation rules evaluation results are not valid, it returns
  1071  // an error with an optional message that was set in the validation rule.
  1072  func checkValidationRulesEvaluation(results []authenticationcel.EvaluationResult, messageFn messageFunc) error {
  1073  	for _, result := range results {
  1074  		if result.EvalResult.Type() != celgo.BoolType {
  1075  			return fmt.Errorf("validation expression must return a boolean")
  1076  		}
  1077  		if !result.EvalResult.Value().(bool) {
  1078  			expression := result.ExpressionAccessor.GetExpression()
  1079  
  1080  			message, err := messageFn(result.ExpressionAccessor)
  1081  			if err != nil {
  1082  				return err
  1083  			}
  1084  
  1085  			return fmt.Errorf("validation expression '%s' failed: %s", expression, message)
  1086  		}
  1087  	}
  1088  
  1089  	return nil
  1090  }
  1091  
  1092  func convertObjectToUnstructured(obj interface{}) (*unstructured.Unstructured, error) {
  1093  	if obj == nil || reflect.ValueOf(obj).IsNil() {
  1094  		return &unstructured.Unstructured{Object: nil}, nil
  1095  	}
  1096  	ret, err := runtime.DefaultUnstructuredConverter.ToUnstructured(obj)
  1097  	if err != nil {
  1098  		return nil, err
  1099  	}
  1100  	return &unstructured.Unstructured{Object: ret}, nil
  1101  }
  1102  
  1103  func convertUserInfoToUnstructured(info user.Info) (*unstructured.Unstructured, error) {
  1104  	userInfo := &authenticationv1.UserInfo{
  1105  		Extra:    make(map[string]authenticationv1.ExtraValue),
  1106  		Groups:   info.GetGroups(),
  1107  		UID:      info.GetUID(),
  1108  		Username: info.GetName(),
  1109  	}
  1110  	// Convert the extra information in the user object
  1111  	for key, val := range info.GetExtra() {
  1112  		userInfo.Extra[key] = authenticationv1.ExtraValue(val)
  1113  	}
  1114  
  1115  	// Convert the user info to unstructured so that we can evaluate the CEL expressions
  1116  	// against the user info. This is done once here so that we don't have to convert
  1117  	// the user info to unstructured multiple times in the CEL mapper for each mapping.
  1118  	userInfoUnstructured, err := convertObjectToUnstructured(userInfo)
  1119  	if err != nil {
  1120  		return nil, err
  1121  	}
  1122  
  1123  	// check if the user info contains the required fields. If not, set them to empty values.
  1124  	// This is done because the CEL expressions expect these fields to be present.
  1125  	if userInfoUnstructured.Object["username"] == nil {
  1126  		userInfoUnstructured.Object["username"] = ""
  1127  	}
  1128  	if userInfoUnstructured.Object["uid"] == nil {
  1129  		userInfoUnstructured.Object["uid"] = ""
  1130  	}
  1131  	if userInfoUnstructured.Object["groups"] == nil {
  1132  		userInfoUnstructured.Object["groups"] = []string{}
  1133  	}
  1134  	if userInfoUnstructured.Object["extra"] == nil {
  1135  		userInfoUnstructured.Object["extra"] = map[string]authenticationv1.ExtraValue{}
  1136  	}
  1137  	return userInfoUnstructured, nil
  1138  }