Skip to content

auth: support Vercel preview origins - #239

Merged
Kzoeps merged 2 commits into
stagingfrom
auth/vercel-preview-origins
Jul 30, 2026
Merged

auth: support Vercel preview origins#239
Kzoeps merged 2 commits into
stagingfrom
auth/vercel-preview-origins

Conversation

@Kzoeps

@Kzoeps Kzoeps commented Jul 30, 2026

Copy link
Copy Markdown
Contributor

Summary

  • resolve the canonical OAuth origin as PUBLIC_URLVERCEL_BRANCH_URLVERCEL_URL
  • allow exact same-origin CSRF requests on configured public, branch, and commit deployment origins
  • keep branch and commit deployments isolated by requiring the source origin to equal the request destination
  • validate URL configuration and produce actionable OAuth errors
  • cover config precedence, CSRF behavior, OAuth metadata, and loopback handling

Behavior

When PUBLIC_URL exists, OAuth metadata and callbacks continue using it. Otherwise the stable Vercel branch URL is used, with the commit deployment URL as the final fallback. A login started on another accepted Vercel origin completes on the selected canonical origin and receives its session cookie there.

Review documents

Breaking changes

None.

Out of scope

  • wildcard *.vercel.app trust
  • request-header-derived OAuth client identities
  • cross-origin session handoff back to an initiating commit URL
  • OAuth Redis key migration or client_id namespacing
  • Vercel Deployment Protection configuration

Rollout constraints

  • the canonical OAuth metadata endpoint, and JWKS endpoint for confidential clients, must be publicly reachable by the authorization server
  • deployments participating in one callback flow must share compatible Redis configuration
  • distinct canonical OAuth identities should use separate Redis databases because saved OAuth sessions are not namespaced by client_id

Test plan

  • npm test — 145 files, 1200 tests passed
  • npx tsc --noEmit
  • npx eslint src/ --ext .ts,.tsx
  • npx next build
  • git diff --check origin/staging..HEAD
  • verify login from the deployed branch URL after Vercel builds this PR
  • verify the published OAuth metadata and JWKS endpoints are reachable without deployment credentials

Summary by CodeRabbit

  • Security Improvements

    • Strengthened CSRF validation by requiring exact, trusted origins and rejecting missing, malformed, wildcard, or mismatched origins.
    • Added secure handling for development loopback origins.
  • OAuth & Configuration

    • Added canonical URL resolution across production, Vercel deployments, and local development.
    • Enforced publicly reachable OAuth metadata and HTTPS requirements in production.
    • Improved loopback OAuth redirect handling.
  • Documentation

    • Expanded setup and deployment guidance for OAuth URLs, CSRF protection, Vercel configuration, and Redis-backed sessions.
  • Tests

    • Added coverage for URL resolution, OAuth configuration, loopback behavior, and CSRF validation.

@vercel

vercel Bot commented Jul 30, 2026

Copy link
Copy Markdown
Contributor

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

Project Deployment Actions Updated (UTC)
certified-app Ready Ready Preview Jul 30, 2026 8:16am

Request Review

@coderabbitai

coderabbitai Bot commented Jul 30, 2026

Copy link
Copy Markdown

Review Change Stack

Important

Review skipped

Auto reviews are disabled on base/target branches other than the default branch.

Please check the settings in the CodeRabbit UI or the .coderabbit.yaml file in this repository. To trigger a single review, invoke the @coderabbitai review command.

⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: f14d6f53-2b0a-4cb0-ac64-25318f41a1fa

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

Use the checkbox below for a quick retry:

  • 🔍 Trigger review
📝 Walkthrough

Walkthrough

The change centralizes canonical URL resolution, adds Vercel fallbacks and validation, refactors OAuth metadata and loopback handling, tightens CSRF origin checks, and adds tests and documentation for the updated behavior.

Changes

Canonical URL resolution

Layer / File(s) Summary
URL precedence, validation, and trusted origins
src/lib/utils/config.ts, src/lib/utils/__tests__/config.test.ts, .env.local.example, README.md, AGENTS.md
PUBLIC_URL, VERCEL_BRANCH_URL, and VERCEL_URL are normalized with production and development fallbacks; exact allowed origins and malformed-input tests were added.
OAuth mode and metadata construction
src/lib/auth/oauth-client.ts, src/lib/auth/__tests__/oauth-config.test.ts, AGENTS.md
OAuth client creation now selects loopback or web metadata through exported helpers, preserves loopback ports, and enforces HTTPS for production URLs.
CSRF origin validation
src/lib/auth/csrf.ts, src/lib/auth/__tests__/csrf.test.ts, AGENTS.md
CSRF validation strictly parses Origin and Referer, requires configured source and request-destination origins, and tests malformed, missing, cross-origin, and loopback cases.

Estimated code review effort: 4 (Complex) | ~45 minutes

Possibly related PRs

Suggested reviewers: holkexyz

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 38.46% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly matches the main change: auth updates to support Vercel preview origins.
✨ Finishing Touches 💡 1
📝 Generate docstrings 💡
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch auth/vercel-preview-origins

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.

