You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
Commit d61139f
Browse filesBrowse the repository at this point in the historyBrowse files
fix(client): a bearer-mode ObjectStackClient adopts the session token the three rotating auth routes hand it (#17182)
* fix(client): adopt the rotated session token on the three rotating auth routes
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_015QE8qk46e5CHJxyQEUjbf8
* test(client): drive the rotating auth routes through the real better-auth pipeline
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_015QE8qk46e5CHJxyQEUjbf8
* test(client): register the identity objects AuthManager's boot path reads
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_015QE8qk46e5CHJxyQEUjbf8
* docs(changeset): declare the rotated-session-token adoption
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_015QE8qk46e5CHJxyQEUjbf8
* build(client): resolve the suite's two new producers from source in tsc too
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_015QE8qk46e5CHJxyQEUjbf8
* test(client): classify the new credential helper in the URL-conformance sweep
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_015QE8qk46e5CHJxyQEUjbf8
* test(client): open the ① probe on `login`, the way the card's own probe opens
The card's end-to-end probe is `login -> enable -> verifyTotp -> disable ->
deleteUser`. The suite opened on the registration that had to precede that
login, which is the same credential state by construction but not the same
line. Spell the first step as the card spells it: a real second sign-in whose
echoed token the SDK stores, asserted against the stored credential before
the sequence continues.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_015QE8qk46e5CHJxyQEUjbf8
---------
Co-authored-by: Claude <noreply@anthropic.com>
feat(client): a bearer-mode `ObjectStackClient` keeps the session the server rotates it onto (#16534)
6
+
7
+
Three better-auth routes ROTATE the caller's session on success — they mint a new session, install it in `Set-Cookie` (and, through `bearer()`, in the `set-auth-token` response header), and DELETE the row the caller was presenting:
8
+
9
+
| route | where the new credential is |
10
+
| --- | --- |
11
+
|`auth.twoFactor.verifyTotp()` on the enrolment lane | body — `token`, and it is the LIVE one (plugin-auth's `two-factor-rotated-token-echo` repairs the vendor's stale echo) |
12
+
|`auth.changePassword({ revokeOtherSessions: true })`| body — `token`|
13
+
|`auth.twoFactor.disable()`|**response header only** — the body is `{ status: true }`|
14
+
15
+
A browser is carried across all three by its own cookie. A bearer client — this SDK's own mode — kept presenting the DELETED session's token, so its very next call answered `401 UNAUTHORIZED`. Measured against a real `AuthManager` (better-auth 1.7.2) over a real driver, driven through the real `ObjectStackClient`, `login → enable → verifyTotp → disable → deleteUser` could not run to the end without the caller re-seating `client.token` by hand between the steps.
16
+
17
+
The three methods now adopt the rotated credential themselves, the way `login()` already adopts the token it is handed. The `token` members stay on the wire and stay declared, so a caller that keeps its own credential store is unaffected; what changes is that it no longer has to.
18
+
19
+
**No public surface moves.** No new export, no new option or flag, no new key on any declared request or response type — the SDK stores a token the server already sends and this package already declares. Graded `minor` rather than `patch` because the published runtime behaviour of three methods moves for existing callers.
20
+
21
+
## What does NOT change, deliberately
22
+
23
+
The adoption is on those three routes only, never in the shared `fetch` wrapper. `set-auth-token` rides **every** response that stages a session cookie — `POST /update-user` stages one to carry the updated user without rotating anything — and it carries the SIGNED `<token>.<sig>` spelling while every JSON `token` echo carries the UNSIGNED one. A wrapper-level read would therefore rewrite the stored credential into a different spelling of the SAME session on ordinary traffic. `auth.me()`, `auth.sessions.list()`, `auth.updateUser()` and `auth.twoFactor.verifyBackupCode()` (which does not rotate — the vendor echoes the session it resolved at entry) all leave the stored credential byte-identical, and that is pinned.
24
+
25
+
A cookie-only deployment sends no `set-auth-token`; there is then nothing to adopt and `twoFactor.disable()` leaves the stored credential exactly as it was. `changePassword` without `revokeOtherSessions` answers `token: null` and likewise stores nothing.
26
+
27
+
The three TSDoc warnings that told bearer callers "this SDK does not store it" are updated in the same change.
0 commit comments