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 an email from Eurobase — subject "You're invited to join LexVault on Eurobase" — naming Alex as the inviter. The invitation is pending: Bea signs in to the console with her usual sign-in and accepts it under Organizations (or declines it). Only then is she a member — with access to the org's projects and SSO sign-in. Alex sees her under Pending invitations until then and can withdraw the invitation. The email's button opens the invitation in the console (after signing in if needed); it's also listed under Organizations if the mail never arrives.
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 request logs. Edge functions show as a list with their settings, triggers, metrics and environment variable names — the code, versions, invocation logs and environment variable values need Developer, because they can carry secrets and the data a function handles. 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. Sessions from a console passkey are refused the same way: a passkey is MFA at Eurobase, not your IdP, so it doesn't satisfy "require SSO". Members keep full access to their own personal projects with password or passkey — only the org's projects need the SSO session. MFA for SSO sign-ins is enforced by your identity provider; Eurobase adds no extra step. - 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. - The same applies to the org's projects: any 403 there names the organization, and the console opens the sign-in page in SSO mode with an explanation, then returns you to the page you were on. A new project created from a password session is never attached to an SSO-required org automatically (it lands as a personal project); choosing that org explicitly is refused until you sign in with its SSO.
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 a member, and nobody becomes a member without accepting an invitation, signed in as themselves. So an org admin can't pull someone else's account into their org, and a third-party IdP that returns a valid ID token for someone who isn't a member still can't get a platform session. - An SSO session only works for its own organization: its projects and settings. Your own projects, other organizations, billing, tokens and account settings need your own sign-in (password or passkey).
- Platform superadmin accounts never sign in with SSO (password or passkey only).
- Org settings and membership (SSO config, SSO required, invites, removals) can only be changed in the console, not with a personal access token.
- SSO sessions have the same lifetime as password logins (24 h access token, refresh via the standard flow).