@@ -23,6 +23,7 @@ import { l, serializeErrorForLog } from '@/core/shared/clients/logger/logger'
2323import { getAuthRouteRedirect } from './auth-routes'
2424import {
2525 classifyProxyRequest ,
26+ isAuthEndpointRoute ,
2627 type ProxyPlan ,
2728 planNeedsAuthGate ,
2829} from './classifier'
@@ -64,11 +65,20 @@ export async function runDashboardProxy(
6465 return oryProxy ( request )
6566 }
6667
67- // Pattern B: refresh the e2b_session up front and propagate it to the same
68+ const plan = classifyProxyRequest ( request . nextUrl . pathname )
69+
70+ // refresh the e2b_session up front and propagate it to the same
6871 // request (request.cookies) so RSC/route handlers and the gate below read the
6972 // fresh token, then persist it on the outgoing response for the browser.
70- const session = await refreshSessionCookie ( request )
71- const plan = classifyProxyRequest ( request . nextUrl . pathname )
73+ //
74+ // Auth endpoints own their session lifecycle: sign-out reads the id_token from
75+ // e2b_session before clearing it, the OAuth callback mints a fresh session. A
76+ // dead refresh here would delete the cookie out of the propagated request
77+ // before the handler reads it, breaking RP-initiated logout (Kratos/Hydra
78+ // would never end the session), so skip the refresh for them.
79+ const session = isAuthEndpointRoute ( request . nextUrl . pathname )
80+ ? skipRefresh
81+ : await refreshSessionCookie ( request )
7282
7383 if ( ! planNeedsAuthGate ( plan ) ) {
7484 return session . persist ( await runProxyConcerns ( request , plan ) )
@@ -96,6 +106,8 @@ type SessionRefresh = {
96106
97107const noPersist : SessionRefresh [ 'persist' ] = ( response ) => response
98108
109+ const skipRefresh : SessionRefresh = { hasToken : false , persist : noPersist }
110+
99111async function refreshSessionCookie (
100112 request : NextRequest
101113) : Promise < SessionRefresh > {
0 commit comments