Skip to content

feat(portals): make WorkOS auth host-aware#438

Open
Connorbelez wants to merge 4 commits intoeng-297from
eng-298
Open

feat(portals): make WorkOS auth host-aware#438
Connorbelez wants to merge 4 commits intoeng-297from
eng-298

Conversation

@Connorbelez
Copy link
Copy Markdown
Owner

feat(portals): make WorkOS auth host-aware

Refs ENG-298

fix(auth): complete host-aware portal assignment

eng-298

@linear
Copy link
Copy Markdown

linear Bot commented Apr 21, 2026

Copy link
Copy Markdown

@sourcery-ai sourcery-ai Bot left a comment

Choose a reason for hiding this comment

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

Sorry @Connorbelez, you have reached your weekly rate limit of 500000 diff characters.

Please try again later or upgrade to continue using Sourcery

@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai Bot commented Apr 21, 2026

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

Run ID: d0aea1a6-18de-4bf3-9b39-ca22a171a71c

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
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch eng-298

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 and usage tips.

@Connorbelez Connorbelez marked this pull request as ready for review April 21, 2026 00:17
Copilot AI review requested due to automatic review settings April 21, 2026 00:17
Copy link
Copy Markdown

@greptile-apps greptile-apps Bot left a comment

Choose a reason for hiding this comment

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

Your free trial has ended. If you'd like to continue receiving code reviews, you can add a payment method here.

Copy link
Copy Markdown
Owner Author

Connorbelez commented Apr 21, 2026

Copy link
Copy Markdown

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull request overview

Implements host-aware WorkOS AuthKit flows for multi-host portals (marketing, app/admin, broker portals), ensuring auth initiation, completion, and sign-out preserve the initiating host and enforce explicit portal assignment rules (ENG-298).

Changes:

  • Add signed host-aware auth state + routing helpers, and update sign-in/sign-up to generate per-host WorkOS URLs.
  • Add a post-auth /auth-complete route that validates signed state, resolves the viewer’s home portal via Convex, redirects appropriately, and renders an explicit wrong-portal rejection UI.
  • Thread host-aware returnTo through sign-out flows and update E2E/Playwright localhost + *.localhost coverage; add Convex home-portal assignment sync for newly created users.

Reviewed changes

Copilot reviewed 51 out of 52 changed files in this pull request and generated 2 comments.

