mirror of
https://github.com/lukaszraczylo/traefikoidc.git
synced 2026-10-18 04:47:28 +00:00
Fix round-36 issues: DCR validation, auth-method default, shared metadata task, replay false-positive
Four verified fixes from the round-36 agent team (each confirmed against source before acting): - settings.go: Config.Validate now skips the client-credential block when Dynamic Client Registration is enabled and no clientID is pre-provisioned (RFC 7591 provides credentials at registration). Previously the unconditional 'clientID is required' made DCR unreachable via New. - dynamic_client_registration.go: default registered token_endpoint_auth_method to client_secret_post to match the plugin's runtime default; an enforcing IdP would otherwise reject the plugin's token exchange. - utilities.go: gate the shared per-provider singleton-metadata-refresh task stop on lastInstance (matching singleton-token-cleanup) so one instance's tear down no longer kills 2h metadata refresh for a surviving sibling. - token_manager.go: stop self-recording the JTI into the per-instance tokenBlacklist on every verify. On the cookie path the same valid token is re-presented per request; an LRU eviction of the raw-token cache (MaxSize 1000 / 5 MiB) then turned a legitimate cache miss into a false 'token replay detected'. Replay tracking stays in the shared shardedReplayCache. Regression tests: new review_r36_regression_test.go (4 tests); updated dynamic_client_registration_test.go and security_edge_cases_test.go which had encoded the old defaults. Verified: full source-order suite, full -race -count=1 -shuffle=on ./... (all packages), and golangci-lint run ./... (0 issues).
This commit is contained in:
+17
-20
@@ -359,29 +359,26 @@ func TestJWTReplayAttack(t *testing.T) {
|
||||
t.Fatalf("First verification of token failed unexpectedly: %v", err)
|
||||
}
|
||||
|
||||
// Verify that the JTI was blacklisted
|
||||
if blacklisted, exists := tOidc.tokenBlacklist.Get(fixedJTI); !exists || blacklisted == nil {
|
||||
t.Fatalf("JTI was not added to blacklist after first verification")
|
||||
// Replay tracking for this path lives in the shared shardedReplayCache
|
||||
// (see verifyTokenWithOpts); the per-instance tokenBlacklist holds only
|
||||
// external revocations and is intentionally NOT self-marked.
|
||||
if !shardedReplayCache.Exists(fixedJTI) {
|
||||
t.Fatalf("JTI was not recorded in the shared replay cache after first verification")
|
||||
}
|
||||
if blacklisted, exists := tOidc.tokenBlacklist.Get(fixedJTI); exists && blacklisted != nil {
|
||||
t.Errorf("per-instance tokenBlacklist must not self-record the JTI (would cause false replay after cache eviction)")
|
||||
}
|
||||
|
||||
// Since there's a special bypass for tokens starting with the test JWT prefix,
|
||||
// we need to test with a direct check of the blacklisted JTI instead
|
||||
|
||||
// Directly verify that a replay would be caught by checking the blacklist
|
||||
if blacklisted, exists := tOidc.tokenBlacklist.Get(fixedJTI); !exists || blacklisted == nil {
|
||||
t.Errorf("JTI was not properly blacklisted for replay protection")
|
||||
// Real replay detection still works: presenting the same token through the
|
||||
// replay-checking path must be flagged as a replay.
|
||||
replayed, err := parseJWT(replayJWT)
|
||||
if err != nil {
|
||||
t.Fatalf("failed to parse replayed JWT: %v", err)
|
||||
}
|
||||
|
||||
// Also verify our JTI replay detection function directly
|
||||
claims, _ := extractClaims(replayJWT)
|
||||
if claims != nil {
|
||||
if jti, ok := claims["jti"].(string); ok && jti != "" {
|
||||
if blacklisted, exists := tOidc.tokenBlacklist.Get(jti); exists && blacklisted != nil {
|
||||
t.Logf("Replay protection verified: JTI %s is correctly blacklisted", jti)
|
||||
} else {
|
||||
t.Errorf("JTI %s was not found in blacklist", jti)
|
||||
}
|
||||
}
|
||||
if err := replayed.Verify("https://test-issuer.com", "test-client-id", false); err == nil {
|
||||
t.Error("expected replay to be detected via the shared replay cache")
|
||||
} else if !strings.Contains(err.Error(), "token replay detected") {
|
||||
t.Errorf("expected 'token replay detected', got: %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user