From 861faa05ce36c782c2902beadc8b88cb525df4f1 Mon Sep 17 00:00:00 2001 From: Daniel Roe Date: Sun, 20 Sep 2026 10:33:16 +0100 Subject: [PATCH] fix: use session cookie for in-flight oauth logins --- server/utils/admin/oauth.ts | 42 ++++++++++++++++++++++++++++--------- 1 file changed, 32 insertions(+), 10 deletions(-) diff --git a/server/utils/admin/oauth.ts b/server/utils/admin/oauth.ts index 4cededd6..ccaf27fc 100644 --- a/server/utils/admin/oauth.ts +++ b/server/utils/admin/oauth.ts @@ -23,15 +23,9 @@ export const OAUTH_SCOPES = scopesFor({ collections }) type NodeSavedSession = NonNullable>> type NodeSavedState = NonNullable>> -/** - * In-flight authorizations, shared across requests: `authorize()` and the - * callback are two requests, and the OAuth client is built per request. - */ -const states = new Map() -const stateStore: NodeSavedStateStore = { - async get (key) { return states.get(key) }, - async set (key, value) { states.set(key, value) }, - async del (key) { states.delete(key) }, +interface AdminStateData { + key?: string + state?: NodeSavedState } interface AdminSessionData { @@ -68,6 +62,34 @@ export function clearAdminSessionCookie (event: H3Event) { return clearSession(event, sessionConfig(event)) } +function stateSessionConfig (event: H3Event) { + return { + ...sessionConfig(event), + name: 'admin-oauth-state', + maxAge: 60 * 10, + } +} + +/** + * In-flight authorization state, held in a short-lived cookie: `authorize()` + * and the callback are separate requests that are not guaranteed to hit the + * same serverless instance, so it cannot live in process memory. + */ +function cookieStateStore (event: H3Event): NodeSavedStateStore { + return { + async get (key: string): Promise { + const sess = await getSession(event, stateSessionConfig(event)) + return sess.data.key === key ? sess.data.state : undefined + }, + async set (key: string, value: NodeSavedState): Promise { + await updateSession(event, stateSessionConfig(event), { key, state: value }) + }, + async del (): Promise { + await clearSession(event, stateSessionConfig(event)) + }, + } +} + function baseUrlFor (event: H3Event): string { return useRuntimeConfig(event).admin.baseUrl.replace(/\/$/, '') } @@ -116,7 +138,7 @@ export function getOauth (event: H3Event) { redirectPath: REDIRECT_PATH, name: nameFor(baseUrl), scopes: OAUTH_SCOPES, - stores: { state: stateStore, session: cookieSessionStore(event) }, + stores: { state: cookieStateStore(event), session: cookieSessionStore(event) }, }) } -- 2.51.2