Skip to content

feat(web): authentication with Firebase, NextAuth v5, and sidebar dashboard#42

Merged
GRACENOBLE merged 3 commits into
mainfrom
37-feat-web-auth
Jun 24, 2026
Merged

feat(web): authentication with Firebase, NextAuth v5, and sidebar dashboard#42
GRACENOBLE merged 3 commits into
mainfrom
37-feat-web-auth

Conversation

@GRACENOBLE

@GRACENOBLE GRACENOBLE commented Jun 24, 2026

Copy link
Copy Markdown
Owner

Closes #37

Summary

  • Auth layer: NextAuth v5 (Auth.js) with a Firebase-backed Credentials provider — Google OAuth and email/password both sign in via Firebase Auth SDK, then exchange the ID token for a NextAuth JWT session
  • Route protection: proxy.ts (Next.js 16 convention) guards /dashboard/* and /settings/*, redirecting unauthenticated users to /login
  • tRPC integration: protectedProcedure now validates the NextAuth session via auth() instead of the previous Bearer/cookie stub
  • Dashboard: shadcn Sidebar layout with collapsible trigger, nav items (Dashboard, Settings), and UserMenu (Google avatar + sign-out dropdown) in the footer
  • Pages: /login, /register, /dashboard, /settings — all wired up and protected
  • UI: Manrope replaces Geist Sans globally; Sonner toasts replace inline error state in all auth forms
  • Config: lh3.googleusercontent.com added to next.config.ts image remote patterns for Google profile pictures; both .env.example files verified complete

Test plan

  • Fill in web/.env Firebase values, run pnpm dev, verify Google Sign-In popup works end-to-end
  • Register with email/password (requires Firebase email auth enabled in Console)
  • Visit /dashboard while logged out — confirm redirect to /login
  • Visit /settings while logged out — confirm redirect to /login
  • Sign in, confirm avatar appears in sidebar footer with correct name/email
  • Sign out via sidebar dropdown, confirm redirect to /login
  • pnpm lint && pnpm build && pnpm test — 79 tests, 0 errors

🤖 Generated with Claude Code

Summary by CodeRabbit

  • New Features
    • Added authentication pages for sign in and registration.
    • Added a protected dashboard and settings area with sidebar navigation, user menu, and sign-out flow.
    • Added Google sign-in, email/password forms, and session-aware app header/footer behavior.
  • Bug Fixes
    • Improved auth protection so restricted pages redirect unauthenticated visitors to sign in.
    • Updated app/session handling to show the correct user state across the UI.
  • Documentation
    • Added and updated docs for authentication, data fetching, and app usage.

…hboard (closes #37)

- NextAuth v5 (Auth.js) with a single Credentials provider that accepts
  Firebase ID tokens — Google OAuth and email/password both flow through
  Firebase Auth SDK on the client, then exchange the ID token for a
  NextAuth session
- proxy.ts (Next.js 16 convention) protects /dashboard and /settings,
  redirecting unauthenticated users to /login
- tRPC protectedProcedure updated to use NextAuth auth() for session
  validation; TRPCContext now carries Session | null
- Login, Register, and Google Sign-In pages with shadcn Card + Form +
  react-hook-form + Zod validation; errors surfaced via Sonner toasts
- Dashboard layout with shadcn Sidebar (AppSidebar), collapsible trigger,
  and UserMenu (avatar + dropdown) in the sidebar footer
- Settings page added so sidebar nav link resolves without 404
- Header component wired into home page; "App" logo links home
- Manrope replaces Geist Sans as the global font
- next.config.ts allows lh3.googleusercontent.com for Google profile pics
- Shared lib/firebase.ts initialises the Firebase app and exports
  getFirebaseAuth() for use across client components
- .env.example updated with all required vars; both backend and web
  examples verified complete
- 79 Vitest tests pass (19 test files)
@github-actions github-actions Bot added area: web Next.js web app type: chore Cleanup or maintenance tasks labels Jun 24, 2026
@coderabbitai

coderabbitai Bot commented Jun 24, 2026

Copy link
Copy Markdown

Review Change Stack

Warning

Review limit reached

@GRACENOBLE, we couldn't start this review because you've reached your PR review rate limit.

More reviews will be available in 39 minutes and 25 seconds. Learn how PR review limits work.

Your organization has run out of usage credits. Purchase more credits in the billing tab to continue.

⌛ How to resolve this issue?

After more reviews become available, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

To avoid repeated limits, reduce automatic review volume by pausing incremental auto-reviews earlier, using label-based review opt-in, excluding WIP or generated PR titles, or requesting reviews manually when the PR is ready. If your team needs uninterrupted high-volume reviews, an organization admin can enable usage-based credits.

🚦 How do rate limits work?

CodeRabbit enforces per-developer PR review limits for each organization. Most developers receive the normal plan review availability.

For paid Pro and Pro+ PR reviews, CodeRabbit uses adaptive limits for sustained high-volume activity. When a developer's recent PR review activity reaches the 95th percentile or higher among CodeRabbit users, additional reviews become available more gradually as earlier reviews age out of the rolling window.

Please see our Fair Usage Limits Policy for further information.

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 1be2a9da-84e0-41a4-baea-889dded6ac65

📥 Commits

Reviewing files that changed from the base of the PR and between f62883e and 4e491c6.

⛔ Files ignored due to path filters (1)
  • web/pnpm-lock.yaml is excluded by !**/pnpm-lock.yaml
