Skip to content

[management] use the cache for the pkce state#5516

Merged
pascal-fischer merged 3 commits intomainfrom
feature/cache-for-pkce-state
Mar 9, 2026
Merged

[management] use the cache for the pkce state#5516
pascal-fischer merged 3 commits intomainfrom
feature/cache-for-pkce-state

Conversation

@pascal-fischer
Copy link
Copy Markdown
Collaborator

@pascal-fischer pascal-fischer commented Mar 5, 2026

Describe your changes

Issue ticket number and link

Stack

Checklist

  • Is it a bug fix
  • Is a typo/documentation fix
  • Is a feature enhancement
  • It is a refactor
  • Created tests that fail without the change (if possible)

By submitting this pull request, you confirm that you have read and agree to the terms of the Contributor License Agreement.

Documentation

Select exactly one:

  • I added/updated documentation for this change
  • Documentation is not needed for this change (explain why)

Docs PR URL (required if "docs added" is checked)

Paste the PR link from https://github.com/netbirdio/docs here:

https://github.com/netbirdio/docs/pull/__

Summary by CodeRabbit

  • New Features

    • Added a PKCE verifier store for OAuth flows with configurable backends (in-memory or Redis), TTL, and automatic cleanup.
  • Refactor

    • Replaced ad-hoc in-memory PKCE handling with a centralized PKCE store, updating proxy initialization to use the store for state storage and validation for more robust, single-use PKCE handling.

@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai Bot commented Mar 5, 2026

📝 Walkthrough

Walkthrough

This PR introduces a PKCEVerifierStore (in-memory/Redis-backed), wires it into server bootstrap and ProxyServiceServer, removes the per-server pkce sync.Map and cleanup goroutine, and updates all constructor call sites and tests to pass and use the new store.

Changes

Cohort / File(s) Summary
PKCE Verifier Store Implementation
management/internals/shared/grpc/pkce_verifier.go
Adds PKCEVerifierStore, NewPKCEVerifierStore(ctx, maxTimeout, cleanupInterval, maxConn), Store() and LoadAndDelete() methods with cache backend (in-memory or Redis) and TTL semantics.
ProxyServiceServer & PKCE wiring
management/internals/shared/grpc/proxy.go
Adds pkceVerifierStore *PKCEVerifierStore field, updates NewProxyServiceServer signature to accept pkceStore, removes sync.Map + cleanup goroutine and Close(), and updates GetOIDCURL() / ValidateState() to use the store.
Server bootstrap
management/internals/server/boot.go
Adds BaseServer.PKCEVerifierStore() initializer (10m TTL, cleanup interval, cap) and passes PKCE store into nbgrpc.NewProxyServiceServer() call.
Constructor call sites & test setups
management/internals/modules/reverseproxy/service/manager/manager_test.go, management/internals/shared/grpc/validate_session_test.go, management/server/account_test.go, management/server/http/handlers/proxy/auth_callback_integration_test.go, management/server/http/testing/testing_tools/channel/channel.go, proxy/management_integration_test.go, management/internals/shared/grpc/proxy_test.go
Update NewProxyServiceServer() calls to include the new pkceStore argument; add NewPKCEVerifierStore() creation in tests and integration setups; adjust test code to use the store.
Shutdown behavior
management/internals/server/server.go
Removes explicit s.ReverseProxyGRPCServer().Close() from Stop() to reflect removal of per-server PKCE cleanup lifecycle.

Sequence Diagram(s)

sequenceDiagram
    participant Client as OAuth Client
    participant Proxy as ProxyServiceServer
    participant Store as PKCEVerifierStore
    participant Cache as Cache Backend (Redis / In-Memory)
    participant IDP as Identity Provider

    Client->>Proxy: GetOIDCURL request
    Proxy->>Proxy: generate code_verifier & state
    Proxy->>Store: Store(state, verifier, ttl)
    Store->>Cache: Set(state -> verifier)
    Cache-->>Store: OK
    Store-->>Proxy: Store OK
    Proxy-->>Client: Redirect URL with state

    Client->>IDP: Authorize with code_challenge
    IDP-->>Client: Authorization code + state
    Client->>Proxy: Callback (state, code)
    Proxy->>Store: LoadAndDelete(state)
    Store->>Cache: Get & Delete state
    Cache-->>Store: verifier
    Store-->>Proxy: verifier (atomically deleted)
    Proxy->>Proxy: verify code_challenge, exchange code
    Proxy-->>Client: Session established
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45 minutes

Possibly related PRs

Suggested reviewers

  • mlsmaycon
  • bcmmbaga

Poem

🐰 I hopped and hid a secret key,

Stored it safe for you and me.
Redis hums or memory sings,
PKCE bound to tighter strings.
Hoppity-hop — the tests take wing. ✨

🚥 Pre-merge checks | ✅ 1 | ❌ 2

❌ Failed checks (2 warnings)

Check name Status Explanation Resolution
Description check ⚠️ Warning The PR description is incomplete; the 'Describe your changes' section is empty, providing no details about the implementation, rationale, or affected components. Fill in the 'Describe your changes' section with details about the PKCEVerifierStore implementation, its integration points, and why this change improves the system.
Docstring Coverage ⚠️ Warning Docstring coverage is 27.78% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (1 passed)
Check name Status Explanation
Title check ✅ Passed The title clearly summarizes the main change: introducing cache-backed PKCE state storage to replace the in-memory map implementation.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
  • 📝 Generate docstrings (stacked PR)
  • 📝 Generate docstrings (commit on current branch)
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch feature/cache-for-pkce-state

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

🧹 Nitpick comments (1)
management/server/account_test.go (1)

3136-3136: Avoid nil PKCE store in shared test setup

