27. Organizations & SSO (Team & Legal Team)
Alex hires Bea. She needs her own login for LexVault's projects — no shared passwords, no forwarded API keys.
What Alex is setting up
- A LexVault organization that owns the Team project from chapter 26.
- Google Workspace as the identity provider (any OIDC-conformant provider works — Microsoft Entra ID, Okta, Authentik, custom).
- Bea invited to the org as a member — she'll sign in with her
@lexvault.euGoogle account and see the LexVault project alongside her own.
1. Create the organization
team_beta_access on your account.
Fresh accounts don't have it and won't see the Organizations entry in the sidebar or the Create organization button.
Email contact@eurobase.app or use the request form on the pricing page to ask for the beta grant.- Console → Organizations → Create organization.
- Give it a name ("LexVault") and submit.
- Alex is now the sole admin. Nothing broke on the project side — Alex still owns their existing project directly.
- One org per user, first release. After you create your first org, the Create organization button
hides and the API refuses a second org for the same creator with
409 you already own an organization. Multi-org support (each with its own SSO) is on the roadmap; ping us on Discord if it blocks your setup.
2. Register Eurobase as an OIDC client at Google
You'll need four values from Google Cloud Console:
| Field | What it is |
|---|---|
| Issuer | https://accounts.google.com |
| Client ID | From your OAuth client registration. |
| Client Secret | Kept only server-side, sealed with AES-256-GCM. |
| Redirect URL | Register exactly https://api.eurobase.app/platform/auth/sso/callback at Google. |
Google Workspace walkthrough:
- Google Cloud Console → APIs & Services → Credentials.
- Create Credentials → OAuth client ID.
- Application type: Web application. Name it "Eurobase".
- Authorised redirect URIs:
https://api.eurobase.app/platform/auth/sso/callback - Save. Copy the Client ID + Client Secret.
state parameter, so registering /platform/auth/sso/callback once at the IdP is enough — no
per-org URI edits.3. Save the OIDC config in Eurobase
- Console → Organizations → LexVault → SSO panel.
- Fill in Provider (
google), Issuer, Client ID, paste the Client Secret, and the redirect URL above. - Save. The panel shows
•••••••• (set)once stored. To rotate later, paste a new secret and save again — old value overwritten.
4. Invite Bea
platform_users — you'll see "no platform user with that email — user must sign up first" otherwise.- Bea creates a Eurobase account at
console.eurobase.appwithbea@lexvault.eu— the same email that lives in her Google Workspace. - Alex → Organizations → LexVault → Members → Add member → paste
bea@lexvault.eu. - Role: member (can't invite others or edit SSO). admin would let her manage the org config.
- Save.
Bea receives a notification email from Eurobase — subject "You've been added to LexVault on Eurobase" — naming Alex as the inviter and linking to the sign-in page. The DB row is written before the mail is sent, so a mail-provider hiccup never leaves her in a half-invited state.
5. Bea signs in with SSO
- Bea opens
console.eurobase.app/login. - Clicks Sign in with SSO and enters
bea@lexvault.eu. - Redirected to Google, completes the Workspace login, comes back to Eurobase.
Backend validates the ID token against LexVault's OIDC config, checks Bea's email is in
org_members, and issues a platform session. - Bea lands on the projects page with LexVault's project visible.
6. Attach a project to the org
Projects attach to an org at creation time. Every project Alex creates while admin of LexVault auto-attaches to LexVault by default, and the create-project wizard shows an Owner picker (see chapter 2) so Alex can pick Personal per-project when they don't want a project to be org-visible. Any org-attached project shows up in every LexVault member's project list with an Org badge.
project_members row (chapter 19) with the org's org_members row via IsProjectAccessible. Bea sees the project in her list AND can open it in a single click — no separate per-project invite required for read access. Role mapping (conservative by default; see chapter 19 to widen per-project):
- Org admin → project admin. Mirrors
SetProjectOrg's auth check — "admin of the org that owns the project" ≈ "admin of the project." Can manage members, save Settings, run DDL, deploy edge functions, etc. - Org member → project viewer (read-only). Can open the project, browse tables, read rows, read logs. Cannot run
/sqlwrites, deploy edge functions, change schema, upload storage, manage webhooks / cron / migrations. To grant an org member write access to a specific project, add them under that project's Members tab (chapter 19) with the role you want — the effective role is the higher of the direct membership and the org-derived one.
PATCH /platform/projects/{id}/org) exists, but no console UI is wired to it yet.
If you have a personal project that predates your org and want to move it in (or vice versa), reply to any support email or ping us on Discord — we'll flip org_id for you.
A self-serve Owner control on the project settings page is tracked as a follow-up.7. Require SSO for every org project
Once SSO is configured, you can lock every project the org owns behind SSO login. Turning this on stops password sessions from reaching any project attached to the org — even for the admin who flipped the switch, until they re-sign-in via SSO.
- Console → Organizations → LexVault → Require SSO toggle → ON. The toggle fires the PATCH the moment you click it — there is no confirmation modal today, so the switch is live before you finish reading this sentence. Have an SSO login ready first (see the red callout below).
- Any subsequent request from a password session hits
403 sso_required_for_orgat every org-owned access site — ListProjects filters the org's projects out; the project middleware refuses direct navigation; the org detail page refuses reads; the WebSocket realtime upgrade refuses subscribes. Enforcement is re-checked on every request, so flipping the toggle takes effect without waiting for sessions to expire. - When a user's password session hits an org-owned org detail route (
/platform/orgs/<id>), the console sends them to/login?sso_required_for=<org>. On project or realtime 403s the browser goes to plain/login; the user picks Sign in with SSO themselves and enters their email. Deep-link handling of thesso_required_forquery param on the login page is a follow-up.
PATCH /platform/orgs/<id>/sso-required handler applies the same SSO gate to itself once sso_required is on, so a password session can't turn it back off (that's the "lock yourself out" case above — sign in via SSO first). Once flipped off, password sessions immediately regain access on the next request; no session invalidation, no re-login. Enforcement lives on the org row (organizations.sso_required) and is read fresh on every check.Troubleshooting
- "no platform user with that email — user must sign up first" — the invitee hasn't
created a Eurobase account yet. Ask them to sign up first, then retry the invite. The email must match exactly — case-insensitive OK, but
+tagaliases likebea+eurobase@lexvault.euwon't. - "SSO sign-in failed" after the IdP redirect — three usual causes: (1) the returned
email isn't in
org_members; (2) the redirect URI at Google doesn't match the exact/platform/auth/sso/callbackpath; (3) the client secret in the SSO panel doesn't match the current secret at Google. - "SSO requires a Team-tier organization to have invited you" — the person hit Sign in with SSO but no org has invited them under that email. Add them via the members panel.
Security notes
- Client secret is encrypted at rest with AES-256-GCM using a server-side platform key (
PLATFORM_ENCRYPTION_KEY). Never persisted plaintext. - Access to org-owned projects is gated on
org_members: an SSO login only succeeds for an email an org admin has already invited. Two properties fall out of that: (1) the load-bearing trust is on your org admins to only invite people they mean to; (2) a third-party IdP that returns a valid ID token for a user we've never invited still cannot get a platform session. - SSO sessions have the same lifetime as password logins (24 h access token, refresh via the standard flow).