Skip to content

fix(auth): improve auth error handling and fix Link CORS issue#120

Open
konokenj wants to merge 1 commit intomainfrom
fix/107-auth-error-handling
Open

fix(auth): improve auth error handling and fix Link CORS issue#120
konokenj wants to merge 1 commit intomainfrom
fix/107-auth-error-handling

Conversation

@konokenj
Copy link
Contributor

Issue

close #107

Problem

The authentication module had three issues:

  1. CORS errors from <Link> on auth routes: Header.tsx and sign-in/page.tsx used <Link prefetch={false}> for auth API routes (/api/auth/sign-out, /api/auth/sign-in). Even with prefetch={false}, clicking triggers a client-side RSC fetch. Since auth routes return a 302 redirect to the Cognito domain, the fetch follows the redirect cross-origin, causing a CORS error. Next.js falls back to MPA navigation via the catch block (source), so the navigation still works, but an unnecessary fetch + console error occurs on every click.

  2. getSession() coupled auth and DB access: Authentication (Amplify fetchAuthSession) and database access (Prisma user.findUnique) were in a single function. When tokens expired, the thrown error was indistinguishable from a DB error, making it impossible to return 401 vs 500 in API routes. Additionally, cache() was not used, so multiple calls within the same request would hit Cognito + DB each time.

  3. Duplicate auth paths in authActionClient: safe-action.ts used getCurrentUser() from Amplify while auth.ts used fetchAuthSession(), creating two separate authentication code paths.

Solution

  1. Replace <Link> with <a> for auth routes: Auth API routes that redirect to Cognito should use plain <a> tags for full-page navigation, avoiding the RSC fetch entirely. This also allowed Header.tsx to become a Server Component by removing "use client" and useRouter.

  2. Split getSession() into three functions with cache():

    • getAuthSession(): Auth only, no DB access. Memoized with React cache().
    • tryGetAuthSession(): Returns null on failure instead of throwing. For API routes that need to distinguish 401 from 500.
    • getSessionWithUser(): Auth + DB user lookup. Memoized with React cache().
  3. Unify authActionClient to use getAuthSession(): Replaced getCurrentUser() with getAuthSession() to consolidate the auth path.

Changes

  • webapp/src/lib/auth.ts: Split getSession() into getAuthSession(), tryGetAuthSession(), getSessionWithUser() with cache()
  • webapp/src/lib/safe-action.ts: Replace getCurrentUser() with getAuthSession()
  • webapp/src/components/Header.tsx: <Link><a> for sign-out, remove "use client"/useRouter
  • webapp/src/app/sign-in/page.tsx: <Link><a> for sign-in, remove Link import
  • webapp/src/app/(root)/page.tsx: getSession()getAuthSession()
  • webapp/src/app/auth-callback/page.tsx: Simplify user creation flow with getAuthSession() + direct Prisma call

Verification

  • tsc --noEmit: passes
  • eslint: passes (with intentional no-html-link-for-pages disable for auth routes)
  • prettier --check: passes
  • Auth routes (/api/auth/*) use <a> tags, preventing RSC fetch and CORS errors
  • auth-callback correctly creates users on first login
  • cache() prevents duplicate Cognito/DB calls within a single request

Why: The auth module had three issues:
1. <Link> to auth API routes caused unnecessary RSC fetch + CORS errors
2. getSession() coupled auth and DB access, making error types indistinguishable
3. authActionClient used a separate auth path (getCurrentUser) from getSession()

What:
- Split getSession() into getAuthSession(), tryGetAuthSession(), getSessionWithUser()
- Add React cache() for per-request memoization
- Replace <Link> with <a> for auth routes that redirect to Cognito
- Unify authActionClient to use getAuthSession()
- Convert Header.tsx from Client to Server Component
@konokenj
Copy link
Contributor Author

Local Verification

Verified on localhost:3010 with dev server + SSM port-forwarded Aurora.

  • Sign In/Out (<a> tag): No Failed to fetch RSC payload console errors. Full-page navigation to Cognito works correctly.
  • Auth callback (user creation): New user created in DB on first login, redirected to /.
  • CRUD (authActionClient): Todo create, status update, delete all succeed with unified getAuthSession().
  • Token expiry (cookie cleared): Redirected to /sign-in without DB errors.

@konokenj konokenj requested a review from tmokmss March 20, 2026 03:55
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

認証エラーハンドリング改善: tryGetAuthSession + cache() + Link→a変更

1 participant