fix: thread per-account cookie slot through OAuth flows
The multi-account refresh-token cookie slot wiring was half-implemented: every account's refresh token ended up on slot 0, so "+ Add Account" silently clobbered the previous account's `jmap_rt` cookie. On page refresh, only the most-recently-added account had a working refresh token; the others bounced to login. Three coordinated changes: 1. `app/[locale]/login/page.tsx` (handleOAuthLogin): write the next-free cookie slot to `sessionStorage['oauth_cookie_slot']` before redirecting to the IdP. `loginWithOAuth` already reads this key but it was never written, so it always defaulted to 0. 2. `stores/auth-store.ts` (loginWithOAuth): distinguish "no value set" (`rawSlot === null`) from "value is 0". Previously `parseInt(getItem(...) || '0')` collapsed both cases, making the `getNextCookieSlot()` fallback unreachable. 3. `stores/auth-store.ts` (loginWithServerSso) + `app/api/auth/sso/complete/route.ts`: pass the slot through the body of the POST and use it for `refreshTokenCookieName(slot)`. Same pattern as the existing `/api/auth/token POST` that already accepts a slot. The server defaults to 0 for back-compat with any caller that omits it. After the fix, signing in with multiple accounts produces distinct `jmap_rt`, `jmap_rt_1`, `jmap_rt_2`, ... cookies (matching the cookieSlot field in account-store) and all accounts survive a page refresh. Repro before the fix: - Sign in with one account, refresh — works. - Click "+ Add Account", sign in with a second account, refresh — second account vanishes from the dropdown; switching to the first account in the dropdown still shows the second account's identity in the From box.
This commit is contained in:
@@ -13,12 +13,18 @@ export async function POST(request: NextRequest) {
|
||||
const cookieStore = await cookies();
|
||||
|
||||
try {
|
||||
const { code, state } = await request.json();
|
||||
const { code, state, slot: bodySlot } = await request.json();
|
||||
|
||||
if (!code || !state) {
|
||||
return NextResponse.json({ error: 'Missing code or state' }, { status: 400 });
|
||||
}
|
||||
|
||||
// Per-account refresh-token cookie slot. Without this the route hardcoded
|
||||
// slot 0, so the "+ Add Account" flow overwrote the first account's
|
||||
// refresh-token cookie. Default to 0 for back-compat with any caller that
|
||||
// omits slot. Mirrors the validation in /api/auth/token POST.
|
||||
const slot = typeof bodySlot === 'number' && bodySlot >= 0 && bodySlot <= 4 ? bodySlot : 0;
|
||||
|
||||
// Read and decrypt the pending SSO cookie
|
||||
const pendingCookie = cookieStore.get(SSO_PENDING_COOKIE)?.value;
|
||||
if (!pendingCookie) {
|
||||
@@ -58,9 +64,9 @@ export async function POST(request: NextRequest) {
|
||||
// Exchange code for tokens
|
||||
const tokens = await exchangeCodeForTokens(code, codeVerifier, redirectUri);
|
||||
|
||||
// Store refresh token
|
||||
// Store refresh token in the per-account cookie slot.
|
||||
if (tokens.refresh_token) {
|
||||
const cookieName = refreshTokenCookieName(0);
|
||||
const cookieName = refreshTokenCookieName(slot);
|
||||
cookieStore.set(cookieName, tokens.refresh_token, getCookieOptions());
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user