Skip to content

feat: proxy route protection - #75

Merged
AlyaEngineer merged 8 commits into
story/2-supabase-setupfrom
story/2-proxy-route-protection
Jul 11, 2026
Merged

feat: proxy route protection#75
AlyaEngineer merged 8 commits into
story/2-supabase-setupfrom
story/2-proxy-route-protection

Conversation

@AlyaEngineer

@AlyaEngineer AlyaEngineer commented Jul 10, 2026

Copy link
Copy Markdown
Owner

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

  • added AUTH_ROUTES handling in src/proxy.ts
  • added src/utils/auth/get-authenticated-user.ts - checks the current session via supabase.auth.getClaims()
  • added src/app/[locale]/unauthorized.tsx - the boundary Next.js renders when unauthorized() is called; delivers a real 401 status on full page navigation, then redirects to the Main page
  • added page stubs for /history, /sign-in, /sign-up to have real routes to test against
  • added unit tests for getAuthenticatedUser, the Unauthorized boundary component, and the AUTH_ROUTES redirect logic in proxy.ts

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
  • Non-auth users see Sign In / Sign Up in header (upper right) — 15
  • Auth users see History / Sign Out in header — 10
  • About link in header and footer — 10
  • Expired/invalid token → redirect from private routes to Main — 10
  • Sign In / Sign Up buttons redirect to respective form route — 15
Feature 2: Sign In / Sign Up
  • Sign In / Sign Up / Sign Out buttons present everywhere they should be — 10
  • Client-side validation (email, password: 8+ chars, letter+digit+special, Unicode) — 20
  • Successful login → redirect to Main — 10
  • Logged-in user on Sign In / Sign Up routes → redirect to Main — 10
Feature 3: Swagger Editor
  • Load/paste schema in JSON and YAML — 25
  • Auto-detect input format (JSON vs YAML) — 20
  • Format switch with JSON ↔ YAML conversion, no data loss — 20
  • Schema validation with error indication — 15
  • Auth users save schema; restored on next login — 10
  • Viewer auto-populates with endpoints on valid schema — 10
  • Responsive split view (horizontal/vertical by orientation) — 20
Feature 4: Swagger Viewer
  • Endpoint list organized by path/method — 20
  • Details: method, path, all param types (path/query/header/cookie) — 25
  • Request schema and example payloads displayed — 20
  • Response schema, examples, all status codes displayed — 25
  • Try-It-Out: fill params/headers/body, execute, show response — 20
  • Generate cURL + copy-to-clipboard — 10
Feature 5: History and Analytics
  • SSR-generated; empty state with links to editor/viewer — 15
  • Requests sorted by timestamp (most recent first) — 10
  • Server-side analytics: duration, status, timestamp, method, request size, response size, error details, endpoint/URL — 45
Feature 6: About Page
  • Public route, accessible to all — 5
  • Info about RS School course — 5
  • Team info (names, roles, GitHub links) — 10
  • Design consistent with the app — 5
Feature 7: General Requirements
  • i18n: 2+ languages with toggler in header — 30
  • Sticky header with animation on stick — 10
  • User-friendly error display — 10
  • Private routes protected (401 if not authenticated) — 5
Feature 8: YouTube Video
  • 5–7 min YouTube walkthrough linked, covers all criteria — 50

Type of change

  • Bug fix (non-breaking change which fixes an issue)
  • New feature (non-breaking change which adds functionality)
  • Breaking change (fix or feature that would break existing functionality)
  • Documentation update

Checklist

  • Self-review done
  • No console errors / warnings / logs
  • No any / @ts-ignore
  • Tests added / updated and passing

Summary by CodeRabbit

Summary by CodeRabbit

  • New Features
    • Added localized About, History, Sign In, and Sign Up pages.
    • Added an authentication utility and Unauthorized page that immediately redirects to the home route.
  • Bug Fixes
    • Updated request proxying to redirect authenticated users away from Sign In/Sign Up to the locale root, preserving relevant cookies/headers.
    • Enabled improved auth interrupt behavior in Next.js configuration.
  • Tests
    • Added/expanded unit and route tests covering authenticated vs. unauthenticated redirects and Unauthorized behavior.

@AlyaEngineer AlyaEngineer added feature-1-header Feature 1: App Header - a header with navigation controls and authentication options feature-2-auth Feature 2: Sign In / Sign Up registration and authentication using email and password feature-7-general Feature 7: General Requirements labels Jul 10, 2026
@vercel

vercel Bot commented Jul 10, 2026

Copy link
Copy Markdown

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

Project Deployment Actions Updated (UTC)
swagger-editor-app Ready Ready Preview, Comment Jul 11, 2026 7:55am

@coderabbitai

coderabbitai Bot commented Jul 10, 2026

Copy link
Copy Markdown

