mirror of
https://github.com/lukaszraczylo/traefikoidc.git
synced 2026-10-14 04:07:46 +00:00
- fail-on-old regression tests for every shipped fix (R-rounds) - fuzz targets (parseJWT, bearer), concurrency-stress, shuffle-flake guards - coverage-closing contract tests (health-check backend, TLS-posture reuse) - r154 ID-token fixture built from parts to avoid false-positive secret scan
63 lines
2.6 KiB
Go
63 lines
2.6 KiB
Go
package traefikoidc
|
|
|
|
import (
|
|
"testing"
|
|
"time"
|
|
)
|
|
|
|
// TestRevokeToken_EvictsIntrospectionCache guards the R120 fix: RevokeToken
|
|
// cleared tokenCache + blacklist but never the positive introspection cache,
|
|
// so a revoked OPAQUE access token (which has no jti and is therefore not
|
|
// covered by the JTI blacklist) kept passing via a cached "active"
|
|
// introspection verdict after logout. Revocation must evict that verdict so
|
|
// the next request re-introspects and sees active=false.
|
|
func TestRevokeToken_EvictsIntrospectionCache(t *testing.T) {
|
|
introCache := NewCache()
|
|
introCache.Set("opaque-token", &IntrospectionResponse{Active: true}, time.Minute)
|
|
|
|
oidc := &TraefikOidc{
|
|
logger: NewLogger("error"),
|
|
introspectionCache: introCache,
|
|
tokenCache: NewTokenCache(),
|
|
tokenBlacklist: NewCache(),
|
|
}
|
|
|
|
oidc.RevokeToken("opaque-token")
|
|
|
|
if _, found := introCache.Get("opaque-token"); found {
|
|
t.Fatal("revoked opaque token must not be served a cached active introspection result")
|
|
}
|
|
}
|
|
|
|
// TestShouldSkipGraceRefresh_UsesAccessTokenExpiry guards the R120 fix: the
|
|
// proactive-refresh grace check was keyed off the ID token's expiry even
|
|
// though the token forwarded downstream is the ACCESS token (and needsRefresh
|
|
// is derived from the access token's expiry for verifiable-JWT access
|
|
// tokens). With an access token inside the grace window but a longer-lived
|
|
// ID token, the old code skipped the refresh and forwarded a stale access
|
|
// token. The skip decision must be access-first (ID fallback only for
|
|
// opaque access tokens).
|
|
func TestShouldSkipGraceRefresh_UsesAccessTokenExpiry(t *testing.T) {
|
|
now := time.Now()
|
|
const grace = 60 * time.Second
|
|
|
|
accessWithinGrace := makeJWTForTest(map[string]any{"exp": float64(now.Add(30 * time.Second).Unix())})
|
|
accessFresh := makeJWTForTest(map[string]any{"exp": float64(now.Add(time.Hour).Unix())})
|
|
idFresh := makeJWTForTest(map[string]any{"exp": float64(now.Add(time.Hour).Unix())})
|
|
|
|
// Access within grace (needs refresh), ID still fresh: must NOT skip.
|
|
if skipRefreshForTokens(accessWithinGrace, idFresh, grace) {
|
|
t.Fatal("access token inside the grace window must trigger refresh even when the ID token is fresh")
|
|
}
|
|
|
|
// Access far outside grace: should skip.
|
|
if !skipRefreshForTokens(accessFresh, idFresh, grace) {
|
|
t.Fatal("access token outside the grace window should skip the refresh")
|
|
}
|
|
|
|
// Opaque access token: fall back to the ID token (safe direction).
|
|
if !skipRefreshForTokens("opaque-access-token", idFresh, grace) {
|
|
t.Fatal("opaque access token should fall back to the fresh ID token and skip")
|
|
}
|
|
}
|