feat(portals): make WorkOS auth host-aware#438
Conversation
There was a problem hiding this comment.
Sorry @Connorbelez, you have reached your weekly rate limit of 500000 diff characters.
Please try again later or upgrade to continue using Sourcery
|
Important Review skippedAuto reviews are disabled on base/target branches other than the default branch. Please check the settings in the CodeRabbit UI or the ⚙️ Run configurationConfiguration used: defaults Review profile: CHILL Plan: Pro Run ID: You can disable this status message by setting the Use the checkbox below for a quick retry:
✨ Finishing Touches🧪 Generate unit tests (beta)
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. Comment |
There was a problem hiding this comment.
Your free trial has ended. If you'd like to continue receiving code reviews, you can add a payment method here.
|
Warning This pull request is not mergeable via GitHub because a downstack PR is open. Once all requirements are satisfied, merge this PR as a stack on Graphite.
This stack of pull requests is managed by Graphite. Learn more about stacking. |
There was a problem hiding this comment.
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-completeroute 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
returnTothrough sign-out flows and update E2E/Playwright localhost +*.localhostcoverage; 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.
| export function resolvePortalHostTypeFromHost(host: string) { | ||
| return host.includes("localhost") | ||
| ? ("local" as const) | ||
| : ("production" as const); | ||
| } |
There was a problem hiding this comment.
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.
| const viewerAssignment = (await createAuthedConvexClient( | ||
| auth.accessToken | ||
| ).query( | ||
| api.portals.queries.getViewerHomePortal, | ||
| {} | ||
| )) as ViewerHomePortalAssignment; |
There was a problem hiding this comment.
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.

feat(portals): make WorkOS auth host-aware
Refs ENG-298
fix(auth): complete host-aware portal assignment
eng-298