fix(auth): OIDC token rotation no longer reloads every other tab
useSessionSync reloaded on any out-of-tab session change, so a routine refresh in one tab hard-reloaded the others mid-call. Classify the change: removed → reload, user/device changed → reload, same device with a new token → swap it into the running client (setAccessToken + the shared refresh token) in place. The refresher takes a Web Lock and adopts tokens another tab already rotated instead of racing the issuer. Unit-tested classifier. Fixes #16 Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01PPmy3tPq869XDW4njjVaKA
This commit is contained in:
@@ -0,0 +1,35 @@
|
||||
import { test } from 'node:test';
|
||||
import assert from 'node:assert/strict';
|
||||
import { classifySessionChange, SessionIdentity } from './useSessionSync';
|
||||
|
||||
const alice: SessionIdentity = { userId: '@alice:hs', deviceId: 'DEV1', accessToken: 'tok-1' };
|
||||
|
||||
test('classifySessionChange: session removed elsewhere → removed', () => {
|
||||
assert.equal(classifySessionChange(alice, null), 'removed');
|
||||
});
|
||||
|
||||
test('classifySessionChange: nothing before or after → none', () => {
|
||||
assert.equal(classifySessionChange(null, null), 'none');
|
||||
});
|
||||
|
||||
test('classifySessionChange: session appeared → relogin', () => {
|
||||
assert.equal(classifySessionChange(null, alice), 'relogin');
|
||||
});
|
||||
|
||||
test('classifySessionChange: different user or device → relogin', () => {
|
||||
assert.equal(classifySessionChange(alice, { ...alice, userId: '@bob:hs' }), 'relogin');
|
||||
assert.equal(classifySessionChange(alice, { ...alice, deviceId: 'DEV2' }), 'relogin');
|
||||
// Even when the access token also changed, the identity change wins.
|
||||
assert.equal(
|
||||
classifySessionChange(alice, { ...alice, deviceId: 'DEV2', accessToken: 'tok-2' }),
|
||||
'relogin',
|
||||
);
|
||||
});
|
||||
|
||||
test('classifySessionChange: same user+device, new access token → rotated (no reload)', () => {
|
||||
assert.equal(classifySessionChange(alice, { ...alice, accessToken: 'tok-2' }), 'rotated');
|
||||
});
|
||||
|
||||
test('classifySessionChange: identical credentials (metadata-only rewrite) → none', () => {
|
||||
assert.equal(classifySessionChange(alice, { ...alice }), 'none');
|
||||
});
|
||||
Reference in New Issue
Block a user