Change:
<!-- Please use this template while reporting a bug and provide as much info as possible. Not doing so may result in your bug not being addressed in a timely manner. Thanks! If the matter is security related, please disclose it privately via --What happened: We using go-code kubectl cli package. We are a long running app serving to many different Kubernetes clusters. Each cluster has a different user credentials and bearer token to access it. But they all use the same issuer and clientid. The oidc cache is keyed off of these two only. So what happens is when we switch to another configuration it pulls in the oidc provider for the other cluster and that bearer token does not have authority to this other cluster. The cluster config has the correct info in it but the oidc provider ignores that and used the stale data. What you expected to happen: oidc provider would always use the appropriate current config context to access the cluster How to reproduce it (as minimally and precisely as possible): In same app instance create two cluster configs to two different clusters with same oidc issuer and clientid by DIFFERENT credentials. Where the credentials on one are not authorized for the other cluster. Try to get namespaces on each, one will work and the other won't. Anything else we need to know?: Environment: Kubernetes version (use ): latest go-code from as a package called from go application.Cloud provider or hardware configuration: IBM Kubernetes OS (e.g: ): Kernel (e.g. ): Install tools: Network plugin and version (if this is a network-related bug): Others:
Reiterating the mentions to trigger a notification: <detailsIn response to : Instructions for interacting with me using PR comments are available . If you have questions or suggestions related to my behavior, please file an issue against the repository. </details
Issues go stale after 90d of inactivity. Mark the issue as fresh with . Stale issues rot after an additional 30d of inactivity and eventually close. If this issue is safe to close now please do so with . Send feedback to sig-testing, kubernetes/test-infra and/or . /lifecycle stale
Stale issues rot after 30d of inactivity. Mark the issue as fresh with . Rotten issues close after an additional 30d of inactivity. If this issue is safe to close now please do so with . Send feedback to sig-testing, kubernetes/test-infra and/or . /lifecycle rotten
/remove-lifecycle rotten

Discussion:
} type cacheKey struct { <ins> clusterAddress string </ins> // Canonical issuer URL string of the provider. issuerURL string clientID  string } <del> func (c *clientCache) getClient(issuer, clientID string) (*oidcAuthProvider, bool) { </del> <ins> func (c *clientCache) getClient(clusterAddress, issuer, clientID string) (*oidcAuthProvider, bool) { </ins> c.mu.RLock() defer c.mu.RUnlock() <del> client, ok := c.cache[cacheKey{issuer, clientID}] </del> <ins> client, ok := c.cache[cacheKey{clusterAddress: clusterAddress, issuerURL: issuer, clientID: clientID}] </ins> return client, ok } // setClient attempts to put the client in the cache but may return any clients // with the same keys set before. This is so there's only ever one client for a provider. <del> func (c *clientCache) setClient(issuer, clientID string, client *oidcAuthProvider) *oidcAuthProvider { </del> <ins> func (c *clientCache) setClient(clusterAddress, issuer, clientID string, client *oidcAuthProvider) *oidcAuthProvider { </ins> c.mu.Lock() defer c.mu.Unlock() <del> key := cacheKey{issuer, clientID} </del> <ins> key := cacheKey{clusterAddress: clusterAddress, issuerURL: issuer, clientID: clientID} </ins> // If another client has already initialized a client for the given provider we want // to use that client instead of the one we're trying to set. This is so all transports

return client } <del> func newOIDCAuthProvider(_ string, cfg map[string]string, persister restclient.AuthProviderConfigPersister) (restclient.AuthProvider, error) { </del> <ins> func newOIDCAuthProvider(clusterAddress string, cfg map[string]string, persister restclient.AuthProviderConfigPersister) (restclient.AuthProvider, error) { </ins> issuer := cfg[cfgIssuerUrl] if issuer == "" { return nil, fmt.Errorf("Must provide %s", cfgIssuerUrl)

} // Check cache for existing provider. <del> if provider, ok := cache.getClient(issuer, clientID); ok { </del> <ins> if provider, ok := cache.getClient(clusterAddress, issuer, clientID); ok { </ins> return provider, nil }

persister: persister, } <del> return cache.setClient(issuer, clientID, provider), nil </del> <ins> return cache.setClient(clusterAddress, issuer, clientID, provider), nil </ins> } type oidcAuthProvider struct {

func TestClientCache(t *testing.T) { cache := newClientCache() <del> if _, ok := cache.getClient("issuer1", "id1"); ok { </del> <ins> if _, ok := cache.getClient("cluster1", "issuer1", "id1"); ok { </ins> t.Fatalf("got client before putting one in the cache") } <ins> assertCacheLen(t, cache, 0) </ins> cli1 := new(oidcAuthProvider) cli2 := new(oidcAuthProvider) <ins> cli3 := new(oidcAuthProvider) </ins> <del> gotcli := cache.setClient("issuer1", "id1", cli1) </del> <ins> gotcli := cache.setClient("cluster1", "issuer1", "id1", cli1) </ins> if cli1 != gotcli { t.Fatalf("set first client and got a different one") } <ins> assertCacheLen(t, cache, 1) </ins> <del> gotcli = cache.setClient("issuer1", "id1", cli2) </del> <ins> gotcli = cache.setClient("cluster1", "issuer1", "id1", cli2) </ins> if cli1 != gotcli { t.Fatalf("set a second client and didn't get the first") } <ins> assertCacheLen(t, cache, 1) gotcli = cache.setClient("cluster2", "issuer1", "id1", cli3) if cli1 == gotcli { t.Fatalf("set a third client and got the first") } if cli3 != gotcli { t.Fatalf("set third client and got a different one") } assertCacheLen(t, cache, 2) } func assertCacheLen(t *testing.T, cache *clientCache, length int) { t.Helper() if len(cache.cache) != length { t.Errorf("expected cache length %d got %d", length, len(cache.cache)) } </ins> }