Skip to content

#285 Fix Sign-Out Returns 403#289

Open
b-at-neu wants to merge 3 commits into
devfrom
285-fix-sign-out-server-action
Open

#285 Fix Sign-Out Returns 403#289
b-at-neu wants to merge 3 commits into
devfrom
285-fix-sign-out-server-action

Conversation

@b-at-neu

Copy link
Copy Markdown
Collaborator

Closes #285

Summary

  • Sign-out was failing with a 403 because authClient.signOut() (browser-side) POSTs to /api/auth/sign-out, which Neon Auth's upstream better-auth rejected due to CSRF/SameSite protection.
  • Fix: replace the client-driven call with a new signOutUser() server action that calls authServer.signOut() server-side, forwarding the session cookie via next/headers — bypassing browser origin/CSRF constraints entirely.

Changes

  • prisma/actions/auth.ts (new) — signOutUser() server action: calls authServer.signOut(), returns { error } on upstream failure, revalidates layout cache and redirects to /login on success.
  • components/layouts/user-menu.tsx — replace the real-auth authClient.signOut() try/catch with signOutUser(); drop the now-unused authClient import and useRouter hook; keep the bypass branch (logoutBypassUser()) unchanged.

Testing plan

  • Sign in via real OTP auth; open the user menu; click Log out; verify landing on /login with no error toast and session cleared (revisiting a gated route redirects to login).
  • Verify the 403 "Could not sign out. Please try again." toast no longer appears for a normal real-auth sign-out.
  • Force an upstream failure (temporarily set NEON_AUTH_BASE_URL to an invalid value) and confirm the action returns the error toast and the menu re-enables without crashing or redirecting.
  • In dev with a bypass session: Log out still clears the bypass cookie and redirects to /login/bypass (unchanged path).
  • Keyboard: open user menu, tab to Log out, press Enter — sign-out triggers; focus handled correctly by the Radix dropdown primitive.
  • Confirm no authClient.signOut references remain in the codebase.

Automated checks

  • prettier:check — pass
  • eslint:check — pass
  • tsc:check — pass

Notes

If the 403 persists in production after this change (server-side path still hits the upstream), the next investigation step is verifying the deployment origin is registered as a trusted origin in the Neon Auth dashboard — a config step outside this repo. The server-side path removes the most common cause (browser SameSite/CSRF fragility) and aligns with the repo's "mutations are server actions, no client fetching" rule.

@b-at-neu b-at-neu self-assigned this Jun 29, 2026
@vercel

vercel Bot commented Jun 29, 2026

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated (UTC)
aplio Ready Ready Preview, Comment Jun 29, 2026 9:54am

@b-at-neu b-at-neu added the ready for review PR ready for review agent label Jun 29, 2026
@b-at-neu b-at-neu added reviewing Review agent working (in-flight) and removed ready for review PR ready for review agent labels Jun 29, 2026

@b-at-neu b-at-neu left a comment

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

Code Review — Cycle 1 · needs revision

1 open — 1 🟠 Medium (see inline)

Comment thread prisma/actions/auth.ts
@b-at-neu b-at-neu added needs revision Review found issues that need fixing revising Revise agent working (in-flight) and removed reviewing Review agent working (in-flight) needs revision Review found issues that need fixing labels Jun 29, 2026
@b-at-neu b-at-neu force-pushed the 285-fix-sign-out-server-action branch from eb1c473 to 1706719 Compare June 29, 2026 09:42
@b-at-neu

Copy link
Copy Markdown
Collaborator Author

Revision — Cycle 1

fixed R1-M1 · 1706719

@b-at-neu b-at-neu added ready for review PR ready for review agent and removed revising Revise agent working (in-flight) labels Jun 29, 2026
@b-at-neu b-at-neu added reviewing Review agent working (in-flight) and removed ready for review PR ready for review agent labels Jun 29, 2026

@b-at-neu b-at-neu left a comment

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

Code Review — Cycle 1 · needs revision

1 open — 1 🟠 Medium (see inline)

Comment thread prisma/actions/auth.ts

@b-at-neu b-at-neu left a comment

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

Code Review — Cycle 1 · needs revision

3 open — 1 🟡 Low, 2 ⚪ Nit (see inline)

Note: a stale review (Cycle 1 draft) was posted immediately before this one with an incorrect finding (R1-M1 claiming missing auth check — disregard it; getCurrentUser() is present at line 14 of prisma/actions/auth.ts). This review supersedes it.

Comment thread components/layouts/user-menu.tsx
Comment thread components/layouts/user-menu.tsx Outdated
Comment thread prisma/actions/auth.ts Outdated
@b-at-neu b-at-neu added needs revision Review found issues that need fixing revising Revise agent working (in-flight) and removed reviewing Review agent working (in-flight) needs revision Review found issues that need fixing labels Jun 29, 2026
b-at-neu and others added 3 commits June 29, 2026 11:48
Replace client-side authClient.signOut() with a signOutUser() server
action that proxies to authServer.signOut() via next/headers, removing
the browser SameSite/CSRF fragility that caused the 403.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
@b-at-neu b-at-neu force-pushed the 285-fix-sign-out-server-action branch from 1706719 to e0936ed Compare June 29, 2026 09:53
@b-at-neu

Copy link
Copy Markdown
Collaborator Author

Revision — Cycle 1

fixed R1-M1, R1-L1, R1-N2, R1-N1 · e0936ed

@b-at-neu b-at-neu removed the revising Revise agent working (in-flight) label Jun 29, 2026
@b-at-neu b-at-neu added ready for review PR ready for review agent reviewing Review agent working (in-flight) and removed ready for review PR ready for review agent labels Jun 29, 2026

@b-at-neu b-at-neu left a comment

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

Code Review — Cycle 4 · needs revision

1 open — 1 🟠 Medium (see inline)

// displays regardless of which path (redirect or error) completes first.
toast.success('Signed out.');
const result = await signOutUser();
if (isError(result)) {

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

R4-M1 🟠 Medium — toast.success('Signed out.') fires unconditionally before the action is called, so on the error path the user sees a success toast immediately followed by an error toast — contradictory feedback. ENGINEERING.md §4 requires a success toast for success and a specific error toast for { error }; showing both simultaneously violates that model. The rationale in the comment (fires before server redirect) is real but the fix is wrong: redirect() in a server action sends a redirect response to the client after the await resolves, so a toast fired after await signOutUser() on the non-error branch would also display before navigation. Fix: const result = await signOutUser(); if (isError(result)) { toast.error(result.error); } else { toast.success('Signed out.'); }.

@b-at-neu b-at-neu added needs revision Review found issues that need fixing and removed reviewing Review agent working (in-flight) labels Jun 29, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

needs revision Review found issues that need fixing

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Sign-out returns 403 — Could not sign out. Please try again.

1 participant