fix(auth): route clearClientInformation through clearAfterHydration too
Round-2 @claude review of #1592 caught a seventh member of the clear family that the first pass missed: clearClientInformation still wrote to the store synchronously, carrying the same pre-hydration clobber risk the clearAfterHydration helper was written for (a clear before hydration lands persists a near-empty blob, overwriting the whole on-disk oauth.json). Wrap its mutation the same way and add a clear-before-hydration test proving a sibling token (and thus every other server's blob) survives. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01S3fTN8H3R8YV4yUGvZjYnX
This commit is contained in:
parent
8c10b8eb8e
commit
4e149719bd
2 changed files with 32 additions and 10 deletions
|
|
@ -172,6 +172,28 @@ describe("OAuthStorageBase — async hydration", () => {
|
|||
expect(await storage.getCodeVerifier(SERVER)).toBe("keep-me");
|
||||
});
|
||||
|
||||
it("clearClientInformation before hydration is deferred and clears only client info", async () => {
|
||||
const { storage, release } = makeAsyncBackedStorage({
|
||||
[SERVER]: {
|
||||
clientInformation: { client_id: "abc" },
|
||||
clientRegistrationKind: "dynamic",
|
||||
tokens: { access_token: "t", token_type: "Bearer" },
|
||||
},
|
||||
});
|
||||
storage.clearClientInformation(SERVER);
|
||||
release();
|
||||
await storage.ready();
|
||||
await Promise.resolve();
|
||||
// Client info cleared, but the sibling token from the persisted blob (and
|
||||
// every other server's blob, in the whole-blob adapter) survives — proving
|
||||
// the pre-hydration clear did not clobber on-disk state.
|
||||
expect(await storage.getClientInformation(SERVER)).toBeUndefined();
|
||||
expect(await storage.getTokens(SERVER)).toEqual({
|
||||
access_token: "t",
|
||||
token_type: "Bearer",
|
||||
});
|
||||
});
|
||||
|
||||
it("clear() after hydration does not schedule a deferred re-apply", async () => {
|
||||
const { storage, release } = makeAsyncBackedStorage({
|
||||
[SERVER]: { tokens: { access_token: "t", token_type: "Bearer" } },
|
||||
|
|
|
|||
|
|
@ -172,16 +172,16 @@ export class OAuthStorageBase implements OAuthStorage {
|
|||
}
|
||||
|
||||
clearClientInformation(serverUrl: string, isPreregistered?: boolean): void {
|
||||
const updates: Partial<ServerOAuthState> = {};
|
||||
|
||||
if (isPreregistered) {
|
||||
updates.preregisteredClientInformation = undefined;
|
||||
} else {
|
||||
updates.clientInformation = undefined;
|
||||
updates.clientRegistrationKind = undefined;
|
||||
}
|
||||
|
||||
this.store.getState().setServerState(serverUrl, updates);
|
||||
this.clearAfterHydration(() => {
|
||||
const updates: Partial<ServerOAuthState> = {};
|
||||
if (isPreregistered) {
|
||||
updates.preregisteredClientInformation = undefined;
|
||||
} else {
|
||||
updates.clientInformation = undefined;
|
||||
updates.clientRegistrationKind = undefined;
|
||||
}
|
||||
this.store.getState().setServerState(serverUrl, updates);
|
||||
});
|
||||
}
|
||||
|
||||
async getTokens(serverUrl: string): Promise<OAuthTokens | undefined> {
|
||||
|
|
|
|||
Loading…
Reference in a new issue