Review Change Stack

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro Plus

Run ID: 5613c141-e1d1-4d1a-af94-ce2508424e9b

📥 Commits

Reviewing files that changed from the base of the PR and between 7e7b8f5 and 13541c8.

📒 Files selected for processing (2)
  • src/proxy.test.ts
  • src/proxy.ts
🚧 Files skipped from review as they are similar to previous changes (2)
  • src/proxy.test.ts
  • src/proxy.ts

📝 Walkthrough

Walkthrough

Adds 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.

Changes

Authentication routing and protected pages

Layer / File(s) Summary
Localized route surfaces
next.config.ts, src/app/[locale]/*
Adds localized About, sign-in, sign-up, and authentication-gated history pages, and enables experimental.authInterrupts.
Server authentication helper
src/utils/auth/get-authenticated-user.ts, src/utils/auth/get-authenticated-user.test.ts
Adds getAuthenticatedUser() with Supabase claims checks and unauthorized() handling, covered by success, missing-claims, and error tests.
Auth-route proxy redirects
src/proxy.ts, src/proxy.test.ts
Refreshes claims, redirects authenticated requests from sign-in and sign-up to the locale-specific root, and preserves refreshed session cookies and headers.
Unauthorized page redirect
src/app/[locale]/unauthorized.tsx, src/app/[locale]/unauthorized.test.tsx
Adds a client component that replaces the current route with / and tests the router redirect.

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
Loading

Suggested reviewers: Ivan-khodorov, lexarudak, a-rudak-EIS

🚥 Pre-merge checks | ✅ 3 | ❌ 2

❌ Failed checks (2 warnings)

Check name Status Explanation Resolution
Out of Scope Changes check ⚠️ Warning The new /about route appears unrelated to the auth protection and redirect objectives. Remove the unrelated /about page from this PR or move it to a separate change focused on that route.
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (3 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly and concisely summarizes the main theme of route-level authentication protection.
Linked Issues check ✅ Passed The changes add auth-route redirects, per-request claim refresh, and /history protection with unauthorized() as requested.
✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch story/2-proxy-route-protection

Comment @coderabbitai help to get the list of available commands.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Actionable comments posted: 4

🧹 Nitpick comments (4)
src/proxy.test.ts (1)

33-39: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Weak 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 win

Use next/dynamic for 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 value

Silent error swallowing before triggering unauthorized().

Unlike proxy.ts, which logs Failed to refresh Supabase session in proxy on error, this path drops error without 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 win

Missing test case: data truthy with error also present.

The !data || error branch in getAuthenticatedUser has an untested combination — non-null data alongside a truthy error. 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

📥 Commits

Reviewing files that changed from the base of the PR and between 8bbfc00 and 9886431.

📒 Files selected for processing (11)
  • next.config.ts
  • src/app/[locale]/about/page.tsx
  • src/app/[locale]/history/page.tsx
  • src/app/[locale]/sign-in/page.tsx
  • src/app/[locale]/sign-up/page.tsx
  • src/app/[locale]/unauthorized.test.tsx
  • src/app/[locale]/unauthorized.tsx
  • src/proxy.test.ts
  • src/proxy.ts
  • src/utils/auth/get-authenticated-user.test.ts
  • src/utils/auth/get-authenticated-user.ts

Comment thread src/proxy.ts Outdated
Comment thread src/proxy.ts Outdated
Comment thread src/utils/auth/get-authenticated-user.ts
Comment thread src/proxy.ts Outdated
Comment thread src/proxy.ts Outdated
lexarudak
lexarudak previously approved these changes Jul 10, 2026

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

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

📥 Commits

Reviewing files that changed from the base of the PR and between 9886431 and 7e7b8f5.

📒 Files selected for processing (4)
  • src/proxy.test.ts
  • src/proxy.ts
  • src/utils/auth/get-authenticated-user.test.ts
  • src/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

Comment thread src/proxy.ts
@AlyaEngineer
AlyaEngineer merged commit 273ae8c into story/2-supabase-setup Jul 11, 2026
3 checks passed
@AlyaEngineer
AlyaEngineer deleted the story/2-proxy-route-protection branch July 11, 2026 08:11
AlyaEngineer added a commit that referenced this pull request Jul 11, 2026
* 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
@coderabbitai coderabbitai Bot mentioned this pull request Jul 12, 2026
42 tasks
AlyaEngineer added a commit that referenced this pull request Jul 12, 2026
* 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
@coderabbitai coderabbitai Bot mentioned this pull request Jul 13, 2026
42 tasks
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

feature-1-header Feature 1: App Header - a header with navigation controls and authentication options feature-2-auth Feature 2: Sign In / Sign Up registration and authentication using email and password feature-7-general Feature 7: General Requirements

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Proxy route protection Token expiration handling Private route protection

3 participants