📒 Files selected for processing (14)
  • web/.env.example
  • web/__mocks__/next/image.tsx
  • web/app/(dashboard)/layout.tsx
  • web/auth.ts
  • web/components/layout/__tests__/NavAuth.test.tsx
  • web/docs/_index.md
  • web/docs/auth.md
  • web/features/auth/components/GoogleSignInButton.tsx
  • web/features/auth/hooks/useSignOut.ts
  • web/features/auth/validation.ts
  • web/hooks/use-mobile.ts
  • web/lib/firebase-admin.ts
  • web/package.json
  • web/proxy.ts
📝 Walkthrough

Walkthrough

Adds a complete authentication layer to the web app: NextAuth v5 credentials provider backed by Firebase Auth, Zod-validated login/register forms with Google OAuth support, proxy middleware and server-component redirects for route protection, a session-aware tRPC context replacing header/cookie auth, a sidebar-based dashboard layout, and supporting UI primitives (Form, Sheet, Tooltip, Sidebar).

Changes

Authentication Feature — NextAuth v5 + Firebase + Protected Routes

Layer / File(s) Summary
Auth types, validation schemas, Firebase helper, and env config
web/features/auth/types.ts, web/features/auth/validation.ts, web/lib/firebase.ts, web/.env.example, web/package.json
Defines AuthUser/AuthSession interfaces, Zod loginSchema/registerSchema with inferred form types, a lazy-init getFirebaseAuth() helper, .env.example entries for AUTH_SECRET and Firebase vars, and production/dev dependency additions (next-auth, react-hook-form, @hookform/resolvers, @testing-library/user-event).
NextAuth config, tRPC session context, and route protection
web/auth.ts, web/app/api/auth/[...nextauth]/route.ts, web/proxy.ts, web/server/trpc.ts, web/server/routers/auth.ts, web/app/providers.tsx, web/next.config.ts
auth.ts implements a credentials provider that validates idToken with Zod, decodes the JWT payload, and maps claims to the NextAuth user shape. proxy.ts middleware redirects unauthenticated requests to /login for dashboard/settings routes. createTRPCContext now calls auth() to populate session, and protectedProcedure authorizes via ctx.session?.user. authRouter.session returns the session user. Providers wraps with SessionProvider and Toaster.
useSession hook, useSignOut hook, and UserMenu
web/features/auth/hooks/useSession.ts, web/features/auth/hooks/useSignOut.ts, web/features/auth/components/UserMenu.tsx
useSession wraps next-auth's hook and normalizes {user, isAuthenticated, isLoading}. useSignOut calls signOut({redirect:false}) then pushes to /login. UserMenu renders an Avatar-based dropdown with computed initials and a Sign out action.
GoogleSignInButton, LoginForm, and RegisterForm components
web/features/auth/components/GoogleSignInButton.tsx, web/features/auth/components/LoginForm.tsx, web/features/auth/components/RegisterForm.tsx
GoogleSignInButton performs Firebase popup OAuth, extracts an ID token, and exchanges it for a NextAuth session. LoginForm uses react-hook-form with loginSchema for Firebase email/password sign-in followed by NextAuth credential exchange. RegisterForm creates the Firebase user, updates the display name, retrieves an ID token, and signs into NextAuth, with specific handling for auth/email-already-in-use.
Login and register pages
web/app/(auth)/login/page.tsx, web/app/(auth)/register/page.tsx
LoginPage and RegisterPage each render a centered card with GoogleSignInButton, an "or" separator, their respective forms, and a cross-link between /login and /register.
Form, Sheet, and Tooltip UI primitives
web/components/ui/form.tsx, web/components/ui/sheet.tsx, web/components/ui/tooltip.tsx
form.tsx integrates react-hook-form Controller with React context to auto-wire accessibility ids and aria-invalid across FormField, FormItem, FormLabel, FormControl, FormDescription, and FormMessage. sheet.tsx wraps Radix Dialog with configurable side and showCloseButton. tooltip.tsx wraps Radix Tooltip with portal rendering and a styled arrow.
Sidebar UI system and useIsMobile hook
web/components/ui/sidebar.tsx, web/hooks/use-mobile.ts
useIsMobile tracks a 768 px matchMedia breakpoint. SidebarProvider manages expand/collapse state persisted to a cookie with a Ctrl/Meta+B keyboard shortcut. Sidebar renders as a Sheet on mobile and as offcanvas/floating/inset on desktop. Exports the full subcomponent suite including SidebarMenuButton with collapsed-state-gated tooltip support.
Dashboard layout, protected pages, nav components, and app wiring
web/app/(dashboard)/layout.tsx, web/app/(dashboard)/dashboard/page.tsx, web/app/(dashboard)/settings/page.tsx, web/components/layout/AppSidebar.tsx, web/components/layout/NavAuth.tsx, web/components/layout/header.tsx, web/components/layout/footer.tsx, web/app/layout.tsx, web/app/page.tsx
DashboardLayout wraps content with SidebarProvider, AppSidebar, and SidebarInset. Dashboard and settings pages call auth() and redirect unauthenticated users. NavAuth conditionally renders a skeleton, UserMenu, or Sign in button. Header adds a sticky nav with NavAuth. Root layout switches to Manrope font; homepage adds Footer.
Tests and documentation
web/features/auth/__tests__/*, web/components/layout/__tests__/NavAuth.test.tsx, web/server/routers/__tests__/auth.test.ts, web/server/routers/__tests__/health.test.ts, web/lib/trpc/__tests__/server.test.ts, web/__tests__/page.test.tsx, web/docs/auth.md, web/docs/trpc.md, web/docs/data-fetching.md, web/docs/_index.md
Adds Vitest suites for NavAuth, GoogleSignInButton, LoginForm, RegisterForm, useSession, and Zod schemas. Rewrites auth.test.ts to use session mocking via mockAuth.mockResolvedValue. Health and tRPC server tests gain @/auth mocks. docs/auth.md added in full; trpc.md updated for session-based context and testing patterns.

Sequence Diagram(s)

sequenceDiagram
  participant User
  participant LoginForm
  participant FirebaseAuth as Firebase Auth
  participant NextAuth as NextAuth (credentials)
  participant proxy_ts as proxy.ts middleware
  participant DashboardPage as Dashboard Server Component

  User->>LoginForm: submit email + password
  LoginForm->>FirebaseAuth: signInWithEmailAndPassword
  FirebaseAuth-->>LoginForm: Firebase User
  LoginForm->>FirebaseAuth: user.getIdToken()
  FirebaseAuth-->>LoginForm: idToken (JWT)
  LoginForm->>NextAuth: signIn('credentials', { idToken, redirect: false })
  NextAuth->>NextAuth: authorize — decode JWT payload, validate sub
  NextAuth-->>LoginForm: { ok: true }
  LoginForm->>User: router.push('/dashboard')
  User->>proxy_ts: GET /dashboard
  proxy_ts->>NextAuth: auth(req)
  NextAuth-->>proxy_ts: session | null
  alt no session
    proxy_ts-->>User: redirect /login?callbackUrl=/dashboard
  else authenticated
    proxy_ts->>DashboardPage: pass through
    DashboardPage->>NextAuth: await auth()
    NextAuth-->>DashboardPage: session
    DashboardPage-->>User: render "Welcome back, {name}"
  end
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~60 minutes

Possibly related PRs

  • GRACENOBLE/fullstack-template#41: Established the initial Bearer/cookie-based protectedProcedure and TRPCContext in web/server/trpc.ts that this PR replaces with NextAuth session-based authorization.
  • GRACENOBLE/fullstack-template#13: Set up the Vitest testing infrastructure that web/__tests__/page.test.tsx and all new auth test files build on top of.

Suggested labels

area: web

🐇 Hoppity-hop, the tokens flow,
A Firebase idToken steals the show.
NextAuth decodes the JWT split in three,
The dashboard guards say "Authenticated? Free!"
Credentials, sessions, and cookies in place —
This bunny ships auth at full-speed pace! 🔐✨

🚥 Pre-merge checks | ✅ 3 | ❌ 2

❌ Failed checks (2 warnings)

Check name Status Explanation Resolution
Linked Issues check ⚠️ Warning The PR covers auth UI, session wiring, and route protection, but it does not show the required Go-backend credential exchange or Google OAuth env vars. Route email/password auth through the Go backend as specified, add the Google OAuth env vars, and verify the protected-route flow matches the issue.
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 summarizes the main auth and dashboard changes in the PR.
Out of Scope Changes check ✅ Passed Most changes support auth and the dashboard, and no clearly unrelated code is evident from the summary.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch 37-feat-web-auth

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

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: 12

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
web/features/auth/components/UserMenu.tsx (1)

1-56: 📐 Maintainability & Code Quality | 🟡 Minor

Add a render test for UserMenu

web/features/auth/components/UserMenu.tsx needs its own Vitest + @testing-library/react render test. The current NavAuth test mocks UserMenu, so it doesn’t cover this client component directly.

🤖 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 `@web/features/auth/components/UserMenu.tsx` around lines 1 - 56, Add a
dedicated Vitest render test for UserMenu so this client component is covered
directly instead of only through NavAuth mocks. Create a test that renders
UserMenu with mocked useSession and useSignOut, verifies the authenticated state
shows the avatar/menu trigger and user details, and confirms the sign-out action
is wired through DropdownMenuItem. Use the UserMenu, useSession, and useSignOut
symbols to locate the component and keep the test focused on its own rendering
behavior.

Source: Coding guidelines

🧹 Nitpick comments (2)
web/components/layout/AppSidebar.tsx (1)

35-44: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value

No active-route indication in the sidebar nav.

SidebarMenuButton exposes an isActive prop (driving data-active styling in sidebar.tsx), but it's never set, so the current route is not highlighted. Consider deriving the active item from usePathname() (requires "use client").

♻️ Sketch
-                  <SidebarMenuButton asChild>
+                  <SidebarMenuButton asChild isActive={pathname === item.url}>
                     <Link href={item.url}>
🤖 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 `@web/components/layout/AppSidebar.tsx` around lines 35 - 44, The sidebar nav
currently never marks the active route, so `SidebarMenuButton`’s `data-active`
styling is not applied. Update `AppSidebar` to be a client component, derive the
current path with `usePathname()`, and pass `isActive` to each
`SidebarMenuButton` by comparing the pathname to each `navItems` entry (using
the existing `item.url` and `navItems.map` render).
web/components/ui/sidebar.tsx (1)

181-205: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value

Spreading ...props onto Sheet instead of SheetContent.

On the mobile branch, {...props} (typed as React.ComponentProps<"div">) is forwarded to the Sheet root rather than SheetContent. Any className/HTML div attributes a caller passes to Sidebar won't reach the rendered content element and may not be valid on the Radix Dialog root. Forwarding them to SheetContent matches the desktop branch 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 `@web/components/ui/sidebar.tsx` around lines 181 - 205, The mobile branch in
Sidebar is forwarding { ...props } to Sheet instead of the rendered content
container, so div-style props like className never reach the actual sidebar
element. Update the mobile path in sidebar.tsx to pass the spread props through
SheetContent, matching the desktop branch behavior and keeping Sheet as only the
dialog root. Use the existing Sidebar, Sheet, and SheetContent structure as the
reference point when moving the props.
🤖 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 `@web/auth.ts`:
- Around line 9-32: The authorize flow in auth.ts is only decoding the JWT
payload and must be changed to verify the Firebase ID token before creating a
session. Replace the manual Buffer/JSON.parse logic in authorize with
firebase-admin/auth verifyIdToken() so signature and standard claims are
checked, then build the returned user from the verified decoded claims (for
example sub, email, name, picture) after successful verification. Keep the
existing zod credential validation and null returns for invalid tokens.

In `@web/components/layout/__tests__/NavAuth.test.tsx`:
- Around line 22-34: The `NavAuth` test mocks are using the wrong `useSession`
return shape, which makes them incompatible with `UseSessionReturn`. Update the
`mockUseSession.mockReturnValue` calls in `NavAuth.test.tsx` to pass `user`,
`isAuthenticated`, and `isLoading` instead of `session`, keeping the
authenticated case’s user email data under `user` so the `NavAuth` and
`useSession` types line up.

In `@web/components/ui/sidebar.tsx`:
- Around line 55-88: The sidebar persistence is only writing to the cookie in
SidebarProvider but never initializing from it, so reloads always fall back to
the hard-coded defaultOpen value. Update the component flow around
SidebarProvider so the saved sidebar_state cookie is read before rendering (for
example in DashboardLayout using next/headers) and passed into SidebarProvider
as defaultOpen, ensuring the initial open state matches the persisted value.

In `@web/docs/_index.md`:
- Line 18: The docs index entry for the Authentication page points to the wrong
source file, so update the sources list in the `_index.md` row for the NextAuth
v5 documentation to reference `proxy.ts` instead of `middleware.ts`; keep the
rest of the row aligned with the actual implementation symbols used by the auth
flow, such as `auth.ts` and `features/auth/`.

In `@web/docs/auth.md`:
- Around line 176-186: Update the auth docs to match the Firebase-backed client
flow: revise LoginForm to describe `signInWithEmailAndPassword`, ID token
retrieval, then `signIn('credentials', { redirect: false })`, and replace the
inline error note with Sonner toast handling; revise RegisterForm to describe
`createUserWithEmailAndPassword`, `updateProfile` for `displayName`, then the
same credentials sign-in instead of posting to
`${NEXT_PUBLIC_API_URL}/auth/register`; revise GoogleSignInButton to mention
Firebase `signInWithPopup` followed by credentials sign-in rather than
`signIn('google', ...)`. Keep the changes anchored to the `LoginForm`,
`RegisterForm`, and `GoogleSignInButton` sections.
- Around line 26-53: The auth docs are out of sync with the actual `auth.ts`
flow, so update the “Providers” section to match the implementation. Remove the
claim that NextAuth uses a Google provider with `AUTH_GOOGLE_ID` /
`AUTH_GOOGLE_SECRET`, and describe that Google sign-in happens via Firebase on
the client before reaching the single Credentials provider. Also rewrite the
Credentials description to reflect `authorize` validating an `idToken`,
base64url-decoding the Firebase JWT payload, and returning `{ id, email, name,
image }` or `null`, instead of email/password Zod validation plus a backend
POST.
- Around line 91-96: The auth docs example is incomplete because it only shows
the matcher for dashboard routes even though proxy.ts also protects settings
routes. Update the config snippet referenced by export const config to include
both /dashboard/:path* and /settings/:path* so the documented example matches
the actual protected paths.

In `@web/features/auth/components/GoogleSignInButton.tsx`:
- Around line 13-23: Update handleClick in GoogleSignInButton to treat popup
cancellation as a non-error by catching the signInWithPopup flow and suppressing
toast.error when the Firebase auth error code is auth/popup-closed-by-user or
auth/cancelled-popup-request. Also add an in-flight loading/disabled state
around the button and set it while handleClick is running so repeated clicks
cannot open multiple popups.

In `@web/features/auth/hooks/useSignOut.ts`:
- Around line 9-11: The sign-out flow in useSignOut only calls nextAuthSignOut
and then navigates to /login, so Firebase remains authenticated. Update the
signOut function to also call firebaseSignOut(getFirebaseAuth()) in the same
logout path, keeping the existing router.push('/login') behavior and using the
Firebase auth helper alongside nextAuthSignOut.

In `@web/features/auth/validation.ts`:
- Line 4: The email validation schemas in the auth validation module are using
the deprecated string-based email helper. Update both schema definitions in
validation.ts to use the top-level z.email with the same message, and keep the
existing validation behavior unchanged while replacing the deprecated
z.string().email usage.

In `@web/hooks/use-mobile.ts`:
- Around line 5-19: The useIsMobile hook currently sets React state
synchronously inside the mounting useEffect, which triggers the
react-hooks/set-state-in-effect warning. Refactor useIsMobile to read the
media-query value through a subscription-based snapshot approach such as
useSyncExternalStore instead of calling setIsMobile in the effect, and keep the
window.matchMedia listener logic encapsulated in the hook so the mobile state
stays in sync without effect-time state updates.

In `@web/proxy.ts`:
- Around line 7-9: The login redirect in the proxy drops the original query
string because it uses req.nextUrl.pathname for callbackUrl. Update the redirect
logic in the proxy handler to build callbackUrl from req.nextUrl.pathname plus
req.nextUrl.search, so users return to the same route with its params after
login.

---

Outside diff comments:
In `@web/features/auth/components/UserMenu.tsx`:
- Around line 1-56: Add a dedicated Vitest render test for UserMenu so this
client component is covered directly instead of only through NavAuth mocks.
Create a test that renders UserMenu with mocked useSession and useSignOut,
verifies the authenticated state shows the avatar/menu trigger and user details,
and confirms the sign-out action is wired through DropdownMenuItem. Use the
UserMenu, useSession, and useSignOut symbols to locate the component and keep
the test focused on its own rendering behavior.

---

Nitpick comments:
In `@web/components/layout/AppSidebar.tsx`:
- Around line 35-44: The sidebar nav currently never marks the active route, so
`SidebarMenuButton`’s `data-active` styling is not applied. Update `AppSidebar`
to be a client component, derive the current path with `usePathname()`, and pass
`isActive` to each `SidebarMenuButton` by comparing the pathname to each
`navItems` entry (using the existing `item.url` and `navItems.map` render).

In `@web/components/ui/sidebar.tsx`:
- Around line 181-205: The mobile branch in Sidebar is forwarding { ...props }
to Sheet instead of the rendered content container, so div-style props like
className never reach the actual sidebar element. Update the mobile path in
sidebar.tsx to pass the spread props through SheetContent, matching the desktop
branch behavior and keeping Sheet as only the dialog root. Use the existing
Sidebar, Sheet, and SheetContent structure as the reference point when moving
the props.
🪄 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: defaults

Review profile: CHILL

Plan: Pro

Run ID: 8fdc2732-d01b-4db2-83b6-dacf6b0113b4

📥 Commits

Reviewing files that changed from the base of the PR and between 290ca07 and f62883e.

⛔ Files ignored due to path filters (1)
  • web/pnpm-lock.yaml is excluded by !**/pnpm-lock.yaml
