Skip to content

Add Redis backend for DCRCredentialStore#5195

Draft
tgrunnagle wants to merge 1 commit intodcr-3a_issue_5183from
dcr-3b_issue_5184
Draft

Add Redis backend for DCRCredentialStore#5195
tgrunnagle wants to merge 1 commit intodcr-3a_issue_5183from
dcr-3b_issue_5184

Conversation

@tgrunnagle
Copy link
Copy Markdown
Contributor

@tgrunnagle tgrunnagle commented May 5, 2026

DRAFT - not ready for review

Summary

An authserver replica that registers itself as a DCR client against an upstream
authorization server currently keeps the resulting (client_id, client_secret)
in the in-process MemoryStorage from sub-issue 1. Restarts and horizontal
scale-outs lose the registration, forcing every replica to re-register on cold
start and breaking RFC 7592 management URLs. This PR adds the persistent half
of DCRCredentialStore so a Redis-Sentinel-backed authserver shares DCR
credentials across replicas and survives restarts.

  • Add KeyTypeDCR and a length-prefixed redisDCRKey(prefix, DCRKey) helper
    that handles colons in RedirectURI and Issuer without ambiguity, mirroring
    the existing redisProviderKey shape.
  • Add RedisStorage.StoreDCRCredentials / GetDCRCredentials with JSON
    serialisation (acting as a defensive copy on read) and TTL semantics derived
    from client_secret_expires_at: zero means no Redis TTL (RFC 7591 "never"),
    a future expiry uses time.Until(expiry), and an already-past expiry is
    bounded to a 1s eviction window so already-dead secrets do not linger.
  • Wire a compile-time _ DCRCredentialStore = (*RedisStorage)(nil) assertion
    alongside the existing interface checks at the bottom of redis.go.
  • Add miniredis-backed unit tests covering key distinctness/determinism, full
    round-trip, overwrite, validation, defensive copy via decode, all three TTL
    branches, ErrNotFound semantics, and concurrent Store / Get.
  • Add Redis Sentinel integration tests (testcontainers, //go:build integration)
    pinning the wire-level TTL contract against real Redis (-1 for never-expires)
    plus round-trip, distinct-keys, overwrite, and concurrent access — extending
    redis_integration_test.go rather than introducing a second harness.

Closes #5184

Type of change

  • New feature

Test plan

  • Unit tests (task test)
  • Linting (task lint-fix)
  • Manual testing (describe below)

Ran the integration suite locally against a Docker-backed Redis Sentinel
cluster: go test -tags=integration ./pkg/authserver/storage/... (TTL,
round-trip, distinct-keys, overwrite, and concurrent-access cases all pass,
including the wire-level TTL == -1 assertion for never-expires rows).

API Compatibility

  • This PR does not break the v1beta1 API, OR the api-break-allowed label is applied and the migration guidance is described above.

Changes

File Change
pkg/authserver/storage/redis_keys.go Add KeyTypeDCR const and redisDCRKey length-prefixed key helper.
pkg/authserver/storage/redis.go Add storedDCRCredentials wire type, StoreDCRCredentials / GetDCRCredentials, pastExpiryDCRTTL bound, and DCRCredentialStore interface assertion.
pkg/authserver/storage/redis_test.go Add miniredis unit coverage: key encoding, round-trip, overwrite, validation, defensive copy, TTL (never / future / past), ErrNotFound, concurrent access.
pkg/authserver/storage/redis_integration_test.go Add Redis Sentinel integration coverage extending withIntegrationStorage: round-trip, distinct keys, overwrite, real-Redis TTL, concurrent access.

Does this introduce a user-facing change?

No. This is internal storage plumbing behind the DCRCredentialStore interface
introduced in sub-issue 1. Sub-issue 3 will wire EmbeddedAuthServer to select
the Redis backend via the existing storage_type: redis config toggle.

Special notes for reviewers

  • This branch went through two review iterations. The final state has zero
    CRITICAL / HIGH / MEDIUM findings. One LOW finding remains: a stale docstring
    on a unit-test helper in redis_test.go that still references the
    pre-pastExpiryDCRTTL behaviour. Happy to fix in this PR or as a follow-up
    if reviewers prefer to keep this PR focused on the storage primitive.
  • Past-expiry TTL behaviour is deliberately 1s rather than rejecting the
    write or storing long-lived. Rationale is in the pastExpiryDCRTTL constant
    doc comment and the StoreDCRCredentials docstring: caller's expiry
    timestamp still round-trips so a downstream reader can observe it and trigger
    re-registration, while the row self-evicts almost immediately. Worth a look
    during review to confirm the policy matches how sub-issue 3's resolver will
    consume it.
  • Unit tests use miniredis (already in go.mod from the surrounding
    redis_test.go suite). Integration tests use the existing testcontainers
    Redis Sentinel harness — both layers are required: the unit layer pins
    in-process semantics for task test, and the integration layer pins the
    wire contract (TTL returns -1 for "no TTL") that miniredis cannot
    faithfully reproduce.
  • No new dependencies were introduced.

@tgrunnagle tgrunnagle changed the base branch from main to dcr-3a_issue_5183 May 5, 2026 14:35
@github-actions github-actions Bot added the size/L Large PR: 600-999 lines changed label May 5, 2026
@tgrunnagle tgrunnagle force-pushed the dcr-3a_issue_5183 branch from c0fed52 to cc92472 Compare May 5, 2026 15:50
@tgrunnagle tgrunnagle force-pushed the dcr-3a_issue_5183 branch from cc92472 to 7aa7366 Compare May 6, 2026 14:33
@tgrunnagle tgrunnagle force-pushed the dcr-3b_issue_5184 branch from 43ead51 to b0bf320 Compare May 6, 2026 17:17
@github-actions github-actions Bot added size/L Large PR: 600-999 lines changed and removed size/L Large PR: 600-999 lines changed labels May 6, 2026
@codecov
Copy link
Copy Markdown

codecov Bot commented May 6, 2026

Codecov Report

❌ Patch coverage is 90.78947% with 7 lines in your changes missing coverage. Please review.
✅ Project coverage is 67.80%. Comparing base (409a66e) to head (b0bf320).

Files with missing lines Patch % Lines
pkg/authserver/storage/redis.go 90.00% 4 Missing and 3 partials ⚠️
Additional details and impacted files
@@                  Coverage Diff                  @@
##           dcr-3a_issue_5183    #5195      +/-   ##
=====================================================
+ Coverage              67.72%   67.80%   +0.08%     
=====================================================
  Files                    607      607              
  Lines                  62090    62166      +76     
=====================================================
+ Hits                   42049    42151     +102     
+ Misses                 16878    16849      -29     
- Partials                3163     3166       +3     

☔ View full report in Codecov by Sentry.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

size/L Large PR: 600-999 lines changed

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Persistent DCRCredentialStore: Redis backend

1 participant