Prefer PUBLIC_URL for OAuth callbacks, then VERCEL_BRANCH_URL and VERCEL_URL. Allow same-origin CSRF requests on each exact configured deployment origin without making those origins cross-origin peers.

Co-Authored-By: OpenAI Codex <noreply@openai.com>

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

🧹 Nitpick comments (2)
src/lib/utils/config.ts (1)

105-112: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

isLoopbackHost is duplicated verbatim in src/lib/auth/csrf.ts (lines 105-107).

Same three-hostname check defined independently in both files. Since this predicate underpins both PUBLIC_URL normalization here and the CSRF dev-loopback exception in csrf.ts, keeping two copies risks silent divergence (e.g., one gets updated to add 0.0.0.0 or IPv6 variants and the other doesn't), which would create an inconsistency between what's accepted as a "loopback" origin at config time vs. request time.

♻️ Proposed fix — export once, import elsewhere
-function isLoopbackHost(hostname: string): boolean {
+export function isLoopbackHost(hostname: string): boolean {
   return hostname === "localhost" || hostname === "127.0.0.1" || hostname === "[::1]"
 }

Then in src/lib/auth/csrf.ts:

-import { ALLOWED_REQUEST_ORIGINS } from "`@/lib/utils/config`"
+import { ALLOWED_REQUEST_ORIGINS, isLoopbackHost } from "`@/lib/utils/config`"
...
-function isLoopbackHost(hostname: string): boolean {
-  return hostname === "localhost" || hostname === "127.0.0.1" || hostname === "[::1]"
-}
🤖 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/lib/utils/config.ts` around lines 105 - 112, Export the existing
isLoopbackHost predicate from config.ts and remove the duplicate definition in
csrf.ts. Import and reuse that shared symbol in csrf.ts so PUBLIC_URL
normalization and the CSRF loopback exception use the same hostname checks.
src/lib/auth/csrf.ts (1)

82-107: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

isLoopbackHost duplicates the identical helper in src/lib/utils/config.ts (lines 110-112).

Same three-way hostname check maintained in two places. Export it once from config.ts and import here to avoid drift on this security-relevant predicate.

🤖 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/lib/auth/csrf.ts` around lines 82 - 107, Remove the local isLoopbackHost
helper from csrf.ts and reuse the identical exported helper from config.ts.
Export isLoopbackHost in config.ts, import it into csrf.ts, and keep
isAllowedDevelopmentLoopback’s existing checks and behavior unchanged.
🤖 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.

Nitpick comments:
In `@src/lib/auth/csrf.ts`:
- Around line 82-107: Remove the local isLoopbackHost helper from csrf.ts and
reuse the identical exported helper from config.ts. Export isLoopbackHost in
config.ts, import it into csrf.ts, and keep isAllowedDevelopmentLoopback’s
existing checks and behavior unchanged.

In `@src/lib/utils/config.ts`:
- Around line 105-112: Export the existing isLoopbackHost predicate from
config.ts and remove the duplicate definition in csrf.ts. Import and reuse that
shared symbol in csrf.ts so PUBLIC_URL normalization and the CSRF loopback
exception use the same hostname checks.

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: 7974ce32-fffb-4cd3-84bf-05b25d26a1dd

📥 Commits

Reviewing files that changed from the base of the PR and between bff9613 and e1d0668.

📒 Files selected for processing (9)
  • .env.local.example
  • AGENTS.md
  • README.md
  • src/lib/auth/__tests__/csrf.test.ts
  • src/lib/auth/__tests__/oauth-config.test.ts
  • src/lib/auth/csrf.ts
  • src/lib/auth/oauth-client.ts
  • src/lib/utils/__tests__/config.test.ts
  • src/lib/utils/config.ts

Co-Authored-By: OpenAI Codex <noreply@openai.com>
@Kzoeps

Kzoeps commented Jul 30, 2026

Copy link
Copy Markdown
Contributor Author

(reply generated by OpenAI Codex)

Addressed the duplicated isLoopbackHost findings from CodeRabbit’s review summary in 2760a15d. CSRF now imports the shared config predicate, and its test keeps the real helper while overriding only configured origins.

Validated with 1,200 Vitest tests, source and test typechecks, ESLint, and npm run build.

@Kzoeps

Kzoeps commented Jul 30, 2026

Copy link
Copy Markdown
Contributor Author

(reply generated by OpenAI Codex)

Regarding the docstring-coverage warning in CodeRabbit’s PR comment: leaving the remaining private helpers undocumented. The exported OAuth/config contracts introduced by this PR have focused contract comments; adding narration solely to meet a generated percentage would conflict with the repository’s comment policy and is outside this fix.

@Kzoeps
Kzoeps merged commit 2adc79f into staging Jul 30, 2026
6 checks passed
@holkexyz
holkexyz deleted the auth/vercel-preview-origins branch August 12, 2026 14:47
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