feat(orgs): internal email-domain auto-join (Phase 5) #9
Loading…
Reference in a new issue
No description provided.
Delete branch "feat/company-access-modes-phase5"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
What this is
Phase 5 of "company access modes": internal email-domain auto-join. An internal org can register email domains it owns; once a domain is ownership-PROVEN, a new user whose VERIFIED email matches that domain is silently auto-joined to the org (Google-Workspace style) instead of landing in the Default org.
Stacked on the Phases 6-7 branch (PR #8) — this PR targets that branch, so its diff is Phase 5 only.
Mechanism
The verification-timing problem (registration issues a session with an unverified email; auto-join runs on the first authenticated request) is solved without any webhook or identity-metadata machinery. The auto-join middleware already re-runs
whoamieach request and gets the identity's liveverifiable_addresses(this was confirmed against the running stack: an existing session reflects verification status live). So for a member-less user the middleware decides:member.The verified check is on the specific
verifiable_addressesentry whose value matches the email andverified == true(never the raw trait email), reusing the invite flow's exact predicate. Owner promotion stays Default-only; domain joins are alwaysmember. Fail-closed on access mode (only internal, non-Default orgs are auto-joinable).Ownership proof: three owner-selectable methods
https://<domain>/.well-known/forseti-domain-verify, body contains the token. Reuses the existing webhook SSRF guard (https-only, blocks loopback/RFC1918/CGNAT/link-local/IMDS, DNS-rebinding re-check at connect, no redirects, size-capped body, bounded timeouts)._forseti-verify.<domain>contains the token. Adds thehickory-resolverdependency (default features only, no TLS pulled in).A domain can be verified under at most one org globally, enforced by a partial unique index (
domain WHERE verified_at IS NOT NULL) plus a re-check, so no org can absorb another's users, and freemail/public domains are rejected at add time.Admin surface
A
/settings/organization(s)/{slug}/domainssub-page (owner + licensed + non-Default + internal-only, CSRF-protected, fail-closed on access mode): add a domain and method, run verification, remove. i18n across all 9 locales. Each method is individually operator-disableable via config.Security review
A dedicated security audit found the core design sound (the verified-address gate, the SSRF guard application, global uniqueness, and the authorization surface all hold under tracing). Three findings were fixed in this PR: the email method is now gated at confirm time (not only add time); allowed domains are capped per org (config
domain_max_per_org, default 100); and the challenge email now names the requesting org and actor, so abuse of the (paid-license, owner-gated) email method is directly attributable.Verification
cargo clippy --all-targets -- -D warnings: clean. Full unit suite: 603 passed, 0 failed. i18n parity: pass.Known follow-ups
Pull request closed