Skip to content

fix: webhook secret rotation 24h default#951

Merged
alexluong merged 2 commits into
mainfrom
fix/rotation-invalid-at-default
Jun 16, 2026
Merged

fix: webhook secret rotation 24h default#951
alexluong merged 2 commits into
mainfrom
fix/rotation-invalid-at-default

Conversation

@alexluong

Copy link
Copy Markdown
Collaborator

Rotating a webhook secret without previous_secret_invalid_at is documented to default the window to now+24h, but the default only ever applied to a destination's first rotation. The Update handler merge-patches stored credentials into the destination before Preprocess, so on later rotations rotateSecret mistook the stored timestamp for a caller-provided one and carried it forward — once that timestamp was in the past, every plain rotation produced an already-expired previous secret, which was then stripped from API responses.

Fixes it by passing the caller's raw config/credentials through PreprocessDestinationOpts.Request, so providers distinguish "caller sent this field" from "merged from storage". Both webhook providers updated (the rotation logic is duplicated).

🤖 Generated with Claude Code

alexluong and others added 2 commits June 12, 2026 15:03
Extends the rotation test: rotate with an explicit
previous_secret_invalid_at, then rotate again without one. The second
rotation must default to now+24h, but the stored window is carried
forward instead — the Update handler merge-patches stored credentials
into the request before Preprocess, so rotateSecret mistakes the stored
timestamp for a caller-provided one. Once that timestamp is in the
past, every plain rotation produces an already-expired previous secret.

Failing test; fix follows.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…t, not merged state

The Update handler merge-patches stored credentials into the
destination before Preprocess, so on any rotation after the first
rotateSecret saw the stored previous_secret_invalid_at as
caller-provided and skipped the documented now+24h default. Once the
stored timestamp was in the past, every subsequent plain rotation
produced an already-expired previous secret, which
ObfuscateDestination then stripped from API responses.

Give Preprocess the caller's view of the provider-owned fields via
PreprocessDestinationOpts.Request (config and credentials exactly as
sent, before merging), and have rotateSecret read the custom
invalidation window from there instead of inferring intent from the
merged map. Applies to both webhook providers (legacy and standard),
which duplicate the rotation logic.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@alexluong alexluong merged commit 25ddeea into main Jun 16, 2026
3 checks passed
@alexluong alexluong deleted the fix/rotation-invalid-at-default branch June 16, 2026 04:32
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