Skip to content

Rework cloud org flow: onboarding page + live membership check#234

Merged
RhysSullivan merged 1 commit intomainfrom
cloud-org-onboarding-flow
Apr 13, 2026
Merged

Rework cloud org flow: onboarding page + live membership check#234
RhysSullivan merged 1 commit intomainfrom
cloud-org-onboarding-flow

Conversation

@RhysSullivan
Copy link
Copy Markdown
Owner

Summary

  • Replace callback auto-org-creation with an explicit onboarding flow. AuthGate now renders a new OnboardingPage when the user has a session but no org. Users name their own org; the callback no longer creates orgs, no longer mirrors on login, and collapses to a linear authenticate → ensureAccount → maybe-rehydrate-from-memberships → setCookie → redirect.
  • Add resolveOrganization helper — local mirror read with lazy WorkOS fallback + writeback. Fixes the bug where existing users were getting routed to onboarding because their session's org wasn't in the local mirror (a latent issue in the old flow too, exposed by removing the login-time upserts).
  • Add authorizeOrganization helper — live listUserMemberships check on every protected request. A user removed from their active org now loses access on their next request instead of up to ~10 minutes later when the JWT access token naturally expires.
  • Fix silent refresh failure in createOrganization. If refreshSession returns null or yields a session still scoped to the previous org (happens when the current session is stale — e.g. caller was removed from the org their cookie is pinned to), clear the cookie and fail loudly. The frontend bounces to login, the callback's rehydrate path picks up the new membership, user lands in the new org.
  • Extract CreateOrganizationForm hook + fields into a shared component so the shell dialog and the new onboarding page share the same state, validation, and styling.

Known follow-ups (not in this PR)

  • If a user is removed from their active org but still belongs to others, they currently land on the onboarding page instead of auto-switching. Data's all there, flow doesn't know to switch yet.
  • The live membership check adds one WorkOS API call per protected request. If that becomes a hot path, drop in a short per-(userId, orgId) TTL cache — localized change.
  • Longer term: build a WorkOS Events API consumer + local memberships table, swap authorizeOrganization to read from the table. Events API fits this stack (Cloudflare Workers) better than webhooks.

Test plan

  • Fresh user with zero memberships lands on onboarding, submits a name, lands in Shell with the new org active.
  • Existing user with one or more memberships lands directly in Shell (no onboarding detour).
  • Remove user from their active org via WorkOS dashboard while they're signed in; next protected request returns 403 / next /me returns organization: null and AuthGate routes to onboarding.
  • Stale-session create-org path: remove user from their active org, have them hit onboarding and submit; cookie is cleared, page bounces to login, re-login lands them in the newly-created org.
  • Shell's "Create organization" dialog still works for users with a valid active org session.
  • /me, switchOrganization, and organizations endpoints all still work with the new helpers.
  • Typecheck clean: turbo run typecheck --filter=@executor/cloud.

Replace the callback's auto-org-creation with an explicit OnboardingPage
that AuthGate renders when a user has a session but no org. Drops the
duplicated create-org logic, the login-time upsertOrganization calls,
and the '"{name}'s Organization"' default. Users name their own org.

Add resolveOrganization helper that reads the local mirror first and
lazily falls back to WorkOS on miss, so /me and protected API routes
self-heal when the mirror is stale instead of silently returning a null
org (the bug that was routing existing users to onboarding).

Add authorizeOrganization helper that performs a live
listUserMemberships check on every protected request. A user removed
from their active org now loses access on the next request, not up to
~10 minutes later when the JWT access token naturally expires. The
extra WorkOS call is acceptable for now; can be swapped for a local
memberships table fed by the Events API as a follow-up.

Fix silent refresh failure in createOrganization: if refreshSession
returns null or yields a session still scoped to the previous org
(happens when the current session is stale — e.g. the caller was
removed from the org their cookie is pinned to), clear the cookie and
fail loudly so the frontend bounces to login instead of getting stuck
on onboarding with a successful 200 and a cookie that will never
match.

Extract CreateOrganizationForm hook + fields into a shared component
so the shell dialog and the new onboarding page reuse the same state
and styling.
@cloudflare-workers-and-pages
Copy link
Copy Markdown

Deploying with  Cloudflare Workers  Cloudflare Workers

The latest updates on your project. Learn more about integrating Git with Workers.

Status Name Latest Commit Updated (UTC)
🔵 In progress
View logs
executor-cloud 551ba87 Apr 13 2026, 10:03 PM

@cloudflare-workers-and-pages
Copy link
Copy Markdown

cloudflare-workers-and-pages Bot commented Apr 13, 2026

Deploying with  Cloudflare Workers  Cloudflare Workers

The latest updates on your project. Learn more about integrating Git with Workers.

Status Name Latest Commit Preview URL Updated (UTC)
✅ Deployment successful!
View logs
executor-marketing 551ba87 Commit Preview URL

Branch Preview URL
Apr 13 2026, 10:05 PM

@RhysSullivan RhysSullivan merged commit 5693f5d into main Apr 13, 2026
7 checks passed
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