feat: proxy route protection - #75
Conversation
…y auth-route redirects
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
|
No actionable comments were generated in the recent review. 🎉 ℹ️ Recent review info⚙️ Run configurationConfiguration used: Path: .coderabbit.yaml Review profile: CHILL Plan: Pro Plus Run ID: 📒 Files selected for processing (2)
🚧 Files skipped from review as they are similar to previous changes (2)
📝 WalkthroughWalkthroughAdds localized About, sign-in, sign-up, and protected history pages. Authentication claims gate history access, middleware redirects authenticated users from authentication routes, and unauthorized rendering redirects to the root path. ChangesAuthentication routing and protected pages
Estimated code review effort: 3 (Moderate) | ~20 minutes Sequence Diagram(s)sequenceDiagram
participant Client
participant proxy
participant Supabase
participant NextResponse
Client->>proxy: Request authentication route
proxy->>Supabase: auth.getClaims()
Supabase-->>proxy: Claims and error result
proxy->>NextResponse: Redirect authenticated request to localized root
NextResponse-->>Client: Redirect response with refreshed cookies and headers
Suggested reviewers: 🚥 Pre-merge checks | ✅ 3 | ❌ 2❌ Failed checks (2 warnings)
✅ Passed checks (3 passed)
✨ Finishing Touches📝 Generate docstrings
🧪 Generate unit tests (beta)
Comment |
There was a problem hiding this comment.
Actionable comments posted: 4
🧹 Nitpick comments (4)
src/proxy.test.ts (1)
33-39: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick winWeak assertion for passthrough behavior.
expect(response).toBeDefined()passes for almost any return value, including a redirect. Consider asserting the response is not a redirect (e.g.expect(response.status).not.toBe(307)) or that it's the same object returned by the mocked intl middleware, to actually verify passthrough behavior.🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@src/proxy.test.ts` around lines 33 - 39, Strengthen the passthrough assertion in the non-auth route test for proxy: instead of only checking that the response is defined, assert it is not a redirect or matches the exact response returned by the mocked intl middleware. Update the test around proxy to verify the request is genuinely passed through.src/app/[locale]/history/page.tsx (1)
1-7: 🚀 Performance & Scalability | 🔵 Trivial | ⚡ Quick winUse
next/dynamicfor the History route content.Path instructions specifically call out the History & Analytics route for lazy-loading rather than static imports. As the real history UI is built out here, wrap the heavy content in
dynamic(() => import(...))rather than importing it statically.import dynamic from 'next/dynamic'; import { getAuthenticatedUser } from '`@/utils/auth/get-authenticated-user`'; const HistoryContent = dynamic(() => import('`@/components/history/history-content`')); export default async function HistoryPage() { await getAuthenticatedUser(); return <HistoryContent />; }As per path instructions, "Flag routes that should be lazy-loaded but aren't using dynamic import - specifically the History & Analytics route per project requirements."
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@src/app/`[locale]/history/page.tsx around lines 1 - 7, Update HistoryPage to lazy-load the route content with next/dynamic: import dynamic, define a HistoryContent component using dynamic(() => import('`@/components/history/history-content`')), and render it after getAuthenticatedUser() instead of the static placeholder.Source: Path instructions
src/utils/auth/get-authenticated-user.ts (1)
9-11: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low valueSilent error swallowing before triggering
unauthorized().Unlike proxy.ts, which logs
Failed to refresh Supabase session in proxyon error, this path dropserrorwithout any log, making auth failures (expired tokens, network errors, misconfiguration) hard to diagnose in production.🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@src/utils/auth/get-authenticated-user.ts` around lines 9 - 11, In the authenticated-user retrieval logic, log the Supabase error before calling unauthorized() when the request returns an error or no data. Update the relevant function to include a descriptive failure message and the original error details, matching the logging approach used by proxy.ts.src/utils/auth/get-authenticated-user.test.ts (1)
28-57: 🎯 Functional Correctness | 🔵 Trivial | ⚡ Quick winMissing test case:
datatruthy witherroralso present.The
!data || errorbranch ingetAuthenticatedUserhas an untested combination — non-nulldataalongside a truthyerror. Add a case to fully cover the OR condition.it('calls unauthorized when getClaims returns an error alongside data', async () => { vi.mocked(createClient).mockResolvedValue( mockSupabaseClient({ data: { sub: 'user-id' }, error: new Error('invalid claim') }), ); await getAuthenticatedUser(); expect(unauthorized).toHaveBeenCalled(); });🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@src/utils/auth/get-authenticated-user.test.ts` around lines 28 - 57, Add a test in the getAuthenticatedUser suite covering the case where getClaims returns both non-null data and an Error, using mockSupabaseClient with claims data and a truthy error, then await getAuthenticatedUser and assert unauthorized was called.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In `@src/proxy.ts`:
- Around line 7-8: Replace substring matching against AUTH_ROUTES in the proxy
route-checking logic with exact path-segment matching, so paths such as
/sign-in-confirmation or /foo/sign-up-info are not treated as authentication
routes. Update the relevant pathname check while preserving matches for the
intended /sign-in and /sign-up routes.
- Around line 38-42: Handle exceptions from the async supabase.auth.getClaims()
call in src/proxy.ts by wrapping it in try/catch, while preserving the existing
error logging and unauthenticated fallback behavior for both returned errors and
thrown failures.
- Around line 48-52: Update the auth redirect branch in proxy’s route handler to
redirect to the current locale root rather than “/”, and preserve the refreshed
session cookies stored on intlResponse. Create the redirect response using the
locale-aware URL, then copy all cookies from intlResponse onto it before
returning.
In `@src/utils/auth/get-authenticated-user.ts`:
- Around line 5-14: getAuthenticatedUser lacks rejection handling for the
Supabase auth call. Wrap supabase.auth.getClaims() in a try/catch, and invoke
unauthorized() from the catch as well as when the returned data is missing or
contains an error, ensuring all authentication failures follow the intended 401
flow.
---
Nitpick comments:
In `@src/app/`[locale]/history/page.tsx:
- Around line 1-7: Update HistoryPage to lazy-load the route content with
next/dynamic: import dynamic, define a HistoryContent component using dynamic(()
=> import('`@/components/history/history-content`')), and render it after
getAuthenticatedUser() instead of the static placeholder.
In `@src/proxy.test.ts`:
- Around line 33-39: Strengthen the passthrough assertion in the non-auth route
test for proxy: instead of only checking that the response is defined, assert it
is not a redirect or matches the exact response returned by the mocked intl
middleware. Update the test around proxy to verify the request is genuinely
passed through.
In `@src/utils/auth/get-authenticated-user.test.ts`:
- Around line 28-57: Add a test in the getAuthenticatedUser suite covering the
case where getClaims returns both non-null data and an Error, using
mockSupabaseClient with claims data and a truthy error, then await
getAuthenticatedUser and assert unauthorized was called.
In `@src/utils/auth/get-authenticated-user.ts`:
- Around line 9-11: In the authenticated-user retrieval logic, log the Supabase
error before calling unauthorized() when the request returns an error or no
data. Update the relevant function to include a descriptive failure message and
the original error details, matching the logging approach used by proxy.ts.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Path: .coderabbit.yaml
Review profile: CHILL
Plan: Pro Plus
Run ID: 8fd9c5d0-7e12-42d3-bf26-8f7091218862
📒 Files selected for processing (11)
next.config.tssrc/app/[locale]/about/page.tsxsrc/app/[locale]/history/page.tsxsrc/app/[locale]/sign-in/page.tsxsrc/app/[locale]/sign-up/page.tsxsrc/app/[locale]/unauthorized.test.tsxsrc/app/[locale]/unauthorized.tsxsrc/proxy.test.tssrc/proxy.tssrc/utils/auth/get-authenticated-user.test.tssrc/utils/auth/get-authenticated-user.ts
There was a problem hiding this comment.
Actionable comments posted: 1
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In `@src/proxy.ts`:
- Around line 56-67: Update the auth-redirect branch in the proxy flow to copy
all headers set on intlResponse, including setAll-derived cache-control headers,
onto redirectResponse before returning it. Preserve the existing cookie
forwarding and redirect behavior, and ensure the returned NextResponse retains
the original response headers.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Path: .coderabbit.yaml
Review profile: CHILL
Plan: Pro Plus
Run ID: ad613c36-e93d-4d00-8a28-6eda7c4a8712
📒 Files selected for processing (4)
src/proxy.test.tssrc/proxy.tssrc/utils/auth/get-authenticated-user.test.tssrc/utils/auth/get-authenticated-user.ts
🚧 Files skipped from review as they are similar to previous changes (3)
- src/utils/auth/get-authenticated-user.ts
- src/utils/auth/get-authenticated-user.test.ts
- src/proxy.test.ts
* chore: install the @supabase/supabase-js and @supabase/ssr helper packages * feat: add Supabase browser client * feat: add Supabase server client * docs: add .env.example * chore: add .env.local to gitignore * docs: update README with project description, setup instructions, and environment variables * feat: integrate Supabase session refresh into proxy * test: add tests for Supabase browser client, server client, and proxy * chore: ensure .env.example is tracked * fix: log getClaims errors in proxy * test: fix after review * fix: pass response headers through setAll in proxy and add test coverage * feat: proxy route protection (#75) * feat: add page stubs for about, history, sign-in, and sign-up routes * feat: redirect authenticated users away from sign-in/sign-up routes * feat: protect private routes with 401 and redirect expired/invalid sessions * test: add tests for getAuthenticatedUser, Unauthorized page, and proxy auth-route redirects * fix: stricter route matching, error handling, cookie and locale preservation on redirect * fix: wrap getClaims() in try/catch in getAuthenticatedUser * refactor: use ROUTES constants and simplify locale/route matching * fix: forward setAll headers on auth-route redirect * docs: link to Supabase SSR guide in browser and server client files
* chore: supabase setup (#73) * chore: install the @supabase/supabase-js and @supabase/ssr helper packages * feat: add Supabase browser client * feat: add Supabase server client * docs: add .env.example * chore: add .env.local to gitignore * docs: update README with project description, setup instructions, and environment variables * feat: integrate Supabase session refresh into proxy * test: add tests for Supabase browser client, server client, and proxy * chore: ensure .env.example is tracked * fix: log getClaims errors in proxy * test: fix after review * fix: pass response headers through setAll in proxy and add test coverage * feat: proxy route protection (#75) * feat: add page stubs for about, history, sign-in, and sign-up routes * feat: redirect authenticated users away from sign-in/sign-up routes * feat: protect private routes with 401 and redirect expired/invalid sessions * test: add tests for getAuthenticatedUser, Unauthorized page, and proxy auth-route redirects * fix: stricter route matching, error handling, cookie and locale preservation on redirect * fix: wrap getClaims() in try/catch in getAuthenticatedUser * refactor: use ROUTES constants and simplify locale/route matching * fix: forward setAll headers on auth-route redirect * docs: link to Supabase SSR guide in browser and server client files * feat: auth forms integration (#79) * feat: add AuthProvider with signOut and reactive auth state * test: add unit tests for AuthProvider and AuthNavigation component * refactor: update ru sign-up label * chore: install zod, react-hook-form, @hookform/resolvers * feat: add sign-in and sign-up forms with zod validation * feat: connect SignIn and SignUp forms to Supabase auth * fix: redirect to home after sign out to leave protected routes * test: cover auth-schemas, auth-card, controlled-text-field, sign-in and sign-up forms * fix: handle rejected Supabase calls in auth flows * fix: show a redirecting indicator in the unauthorized boundary * fix: remove unnecessary 'use client' directive from AuthCard * fix: add type="button" to password visibility toggle to prevent accidental form submission * fix: rebuild proxy response after mutating cookies * test: update tests and proxy.tsx
Summary
Adds route-level protection for authenticated and private routes: redirects already-logged-in users away from Sign In / Sign Up, and protects private routes (currently /history) with a real HTTP 401 response via Next.js's unauthorized() API, followed by a client-side redirect to the Main page once the browser has received the 401.
Changes Made
Related issue
Closes #20, Closes #71
Feature
Feature 1: App Header
Feature 2: Sign In / Sign Up
Feature 7: General Requirements
Acceptance criteria
Feature 1: App Header
Feature 2: Sign In / Sign Up
Feature 3: Swagger Editor
Feature 4: Swagger Viewer
Feature 5: History and Analytics
Feature 6: About Page
Feature 7: General Requirements
Feature 8: YouTube Video
Type of change
Checklist
any/@ts-ignoreSummary by CodeRabbit
Summary by CodeRabbit