Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
24 changes: 24 additions & 0 deletions src/server/middleware-body.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -139,6 +139,30 @@ describe('middlewareBody', () => {
expect(result.response.headers.get('Set-Cookie')).toContain('wos-session=new_value');
});

it.each([
['organization switch', 'wos-session=org_b_r3; Path=/'],
['sign out', 'wos-session=; Path=/; Max-Age=0'],
])('keeps the %s cookie after the auto-refresh cookie', async (_, handlerCookie) => {
const refreshCookie = 'wos-session=org_a_r2; Path=/';
mockAuthkit.withAuth.mockResolvedValue({
auth: { user: { id: 'user_123' } },
refreshedSessionData: 'refreshed_session',
});
mockAuthkit.saveSession.mockResolvedValue({
response: new Response(null, { headers: { 'Set-Cookie': refreshCookie } }),
});

const result = await middlewareBody({
request: new Request('http://test.local'),
next: vi.fn(async ({ context }: any) => {
context.__setPendingHeader('Set-Cookie', handlerCookie);
return { response: new Response('OK') };
}),
});

expect(result.response.headers.getSetCookie()).toEqual([refreshCookie, handlerCookie]);
});

it('provides correct context shape to downstream handlers', async () => {
const mockAuth = { user: { id: 'user_123' }, sessionId: 'session_123' };
mockAuthkit.withAuth.mockResolvedValue({
Expand Down
15 changes: 8 additions & 7 deletions src/server/middleware-body.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,14 @@ export async function middlewareBody(args: any, options?: AuthKitMiddlewareOptio
const { auth, refreshedSessionData } = await authkit.withAuth(args.request);
const pendingHeaders = new Headers();

// Queue auto-refresh cookies first so downstream session writes take precedence.
if (refreshedSessionData) {
const { response: sessionResponse } = await authkit.saveSession(undefined, refreshedSessionData);
for (const cookie of sessionResponse?.headers.getSetCookie() ?? []) {
pendingHeaders.append('Set-Cookie', cookie);
}
Comment on lines +18 to +23

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Sign-out cookie loses precedence

When auto-refresh and signOut() happen on the same request, sign-out puts its session-clear cookie in the redirect response, while this code queues the refreshed session cookie separately. The final merge appends the queued cookie after the redirect headers, so the refreshed session can override the deletion and leave the browser signed in. The new sign-out test writes its deletion cookie through the pending-header path instead, so it does not cover this case.

Knowledge Base Used:

Prompt To Fix With AI
This is a comment left during a code review.
Path: src/server/middleware-body.ts
Line: 18-23

Comment:
**Sign-out cookie loses precedence**

When auto-refresh and `signOut()` happen on the same request, sign-out puts its session-clear cookie in the redirect response, while this code queues the refreshed session cookie separately. The final merge appends the queued cookie *after* the redirect headers, so the refreshed session can override the deletion and leave the browser signed in. The new sign-out test writes its deletion cookie through the pending-header path instead, so it does not cover this case.

**Knowledge Base Used:**
- [Middleware, session, and request context](https://app.greptile.com/workos/-/custom-context/knowledge-base/workos/authkit-tanstack-start/-/docs/middleware-session-context.md)
- [Authentication actions and route bodies](https://app.greptile.com/workos/-/custom-context/knowledge-base/workos/authkit-tanstack-start/-/docs/auth-actions-and-route-bodies.md)

---

For each issue above, determine whether it is valid and should be fixed. If so, fix it directly.

}

const result = await args.next({
context: {
auth: () => auth,
Expand All @@ -30,13 +38,6 @@ export async function middlewareBody(args: any, options?: AuthKitMiddlewareOptio
},
});

if (refreshedSessionData) {
const { response: sessionResponse } = await authkit.saveSession(undefined, refreshedSessionData);
for (const cookie of sessionResponse?.headers.getSetCookie() ?? []) {
pendingHeaders.append('Set-Cookie', cookie);
}
}

const headerEntries = [...pendingHeaders];
if (headerEntries.length === 0) {
return result;
Expand Down
Loading