Using nil here makes createManager brittle—any future test path touching OIDC/PKCE can panic. Prefer constructing a real PKCE store in this helper, like other test setups in this PR.

♻️ Suggested change
-	proxyGrpcServer := nbgrpc.NewProxyServiceServer(nil, nil, nil, nbgrpc.ProxyOIDCConfig{}, peersManager, nil, proxyManager)
+	pkceStore, err := nbgrpc.NewPKCEVerifierStore(ctx, 10*time.Minute, 10*time.Minute, 100)
+	if err != nil {
+		return nil, nil, err
+	}
+	proxyGrpcServer := nbgrpc.NewProxyServiceServer(nil, nil, pkceStore, nbgrpc.ProxyOIDCConfig{}, peersManager, nil, proxyManager)
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@management/server/account_test.go` at line 3136, The shared test setup passes
a nil PKCE store to nbgrpc.NewProxyServiceServer which makes createManager
brittle and can cause panics when OIDC/PKCE paths are exercised; instead
instantiate the real PKCE store used elsewhere in the PR and pass it into
nbgrpc.NewProxyServiceServer (replace the nil PKCE store argument) so tests use
a concrete PKCE store; update the helper createManager (and its call sites if
needed) to construct and supply that store alongside ProxyOIDCConfig,
peersManager and proxyManager.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@management/internals/shared/grpc/pkce_verifier.go`:
- Around line 49-60: The current PKCEVerifierStore.LoadAndDelete uses separate
cache.Get and cache.Delete calls (cache.Get, cache.Delete on s.ctx) so
concurrent callers can both read the verifier before deletion and delete errors
are logged but still return success; change LoadAndDelete to perform an atomic
get-and-delete (use a cache.GetAndDelete / GetDelete method if the cache
supports it) or, if not supported, serialize access per state (e.g., a per-key
mutex map inside PKCEVerifierStore) so only one goroutine can call Get then
Delete for a given state, and treat delete failures as fatal by returning ("",
false) instead of true (also update log messages to include error context).

---

Nitpick comments:
In `@management/server/account_test.go`:
- Line 3136: The shared test setup passes a nil PKCE store to
nbgrpc.NewProxyServiceServer which makes createManager brittle and can cause
panics when OIDC/PKCE paths are exercised; instead instantiate the real PKCE
store used elsewhere in the PR and pass it into nbgrpc.NewProxyServiceServer
(replace the nil PKCE store argument) so tests use a concrete PKCE store; update
the helper createManager (and its call sites if needed) to construct and supply
that store alongside ProxyOIDCConfig, peersManager and proxyManager.

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: d002198f-e5b1-4dda-a597-bdbc3ecfb340

📥 Commits

Reviewing files that changed from the base of the PR and between 4f0a3a7 and 78d513d.

📒 Files selected for processing (9)
  • management/internals/modules/reverseproxy/service/manager/manager_test.go
  • management/internals/server/boot.go
  • management/internals/shared/grpc/pkce_verifier.go
  • management/internals/shared/grpc/proxy.go
  • management/internals/shared/grpc/validate_session_test.go
  • management/server/account_test.go
  • management/server/http/handlers/proxy/auth_callback_integration_test.go
  • management/server/http/testing/testing_tools/channel/channel.go
  • proxy/management_integration_test.go

Comment thread management/internals/shared/grpc/pkce_verifier.go
Comment thread management/internals/shared/grpc/proxy.go
@sonarqubecloud
Copy link
Copy Markdown

sonarqubecloud Bot commented Mar 5, 2026

Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🧹 Nitpick comments (1)
management/internals/shared/grpc/proxy_test.go (1)

96-101: Use cancellable contexts in tests for store lifecycle cleanup.

These tests pass context.Background() into store constructors. If those stores run cleanup goroutines, they cannot be stopped per-test, which can accumulate background work across the suite.

♻️ Suggested change pattern
- ctx := context.Background()
+ ctx, cancel := context.WithCancel(context.Background())
+ t.Cleanup(cancel)

  tokenStore, err := NewOneTimeTokenStore(ctx, time.Hour, 10*time.Minute, 100)
  require.NoError(t, err)

Apply the same pattern in tests that only create pkceStore.

Also applies to: 158-163, 197-202, 258-260, 287-289, 308-310

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@management/internals/shared/grpc/proxy_test.go` around lines 96 - 101,
Replace uses of context.Background() when constructing stores (e.g.,
NewOneTimeTokenStore and NewPKCEVerifierStore) with a per-test cancellable
context created via ctx, cancel := context.WithCancel(context.Background()),
pass ctx into the store constructor, and defer cancel() at the end of the test
so any cleanup goroutines are stopped; apply the same pattern for all tests that
only create pkceStore or similar stores so each test controls store lifecycle
and avoids accumulating background work.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Nitpick comments:
In `@management/internals/shared/grpc/proxy_test.go`:
- Around line 96-101: Replace uses of context.Background() when constructing
stores (e.g., NewOneTimeTokenStore and NewPKCEVerifierStore) with a per-test
cancellable context created via ctx, cancel :=
context.WithCancel(context.Background()), pass ctx into the store constructor,
and defer cancel() at the end of the test so any cleanup goroutines are stopped;
apply the same pattern for all tests that only create pkceStore or similar
stores so each test controls store lifecycle and avoids accumulating background
work.

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 03572854-b203-450c-be38-1f07ae064832

📥 Commits

Reviewing files that changed from the base of the PR and between 603aba3 and 745f51e.

📒 Files selected for processing (1)
  • management/internals/shared/grpc/proxy_test.go

@pascal-fischer pascal-fischer merged commit 30c02ab into main Mar 9, 2026
45 checks passed
@pascal-fischer pascal-fischer deleted the feature/cache-for-pkce-state branch March 9, 2026 11:23
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants