diff --git a/helpers.go b/helpers.go index 454e939..35534a5 100644 --- a/helpers.go +++ b/helpers.go @@ -309,6 +309,15 @@ func (tc *TokenCache) SetNamespace(ns string) { tc.ns.Store(ns) } +// Namespace returns the scope set by SetNamespace, or "" when none is set. +func (tc *TokenCache) Namespace() string { + if tc == nil { + return "" + } + ns, _ := tc.ns.Load().(string) + return ns +} + func (tc *TokenCache) key(token string) string { if ns, _ := tc.ns.Load().(string); ns != "" { return "t-" + ns + "|" + token diff --git a/token_cache_namespace_test.go b/token_cache_namespace_test.go index b7632fd..1270d3a 100644 --- a/token_cache_namespace_test.go +++ b/token_cache_namespace_test.go @@ -54,3 +54,16 @@ func TestTokenCacheEmptyNamespaceKeepsLegacyKey(t *testing.T) { t.Fatal("namespace-less caches must keep sharing the legacy key") } } + +func TestIntrospectionKeyScopedPerInstance(t *testing.T) { + a := &TraefikOidc{tokenCache: &TokenCache{}} + b := &TraefikOidc{tokenCache: &TokenCache{}} + a.tokenCache.SetNamespace("p|app-a|app-a") + b.tokenCache.SetNamespace("p|app-b|app-b") + if a.introspectionKey("tok") == b.introspectionKey("tok") { + t.Fatal("different instances share an introspection cache key") + } + if got := (&TraefikOidc{}).introspectionKey("tok"); got != "tok" { + t.Fatalf("namespace-less key = %q, want raw token", got) + } +} diff --git a/token_introspection.go b/token_introspection.go index d259c18..38f4397 100644 --- a/token_introspection.go +++ b/token_introspection.go @@ -53,7 +53,7 @@ type IntrospectionResponse struct { func (t *TraefikOidc) introspectToken(token string) (*IntrospectionResponse, error) { // Check cache first if t.introspectionCache != nil { - if cached, found := t.introspectionCache.Get(token); found { + if cached, found := t.introspectionCache.Get(t.introspectionKey(token)); found { if response, ok := cached.(*IntrospectionResponse); ok { t.logger.Debugf("Using cached introspection result for token") return response, nil @@ -173,7 +173,7 @@ func (t *TraefikOidc) introspectToken(token string) (*IntrospectionResponse, err cacheDuration = untilExp } } - t.introspectionCache.Set(token, &introspectionResp, cacheDuration) + t.introspectionCache.Set(t.introspectionKey(token), &introspectionResp, cacheDuration) t.logger.Debugf("Cached introspection result for %v", cacheDuration) } @@ -280,3 +280,12 @@ func (t *TraefikOidc) validateOpaqueToken(token string) error { t.logger.Debugf("Opaque token validation successful via introspection") return nil } + +// introspectionKey scopes an introspection verdict to this instance's +// provider, client and audience, like the verified-token cache. +func (t *TraefikOidc) introspectionKey(token string) string { + if ns := t.tokenCache.Namespace(); ns != "" { + return ns + "|" + token + } + return token +} diff --git a/token_manager.go b/token_manager.go index 4b8f970..0a3cd40 100644 --- a/token_manager.go +++ b/token_manager.go @@ -812,7 +812,7 @@ func (t *TraefikOidc) RevokeToken(token string) { // from the introspection cache after revocation. Next request // re-introspects and sees active=false. if t.introspectionCache != nil { - t.introspectionCache.Delete(token) + t.introspectionCache.Delete(t.introspectionKey(token)) } d := blacklistDuration(token)