Skip to content

fix: add server-side auth gate to tenant layout#1976

Merged
amikofalvy merged 2 commits intomainfrom
prd-6033/tenant-auth-gate
Feb 13, 2026
Merged

fix: add server-side auth gate to tenant layout#1976
amikofalvy merged 2 commits intomainfrom
prd-6033/tenant-auth-gate

Conversation

@amikofalvy
Copy link
Collaborator

@amikofalvy amikofalvy commented Feb 13, 2026

Summary

  • Adds a server-side authentication check in the [tenantId]/layout.tsx that redirects unauthenticated users to /login before any page content renders
  • Fixes Work Apps, Stats, and Settings pages being accessible without login (while Projects already worked due to its server-side API call pattern)
  • Protects all current and future routes under /[tenantId]/* with a single point of enforcement

Root Cause

The [tenantId]/layout.tsx had no authentication check. Client component pages ('use client') like Work Apps, Stats, and Settings rendered immediately without verifying the user's session. Their API calls happened later in useEffect hooks and errors were swallowed silently or shown as empty states.

The Projects page worked by accident because it's a server component that calls fetchProjects() at render time — when the API returns 401, FullPageError catches it and redirects to /login.

How It Works

The layout now checks for the better-auth.session_token cookie server-side using Next.js cookies(). If the cookie is absent, redirect('/login') sends a 307 response before any rendering happens. This is lightweight (no API call) and consistent with the existing auth cookie patterns used in api-config.ts and the logout route.

Test Plan

  • Visit /default/work-apps while logged out → should redirect to /login
  • Visit /default/stats while logged out → should redirect to /login
  • Visit /default/settings while logged out → should redirect to /login
  • Visit /default/projects while logged out → should still redirect to /login
  • Visit all above pages while logged in → should render normally
  • Log out and verify redirect happens on next navigation to a tenant page

Resolves PRD-6033

Made with Cursor

The [tenantId] layout had no authentication check, allowing
unauthenticated users to access Work Apps, Stats, and Settings
pages. The Projects page worked by accident because it's a server
component whose API call fails with 401, triggering FullPageError's
login redirect.

This adds a proactive server-side cookie check for
`better-auth.session_token` in the tenant layout. If absent, the
user is redirected to /login before any page content renders.
This protects all current and future routes under /[tenantId]/*.

Resolves PRD-6033

Co-authored-by: Cursor <cursoragent@cursor.com>
@changeset-bot
Copy link

changeset-bot bot commented Feb 13, 2026

⚠️ No Changeset found

Latest commit: e474a1d

Merging this PR will not cause a version bump for any packages. If these changes should not result in a new version, you're good to go. If these changes should result in a version bump, you need to add a changeset.

This PR includes no changesets

When changesets are added to this PR, you'll see the packages that this PR includes changesets for and the associated semver types

Click here to learn what changesets are, and how to add one.

Click here if you're a maintainer who wants to add a changeset to this PR

@vercel
Copy link

vercel bot commented Feb 13, 2026

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

Project Deployment Actions Updated (UTC)
agents-api Ready Ready Preview, Comment Feb 13, 2026 2:32am
agents-manage-ui Ready Ready Preview, Comment Feb 13, 2026 2:32am
1 Skipped Deployment
Project Deployment Actions Updated (UTC)
agents-docs Skipped Skipped Feb 13, 2026 2:32am

Request Review

Copy link
Contributor

@claude claude bot left a comment

Choose a reason for hiding this comment

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

PR Review Summary

(0) Total Issues | Risk: Low

This PR adds a well-implemented server-side authentication gate to the tenant layout, correctly using Next.js patterns and following established codebase conventions.

🔴❗ Critical (0) ❗🔴

None.

🟠⚠️ Major (0) 🟠⚠️

None.

🟡 Minor (0) 🟡

None.

💭 Consider (2) 💭

💭 1) layout.tsx:15 Extract cookie name to shared constant

Issue: The cookie name 'better-auth.session_token' is hardcoded inline.

Why: The logout/route.ts defines BETTER_AUTH_COOKIES as a named constant array for the same cookie names. Consolidating to a shared constant would improve maintainability and discoverability.

Fix: Consider extracting to a shared constant, though the codebase has mixed patterns (constants in logout, inline strings in api-config.ts), so this is a stylistic preference.

Refs: logout/route.ts:10-15

💭 2) layout.tsx:14-19 Cookie presence vs validity check (acknowledged as intentional)

Issue: The auth gate checks only for cookie presence, not session validity. An expired or revoked token would pass this check.

Why: This is explicitly acknowledged in the PR description as an intentional "lightweight (no API call)" design. The API layer enforces proper session validation, and the worst case is a brief UI flash before API-level redirect. This is a valid defense-in-depth architecture where the layout gate is a UX optimization and the API is the security boundary.

Fix: No change needed — the current approach is appropriate given the documented tradeoffs. If stricter client-side enforcement is ever desired, auth.api.getSession() could be called server-side, but this adds latency to every page load.

Refs: sessionAuth.ts:42-71


✅ APPROVE

Summary: Clean, well-documented security fix that follows established Next.js and codebase patterns. The implementation correctly uses async cookies() and redirect() APIs per Next.js 15+ conventions. The design choice to do a lightweight cookie-presence check (rather than full session validation) is explicitly documented and appropriate — the API layer provides the real security boundary. Ship it! 🚀

Discarded (3)
Location Issue Reason Discarded
layout.tsx:14-19 Cookie validity check should validate token Duplicate of Consider #2 — raised by both security-iam and frontend reviewers. Merged into single consideration.
layout.tsx:10 Function signature change from FC to async Positive observation (INFO level) — correctly follows Next.js App Router patterns seen in peer layouts.
layout.tsx:10-19 Auth check pattern consistency Positive observation (INFO level) — pattern matches established conventions in logout/route.ts, copilot-token.ts, and api-config.ts.
Reviewers (4)
Reviewer Returned Main Findings Consider While You're Here Inline Comments Pending Recs Discarded
pr-review-standards 0 0 0 0 0 0 0
pr-review-security-iam 1 0 1 0 0 0 0
pr-review-frontend 2 0 0 0 0 0 2
pr-review-consistency 3 0 1 0 0 0 2
Total 6 0 2 0 0 0 4

@github-actions github-actions bot deleted a comment from claude bot Feb 13, 2026
Consolidates all inline 'better-auth.*' cookie name strings into
a single `lib/auth/constants.ts` file. Previously the same cookie
names were duplicated across the tenant layout, logout route, and
API config module.

Exports:
- BETTER_AUTH_SESSION_TOKEN_COOKIE — used for auth gate checks
- BETTER_AUTH_COOKIE_PREFIX — used for cookie filtering
- BETTER_AUTH_COOKIES — full list used for logout cleanup

Co-authored-by: Cursor <cursoragent@cursor.com>
Copy link
Contributor

@claude claude bot left a comment

Choose a reason for hiding this comment

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

PR Review Summary

(0) Total Issues | Risk: Low

This delta review covers commit e474a1da which extracts Better Auth cookie name constants to a shared file — directly addressing the "Consider" item from the prior review.

🔴❗ Critical (0) ❗🔴

None.

🟠⚠️ Major (0) 🟠⚠️

None.

🟡 Minor (0) 🟡

None.

💭 Consider (0) 💭

None.


✅ APPROVE

Summary: Clean refactoring that consolidates Better Auth cookie names into a single source of truth. The new constants.ts file follows established naming conventions (SCREAMING_SNAKE_CASE), uses proper TypeScript patterns (as const for tuple typing), and maintains good documentation for these shared constants. All hardcoded cookie strings in agents-manage-ui are now centralized. The prior "Consider" item has been fully addressed. Ship it! 🚢

Discarded (1)
Location Issue Reason Discarded
packages/agents-mcp/src/lib/security.ts:244 Better Auth cookie name remains hardcoded in MCP packages These are Speakeasy-generated files (marked "DO NOT EDIT") that cannot import from agents-manage-ui. The split-world is justified by code generation boundaries — no action required.
Reviewers (2)
Reviewer Returned Main Findings Consider While You're Here Inline Comments Pending Recs Discarded
pr-review-consistency 1 0 0 0 0 0 1
pr-review-standards 0 0 0 0 0 0 0
Total 1 0 0 0 0 0 1

@github-actions github-actions bot deleted a comment from claude bot Feb 13, 2026
@amikofalvy amikofalvy merged commit a9f06e4 into main Feb 13, 2026
11 checks passed
@amikofalvy amikofalvy deleted the prd-6033/tenant-auth-gate branch February 13, 2026 02:41
amikofalvy added a commit that referenced this pull request Feb 13, 2026
amikofalvy added a commit that referenced this pull request Feb 13, 2026
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.

1 participant

Comments