Show a summary per file
File Description
vite.config.ts Allow localhost + *.localhost hosts in Vite dev server.
src/test/routes/portal-auth-callback.test.tsx New unit tests for auth completion decisions (redirect vs wrong-portal vs fail-closed).
src/test/routes/auth-routes.test.ts Expanded unit tests for portal auth-state and host-aware routing helpers.
src/routes/sign-up.tsx Switch sign-up initiation to host-aware WorkOS URL generation.
src/routes/sign-out.tsx Use shared host-aware sign-out hook for consistent return-to behavior.
src/routes/sign-in.tsx Switch sign-in initiation to host-aware WorkOS URL generation.
src/routes/e2e/session.tsx Expose viewer home-portal assignment in E2E session snapshot via Convex query.
src/routes/demo/workos.tsx Use shared host-aware sign-out behavior in demo flow.
src/routes/auth-complete.tsx New post-auth completion route that verifies signed state, resolves portal assignment, redirects or renders wrong-portal UI.
src/routeTree.gen.ts Register new /auth-complete route in generated route tree.
src/lib/workos-sign-out.ts Add optional returnTo support to WorkOS sign-out plumbing.
src/lib/portal/auth-state.ts New signed auth state (HMAC) encode/verify + payload validation + expiry checks.
src/lib/portal/auth-routing.ts New host-aware routing utilities (origins, callback URI, completion path, sign-out return-to).
src/lib/portal/auth-initiation.ts New server fn to build host-aware WorkOS auth URLs using resolved portal context + signed state.
src/lib/portal/auth-completion.ts New decision logic for marketing/portal completions, portal mismatch handling, and admin bypass.
src/hooks/use-host-aware-sign-out.ts New hook to compute per-host returnTo from root portalContext.
src/components/workos-user.tsx Use host-aware sign-out hook in shared signed-in header/menu UI.
src/components/portal/WrongPortalState.tsx New UI for explicit wrong-portal rejection with “continue” CTA.
src/components/admin/shell/AdminUserMenu.tsx Use host-aware sign-out in admin shell menu.
specs/ENG-298/tasks.md ENG-298 execution tasks tracking.
specs/ENG-298/summary.md ENG-298 scope/constraints summary.
specs/ENG-298/status.md ENG-298 status + blockers notes.
specs/ENG-298/execution-checklist.md Definition-of-done checklist and validation status.
specs/ENG-298/chunks/manifest.md Chunk manifest for ENG-298 execution artifacts.
specs/ENG-298/chunks/chunk-04-validation-and-audit/tasks.md Validation/audit chunk tasks.
specs/ENG-298/chunks/chunk-04-validation-and-audit/status.md Validation/audit chunk status + blockers.
specs/ENG-298/chunks/chunk-04-validation-and-audit/context.md Validation/audit chunk context and commands.
specs/ENG-298/chunks/chunk-03-sign-out-and-e2e/tasks.md Sign-out/E2E chunk tasks.
specs/ENG-298/chunks/chunk-03-sign-out-and-e2e/status.md Sign-out/E2E chunk status + validation notes.
specs/ENG-298/chunks/chunk-03-sign-out-and-e2e/context.md Sign-out/E2E chunk context.
specs/ENG-298/chunks/chunk-02-auth-completion/tasks.md Auth completion chunk tasks.
specs/ENG-298/chunks/chunk-02-auth-completion/status.md Auth completion chunk status + validation notes.
specs/ENG-298/chunks/chunk-02-auth-completion/context.md Auth completion chunk context.
specs/ENG-298/chunks/chunk-01-auth-initiation/tasks.md Auth initiation chunk tasks.
specs/ENG-298/chunks/chunk-01-auth-initiation/status.md Auth initiation chunk status + validation notes.
specs/ENG-298/chunks/chunk-01-auth-initiation/context.md Auth initiation chunk context.
specs/ENG-298/audit.md Recorded spec audit findings and external blockers.
playwright.config.ts Align E2E server/baseURL to localhost:3000 and bunx vite dev.
package.json Update dev:e2e to localhost:3000 + bunx vite dev.
e2e/helpers/workos-login.ts Allow caller-provided entry URL (absolute host-aware entrypoints).
e2e/helpers/host-aware-auth.ts New helpers for building host-aware localhost origins/sign-in URLs.
e2e/helpers/auth-storage.ts Thread host-aware entry URL through storage state creation.
e2e/auth/host-aware.spec.ts New E2E host-aware auth smoke covering app host + assigned broker host.
e2e/auth.setup.ts Add setup for app-host admin authenticated storage state.
convex/portals/queries.ts Add authed getViewerHomePortal query for home-portal assignment snapshot.
convex/portals/homePortalAssignment.ts New centralized rules for resolving/ensuring user home portal assignment.
convex/portals/tests/registry.test.ts Add tests for home portal sync and viewer home portal query.
convex/brokers/migrations.ts Refactor to reuse centralized home portal assignment helpers.
convex/auth.ts Queue/sync user home-portal assignment on relevant auth/membership events and backfills.
convex/_generated/api.d.ts Generated API typing updates for new modules/exports.
CLAUDE.md Update GitNexus index stats text.
AGENTS.md Update GitNexus index stats text.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment on lines +13 to +17
export function resolvePortalHostTypeFromHost(host: string) {
return host.includes("localhost")
? ("local" as const)
: ("production" as const);
}
Copy link

Copilot AI Apr 21, 2026

Choose a reason for hiding this comment

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

resolvePortalHostTypeFromHost() uses host.includes("localhost") to decide local vs production. This duplicates (and can diverge from) the repo’s canonical host parsing logic in shared/portal/contracts.ts (e.g., parsePortalHostCandidate, isMarketingPortalHost, isAdminPortalHost). Consider reusing that shared logic here (or deriving hostType from RootPortalContext where possible) so host-type classification can’t drift as host rules evolve.

Copilot uses AI. Check for mistakes.
Comment on lines +52 to +57
const viewerAssignment = (await createAuthedConvexClient(
auth.accessToken
).query(
api.portals.queries.getViewerHomePortal,
{}
)) as ViewerHomePortalAssignment;
Copy link

Copilot AI Apr 21, 2026

Choose a reason for hiding this comment

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

viewerAssignment is being force-cast with as ViewerHomePortalAssignment, which can hide mismatches if the Convex query return shape changes. Prefer deriving the type from the generated API (e.g., FunctionReturnType<typeof api.portals.queries.getViewerHomePortal>) or using a runtime validator so the route stays in sync with the backend contract without relying on an unchecked assertion.

Copilot uses AI. Check for mistakes.
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.

2 participants