📒 Files selected for processing (48)
  • web/.env.example
  • web/__tests__/page.test.tsx
  • web/app/(auth)/login/page.tsx
  • web/app/(auth)/register/page.tsx
  • web/app/(dashboard)/dashboard/page.tsx
  • web/app/(dashboard)/layout.tsx
  • web/app/(dashboard)/settings/page.tsx
  • web/app/api/auth/[...nextauth]/route.ts
  • web/app/layout.tsx
  • web/app/page.tsx
  • web/app/providers.tsx
  • web/auth.ts
  • web/components/layout/AppSidebar.tsx
  • web/components/layout/NavAuth.tsx
  • web/components/layout/__tests__/NavAuth.test.tsx
  • web/components/layout/footer.tsx
  • web/components/layout/header.tsx
  • web/components/ui/form.tsx
  • web/components/ui/sheet.tsx
  • web/components/ui/sidebar.tsx
  • web/components/ui/tooltip.tsx
  • web/docs/_index.md
  • web/docs/auth.md
  • web/docs/data-fetching.md
  • web/docs/trpc.md
  • web/features/auth/__tests__/GoogleSignInButton.test.tsx
  • web/features/auth/__tests__/LoginForm.test.tsx
  • web/features/auth/__tests__/RegisterForm.test.tsx
  • web/features/auth/__tests__/useSession.test.ts
  • web/features/auth/__tests__/validation.test.ts
  • web/features/auth/components/GoogleSignInButton.tsx
  • web/features/auth/components/LoginForm.tsx
  • web/features/auth/components/RegisterForm.tsx
  • web/features/auth/components/UserMenu.tsx
  • web/features/auth/hooks/useSession.ts
  • web/features/auth/hooks/useSignOut.ts
  • web/features/auth/types.ts
  • web/features/auth/validation.ts
  • web/hooks/use-mobile.ts
  • web/lib/firebase.ts
  • web/lib/trpc/__tests__/server.test.ts
  • web/next.config.ts
  • web/package.json
  • web/proxy.ts
  • web/server/routers/__tests__/auth.test.ts
  • web/server/routers/__tests__/health.test.ts
  • web/server/routers/auth.ts
  • web/server/trpc.ts

Comment thread web/auth.ts
Comment thread web/components/layout/__tests__/NavAuth.test.tsx Outdated
Comment thread web/components/ui/sidebar.tsx
Comment thread web/docs/_index.md Outdated
Comment thread web/docs/auth.md Outdated
Comment thread web/features/auth/components/GoogleSignInButton.tsx
Comment thread web/features/auth/hooks/useSignOut.ts
Comment thread web/features/auth/validation.ts Outdated
Comment thread web/hooks/use-mobile.ts
Comment thread web/proxy.ts
GRACENOBLE and others added 2 commits June 24, 2026 18:19
- Move initial isMobile read into useState initializer to avoid
  calling setState synchronously inside useEffect body
- Suppress @next/next/no-img-element warning in test mock (intentional)

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Security:
- Verify Firebase ID tokens with firebase-admin verifyIdToken() instead
  of decoding the JWT payload without signature verification; add
  lib/firebase-admin.ts with singleton Admin app init
- Sign out of Firebase client alongside NextAuth in useSignOut hook to
  prevent stale Firebase auth state after logout

Correctness:
- Preserve query string in callbackUrl (proxy.ts) so users are returned
  to their original URL with params after login
- Restore sidebar open/close state from cookie in DashboardLayout by
  reading the sidebar_state cookie server-side and passing as defaultOpen
- Fix NavAuth test mock shape: use user instead of session to match the
  UseSessionReturn type
- Handle popup-dismissed Firebase errors silently in GoogleSignInButton;
  disable button while sign-in is in flight

Quality:
- Switch to z.email() (Zod v4.4.3+) from deprecated z.string().email()
- Update docs/auth.md to accurately describe the Firebase idToken flow
- Fix stale middleware.ts reference in docs/_index.md to proxy.ts
- Add FIREBASE_PROJECT_ID and FIREBASE_SERVICE_ACCOUNT_JSON to
  web/.env.example

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
@GRACENOBLE GRACENOBLE merged commit a9940c6 into main Jun 24, 2026
3 checks passed
@GRACENOBLE GRACENOBLE deleted the 37-feat-web-auth branch June 24, 2026 15:28
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

area: web Next.js web app type: chore Cleanup or maintenance tasks

Projects

None yet

Development

Successfully merging this pull request may close these issues.

feat(web): authentication — Google OAuth and email/password via Next.js middleware

1 participant