Commit 84be605
authored
fix(auth): improve auth error handling and fix Link CORS issue (#120)
## 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](https://github.com/vercel/next.js/blob/canary/packages/next/src/client/components/router-reducer/fetch-server-response.ts)),
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 request1 parent 70cddda commit 84be605
File tree
7 files changed
+65
-82
lines changed- webapp/src
- app
- (root)
- api/cognito-token
- auth-callback
- sign-in
- components
- lib
7 files changed
+65
-82
lines changed| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
1 | 1 | | |
2 | | - | |
| 2 | + | |
3 | 3 | | |
4 | 4 | | |
5 | 5 | | |
6 | 6 | | |
7 | 7 | | |
8 | 8 | | |
9 | | - | |
| 9 | + | |
10 | 10 | | |
11 | 11 | | |
12 | 12 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
1 | 1 | | |
2 | | - | |
3 | | - | |
4 | | - | |
| 2 | + | |
5 | 3 | | |
6 | 4 | | |
7 | 5 | | |
8 | | - | |
9 | | - | |
10 | | - | |
11 | | - | |
12 | | - | |
13 | | - | |
| 6 | + | |
| 7 | + | |
14 | 8 | | |
15 | 9 | | |
16 | 10 | | |
17 | 11 | | |
18 | | - | |
| 12 | + | |
19 | 13 | | |
20 | 14 | | |
21 | 15 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
1 | 1 | | |
2 | | - | |
| 2 | + | |
3 | 3 | | |
4 | 4 | | |
5 | 5 | | |
6 | 6 | | |
7 | 7 | | |
8 | | - | |
9 | | - | |
10 | | - | |
11 | | - | |
12 | | - | |
13 | | - | |
14 | | - | |
15 | | - | |
16 | | - | |
17 | | - | |
18 | | - | |
19 | | - | |
20 | | - | |
21 | | - | |
22 | | - | |
| 8 | + | |
| 9 | + | |
| 10 | + | |
| 11 | + | |
| 12 | + | |
23 | 13 | | |
| 14 | + | |
24 | 15 | | |
25 | 16 | | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
1 | | - | |
2 | | - | |
3 | 1 | | |
4 | 2 | | |
5 | 3 | | |
| |||
15 | 13 | | |
16 | 14 | | |
17 | 15 | | |
18 | | - | |
| 16 | + | |
| 17 | + | |
| 18 | + | |
| 19 | + | |
| 20 | + | |
19 | 21 | | |
20 | 22 | | |
21 | 23 | | |
22 | 24 | | |
23 | | - | |
24 | 25 | | |
25 | 26 | | |
26 | | - | |
| 27 | + | |
27 | 28 | | |
28 | 29 | | |
29 | 30 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
1 | | - | |
2 | | - | |
3 | 1 | | |
4 | | - | |
5 | 2 | | |
6 | 3 | | |
7 | | - | |
8 | | - | |
9 | 4 | | |
10 | 5 | | |
11 | 6 | | |
| |||
16 | 11 | | |
17 | 12 | | |
18 | 13 | | |
19 | | - | |
| 14 | + | |
| 15 | + | |
| 16 | + | |
| 17 | + | |
| 18 | + | |
20 | 19 | | |
21 | 20 | | |
22 | | - | |
23 | 21 | | |
24 | 22 | | |
25 | | - | |
| 23 | + | |
26 | 24 | | |
27 | 25 | | |
28 | 26 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
| 1 | + | |
1 | 2 | | |
2 | 3 | | |
3 | 4 | | |
4 | 5 | | |
5 | 6 | | |
6 | | - | |
7 | | - | |
8 | | - | |
9 | | - | |
10 | | - | |
| 7 | + | |
| 8 | + | |
| 9 | + | |
| 10 | + | |
| 11 | + | |
| 12 | + | |
11 | 13 | | |
12 | 14 | | |
13 | 15 | | |
14 | 16 | | |
15 | 17 | | |
16 | 18 | | |
17 | 19 | | |
18 | | - | |
19 | 20 | | |
20 | 21 | | |
21 | | - | |
22 | | - | |
23 | | - | |
24 | | - | |
25 | | - | |
26 | | - | |
27 | | - | |
28 | | - | |
29 | | - | |
| 22 | + | |
30 | 23 | | |
31 | | - | |
32 | 24 | | |
33 | | - | |
| 25 | + | |
34 | 26 | | |
35 | 27 | | |
36 | | - | |
37 | 28 | | |
| 29 | + | |
| 30 | + | |
| 31 | + | |
| 32 | + | |
| 33 | + | |
| 34 | + | |
| 35 | + | |
| 36 | + | |
| 37 | + | |
| 38 | + | |
| 39 | + | |
| 40 | + | |
| 41 | + | |
| 42 | + | |
| 43 | + | |
| 44 | + | |
| 45 | + | |
| 46 | + | |
| 47 | + | |
| 48 | + | |
| 49 | + | |
| 50 | + | |
| 51 | + | |
| 52 | + | |
| 53 | + | |
| 54 | + | |
| 55 | + | |
| 56 | + | |
| 57 | + | |
38 | 58 | | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
1 | | - | |
2 | | - | |
3 | | - | |
| 1 | + | |
4 | 2 | | |
5 | | - | |
6 | 3 | | |
7 | 4 | | |
8 | 5 | | |
| |||
28 | 25 | | |
29 | 26 | | |
30 | 27 | | |
31 | | - | |
32 | | - | |
33 | | - | |
34 | | - | |
35 | | - | |
36 | | - | |
37 | | - | |
38 | | - | |
39 | | - | |
40 | | - | |
41 | | - | |
42 | | - | |
43 | | - | |
44 | | - | |
45 | | - | |
46 | | - | |
47 | | - | |
48 | | - | |
49 | | - | |
| 28 | + | |
50 | 29 | | |
51 | 30 | | |
0 commit comments