Skip to content

fix(auth): keep a static client's SEP-2352 issuer stamp across saves and restarts - #2794

Merged
cliffhall merged 2 commits into
v2/mainfrom
v2/fix/2751-static-client-issuer-stamp
Oct 25, 2026
Merged

cliffhall merged 2 commits into
v2/mainfrom
v2/fix/2751-static-client-issuer-stamp

Conversation

@cliffhall

Copy link
Copy Markdown
Member

Closes #2751

Problem

For a static (preregistered) OAuth client, the SDK's SEP-2352 issuer back-stamp, saveClientInformation(info, { issuer }), went through resolveSdkRegistrationKind into a per-issuer dynamic slot. clientInformation() reads the preregistered slot first and never looks at that dynamic slot. On top of that, re-seeding the static client at startup overwrote the preregistered slot. So the binding never persisted, and the SDK printed its unstamped-credential warning on every run.

Fix (core/auth/)

  • Back-stamp lands on the static client. If an SDK save (one carrying no registrationKind) has a client_id matching the stored preregistered client, the provider writes { ...preregistered, issuer } to the preregistered slot instead (backStampPreregistered).
  • The preregistered read returns the stamp. OAuthClientInformationSchema strips issuer on parse, and a static client has no byIssuer key to recover the stamp from. So OAuthStorageBase re-attaches the stamp stored on the value itself. The secret split already keeps issuer in the plaintext residue.
  • Re-seeding keeps the stamp unless the client changed. savePreregisteredClientInformation carries an existing stamp over while client_id and client_secret are unchanged. A changed client starts unstamped.
  • AS migration behaves as before. A static client is configured per server, not per AS. If the stamp names a different issuer than ctx.issuer, the client is handed back unstamped, so the SDK re-binds it rather than discarding it. The existing SEP-2352 AS migration e2e test still passes on this path.

savePreregisteredClientInformation's parameter and the provider's saveClientInformation are now typed StoredOAuthClientInformation, the SDK's stamped type. That is what both actually receive.

Tests

clients/web/src/test/core/auth/providers.test.ts, under static client issuer stamp:

  • Unit cases cover:
    • the back-stamp is routed to the preregistered slot
    • a different client_id stays in its own slot
    • our own explicit-kind saves are untouched
    • matching and mismatched stamps on read
    • re-seed keeps the stamp, drops it on a changed client_id or client_secret, and passes an unstamped client through unchanged
  • The regression the issue asks for, end to end. This test uses real OAuthStorageBase, the real startup seed and the SDK's own auth(). The first run warns once and stamps; a simulated restart (new provider, same storage, re-seed) finds the stamp on the preregistered client, nothing in the dynamic slot, and zero SEP-2352 warnings.
  • A companion case re-resolves discovery to a second issuer and checks the static client gets re-bound to it.

Five of the new tests fail against the unfixed core/ and pass with it. One existing assertion ("nothing is read at all") was narrowed to "no registration slot is read", since an SDK save now checks the preregistered slot.

npm run local:gate passes. No UI change, so no screenshots.

🤖 Generated with Claude Code

…and restarts (#2751)

The SDK back-stamps the preregistered client via
saveClientInformation(info, { issuer }), which routed into a per-issuer
dynamic slot that the preregistered read never consults, and re-seeding
the static client at startup overwrote the slot anyway. The binding was
lost and the SDK warned on every run.

- Route an SDK save whose client_id matches the preregistered client to
  the preregistered slot, and return the stamp from the preregistered
  read (it has no byIssuer key to recover it from).
- Re-seeding carries the stamp over while client_id and client_secret
  are unchanged; a changed client starts unstamped.
- A stamp naming a different issuer is handed back unstamped, so the
  static client is re-bound rather than discarded on an AS migration,
  as before.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: cliffhall <cliff@futurescale.com>
@cliffhall cliffhall added the v2 Issues and PRs for v2 label Oct 25, 2026
@cliffhall
cliffhall requested a balanced review from Copilot October 25, 2026 02:14

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🟡 Changes recommended

The restart regression test must reload a persisted snapshot through fresh storage rather than reuse in-memory state.

1 open issue
What changed in this PR

Fixes #2751 by preserving a static OAuth client’s SEP-2352 issuer stamp in Inspector’s shared authentication layer.

Changes:

  • Routes SDK back-stamps to the preregistered client and restores stamps on reads.
  • Preserves stamps when re-seeding unchanged credentials while retaining authorization-server migration behavior.
  • Adds provider and SDK authorization regression tests.
File Description
core/​auth/​storage.ts Accepts stamped preregistered client information in the storage contract.
core/​auth/​providers.ts Routes back-stamps, preserves stamps during re-seeding, and handles issuer changes.
core/​auth/​oauth-storage.ts Restores the stored issuer stamp after schema parsing.
clients/​web/​src/​test/​core/​auth/​providers.test.ts Adds static-client stamp and SDK authorization regression coverage.

Review effort: Balanced

Comment thread clients/web/src/test/core/auth/providers.test.ts Outdated
…gression (#2751)

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: cliffhall <cliff@futurescale.com>
@cliffhall
cliffhall requested a balanced review from Copilot October 25, 2026 02:31
@cliffhall

Copy link
Copy Markdown
Member Author

Copilot round 1: one finding, fixed.

  • Test restart persistence with fresh storage and memory — fixed in 48c5e03. The restart regression now reloads a JSON snapshot into fresh storage and memory for each run.

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🟢 Approval recommended

The focused changes address the reported stamp-loss paths, and the restart regression now covers serialization and reload with no unresolved findings.

No open issues 🎉

1 resolved since last review

Review effort: Balanced

@cliffhall
cliffhall merged commit 5157cdb into v2/main Oct 25, 2026
11 checks passed
@cliffhall
cliffhall deleted the v2/fix/2751-static-client-issuer-stamp branch October 25, 2026 02:57
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

v2 Issues and PRs for v2

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Static OAuth client loses its SEP-2352 issuer stamp across SDK saves and restarts

2 participants