A member address signs you into its own org, not every org

FeatureSecurityService
Shipped
September 2, 2026 at 7:07 AM UTC
Author
Kamo
Commit
ad48f49

A member who signs in with the org mailbox flagged Primary on their member settings was being offered every workspace their user account belongs to. The address exists only inside one organization — mailboxes are unique on (org_id, email), not globally — so signing in with it is signing in THERE. The account's other workspaces stay reachable by the account's own username or e-mail, which is what an account-wide identifier is for. The identifier now carries a scope. An account identifier leaves it empty and nothing changes; a member mailbox address names the org(s) it is Primary in, and that set is the only thing the sign-in may enter. Narrowing the list the picker draws is cosmetic on its own: the choice comes back from a browser, and the member usually IS a genuine member of the other workspaces, so /session/select's membership re-proof would pass. The restriction therefore lives in the selection token, in Redis, where the caller cannot reach it — and goes back with the token when a transient fault returns it, or a retry would silently widen into an unrestricted sign-in. - PrimaryMailboxIdentityLookup: the unscoped lookup returns the org ids with the user id. Still refuses when the address names two different accounts; one account holding it in two of its own orgs offers both, and nothing else. - CredentialResult gains orgScope, empty meaning unrestricted. - loginWithoutAResolvedHost filters enterableOrgs by it (only ever removing, so an inactive membership still falls out), refuses an ?org= hint outside it in the words a wrong password gets, and no longer preselects a last_org_id the address cannot reach. - OrgSelectTokenService stores userId:orgIds; a bare user id from a pod mid rolling deploy still decodes, as unrestricted. An unreadable restriction fails closed. - SessionSelectController refuses an out-of-scope org 403 and does not hand the token back — that is not a fault the member did not cause. The custom-domain path is untouched: the host already names one org.

All changes

Like what you see shipping?

Every one of these updates lands in your workspace automatically. Start free and watch it grow week after week.

Start Free ForeverView Pricing