Skip to content

fix: allow trust portal subdomains and custom domains through CORS#2354

Merged
Marfuen merged 1 commit intomainfrom
mariano/trust-portal-cors-fix
Mar 20, 2026
Merged

fix: allow trust portal subdomains and custom domains through CORS#2354
Marfuen merged 1 commit intomainfrom
mariano/trust-portal-cors-fix

Conversation

@Marfuen
Copy link
Contributor

@Marfuen Marfuen commented Mar 20, 2026

Trust portals are served on dynamic subdomains (e.g. security.trycomp.ai, acme.trust.inc) and verified custom domains (e.g. trust.acmecorp.com), but the CORS config only had a static allowlist. This caused browsers to block requests from trust portals to api.trycomp.ai.

  • Add isStaticTrustedOrigin() for sync checks (*.trycomp.ai, *.trust.inc)
  • Add async isTrustedOrigin() that also checks verified custom domains from the DB via Upstash Redis cache (5-min TTL)
  • Update CORS origin callback, origin-check middleware, and cors-exception filter to use the new functions
  • Update tests to cover subdomain matching and async behavior

What does this PR do?

  • Fixes #XXXX (GitHub issue number)
  • Fixes COMP-XXXX (Linear issue number - should be visible at the bottom of the GitHub issue description)

Visual Demo (For contributors especially)

A visual demonstration is strongly recommended, for both the original and new change (video / image - any one).

Video Demo (if applicable):

  • Show screen recordings of the issue or feature.
  • Demonstrate how to reproduce the issue, the behavior before and after the change.

Image Demo (if applicable):

  • Add side-by-side screenshots of the original and updated change.
  • Highlight any significant change(s).

Mandatory Tasks (DO NOT REMOVE)

  • I have self-reviewed the code (A decent size PR without self-review might be rejected).
  • I have updated the developer docs in /docs if this PR makes changes that would require a documentation change. If N/A, write N/A here and check the checkbox.
  • I confirm automated tests are in place that prove my fix is effective or that my feature works.

How should this be tested?

  • Are there environment variables that should be set?
  • What are the minimal test data to have?
  • What is expected (happy path) to have (input and output)?
  • Any other important info that could help to test that PR

Checklist

  • I haven't read the contributing guide
  • My code doesn't follow the style guidelines of this project
  • I haven't commented my code, particularly in hard-to-understand areas
  • I haven't checked if my changes generate no new warnings

Trust portals are served on dynamic subdomains (e.g. security.trycomp.ai,
acme.trust.inc) and verified custom domains (e.g. trust.acmecorp.com),
but the CORS config only had a static allowlist. This caused browsers to
block requests from trust portals to api.trycomp.ai.

- Add isStaticTrustedOrigin() for sync checks (*.trycomp.ai, *.trust.inc)
- Add async isTrustedOrigin() that also checks verified custom domains
  from the DB via Upstash Redis cache (5-min TTL)
- Update CORS origin callback, origin-check middleware, and
  cors-exception filter to use the new functions
- Update tests to cover subdomain matching and async behavior

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
@vercel
Copy link

vercel bot commented Mar 20, 2026

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

2 Skipped Deployments
Project Deployment Actions Updated (UTC)
app Skipped Skipped Mar 20, 2026 10:27pm
portal Skipped Skipped Mar 20, 2026 10:27pm

Request Review

@cursor
Copy link

cursor bot commented Mar 20, 2026

PR Summary

Medium Risk
Medium risk because it changes CORS/CSRF origin enforcement and adds a Redis-backed DB lookup path that depends on Upstash env vars and async behavior; misconfig or logic bugs could block legitimate traffic or widen allowed origins.

Overview
Expands API origin validation beyond a static allowlist to support trust-portal subdomains (e.g. *.trycomp.ai, *.staging.trycomp.ai, *.trust.inc) and verified custom domains.

Adds isStaticTrustedOrigin (sync) and isTrustedOrigin (async) in auth.server.ts, with custom-domain lookups pulled from the trust table and cached in Upstash Redis (5‑minute TTL). Updates Nest CORS configuration, the CSRF defense originCheckMiddleware, and the CorsExceptionFilter to use these new checks, and adjusts unit tests to cover subdomain matching and async middleware behavior.

Written by Cursor Bugbot for commit 63b4bd8. This will update automatically on new commits. Configure here.

@Marfuen Marfuen merged commit 10c467d into main Mar 20, 2026
10 checks passed
@Marfuen Marfuen deleted the mariano/trust-portal-cors-fix branch March 20, 2026 22:30
Copy link

@cursor cursor bot left a comment

Choose a reason for hiding this comment

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

Cursor Bugbot has reviewed your changes and found 2 potential issues.

Fix All in Cursor

Bugbot Autofix is OFF. To automatically fix reported issues with cloud agents, enable autofix in the Cursor dashboard.

const corsRedisClient = new Redis({
url: process.env.UPSTASH_REDIS_REST_URL!,
token: process.env.UPSTASH_REDIS_REST_TOKEN!,
});
Copy link

Choose a reason for hiding this comment

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

Redis client missing env var guard breaks dev environments

Medium Severity

The corsRedisClient is unconditionally created with process.env.UPSTASH_REDIS_REST_URL! and process.env.UPSTASH_REDIS_REST_TOKEN! at module load time, without checking if the env vars exist. Every other Redis client in the codebase (admin-rate-limit.middleware.ts, upstash-redis.client.ts, device-agent-kv.ts) guards with a hasUpstashConfig check and provides a fallback (either null or InMemoryRedis). In local development or test environments without Upstash configured, this will either crash at module load time (older library versions) or cause every custom-domain CORS check to fail with noisy error logs.

Fix in Cursor Fix in Web

];
}

/**
Copy link

Choose a reason for hiding this comment

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

Unused export getTrustedOrigins after refactor to new functions

Low Severity

getTrustedOrigins is still exported but is no longer imported anywhere outside auth.server.ts. The only external consumers (main.ts, origin-check.middleware.ts) were updated to use isTrustedOrigin or isStaticTrustedOrigin. The function is now only used internally by isStaticTrustedOrigin and the betterAuth config within the same file, so the export keyword is unnecessary.

Fix in Cursor Fix in Web

claudfuen pushed a commit that referenced this pull request Mar 20, 2026
## [3.10.4](v3.10.3...v3.10.4) (2026-03-20)

### Bug Fixes

* 2FA GWS workspace returning users that have been excluded from the config ([bac5138](bac5138))
* allow trust portal subdomains and custom domains through CORS ([#2354](#2354)) ([10c467d](10c467d))
@claudfuen
Copy link
Contributor

🎉 This PR is included in version 3.10.4 🎉

The release is available on GitHub release

Your semantic-release bot 📦🚀

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants