Skip to content

grpc: refresh trusted headers from continuous auth responses - #19

Merged
maoueh merged 3 commits into
streamingfast:developfrom
matthewdarwin:continuous-auth-refresh-headers
Oct 9, 2026
Merged

maoueh merged 3 commits into
streamingfast:developfrom
matthewdarwin:continuous-auth-refresh-headers

Conversation

@matthewdarwin

Copy link
Copy Markdown
Contributor

With continuous=true, the grpc plugin calls Authenticate again on every interval, but it only looks at the error. The response is discarded, so a stream's trusted headers are whatever the first call returned.

If that first call returns an empty success, the stream keeps no x-user-id/x-meta for its whole lifetime, even after later continuous calls return the full headers. An authenticator answers with an empty success when it chooses to fail open, for example a local sidecar that cannot reach its own upstream within its deadline. Metering then emits events without identity until the client disconnects. On long-lived Substreams/Firehose streams that can be hours of usage.

Change

  • continuousAuth merges each response's headers into the stream's trusted headers. Keys are added or updated, never removed. An empty response is a no-op, and an unchanged one skips the swap.
  • The headers are stored in an atomic.Pointer holder on the context. The new ReplaceTrustedHeaders(ctx, h) swaps them, and FromContext returns the current snapshot. Metering reads the headers concurrently with the refresh, so the map is not mutated in place.
  • FromContext's signature and return type are unchanged. Callers that copy values once at stream start keep the old values; to pick up refreshed headers they need to read them per event. Firehose already does this (firehose/server/server.go); Substreams tier1 does not, see the linked PR.

No extra calls: the continuous requests already happen; only their responses are now used.

Tests

  • TestReplaceTrustedHeaders: derived contexts see the replacement, and earlier snapshots are unchanged.
  • TestAuthenticatorPlugin_ContinuousAuthenticate_RefreshesHeaders: the first call returns empty and the second returns headers, which then appear on the context. Later empty responses do not remove them. The test reads concurrently with the refresh.
  • go test -race -count=50 ./ ./grpc/ passes. middleware/connect's Test_validAuth is flaky on develop without this change (it fails intermittently with "987" != "123").

🤖 Generated with Claude Code

continuousAuth discarded every response after the first, so the trusted
headers of a stream were fixed by its first Authenticate call. When that
call returned an empty success (e.g. an authenticator answering without
its own upstream), the stream ran without x-user-id/x-meta for its whole
lifetime even though later continuous calls returned them.

Each continuous response is now merged into the stream's trusted
headers: keys are added or updated, never removed. The headers live in
an atomic holder on the context, and the new ReplaceTrustedHeaders swaps
them; FromContext returns the current snapshot.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
@matthewdarwin
matthewdarwin force-pushed the continuous-auth-refresh-headers branch from 50f2ca2 to 5b7df03 Compare October 7, 2026 12:49
@matthewdarwin
matthewdarwin marked this pull request as ready for review October 7, 2026 12:54
@dfuse-bot
dfuse-bot requested a review from billettc October 7, 2026 12:55
@billettc
billettc requested review from maoueh and removed request for billettc October 8, 2026 11:43
…auth tests

Document that FromContext's map must not be modified, that a nested
WithTrustedHeaders shadows refreshes, and that removal is not handled
by continuous auth refresh. Bump to Go 1.25 so continuous auth tests
run under testing/synctest instead of real-time sleeps.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@maoueh
maoueh merged commit 06c0631 into streamingfast:develop Oct 9, 2026
3 checks passed
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