Provider support matrix
Google is the only provider that can create a new member account without domain verification. For generic OIDC, hub admins can unlock auto-creation by verifying ownership of the member’s email domain — see Verified-Domain SSO below. All other providers require the member to already have an account and to have explicitly connected the provider (see Connect flow).
How it works
code in the fragment is exchanged server-side for the real token pair. The bearer token never appears in a URL, history, or referrer header.
Prerequisites
- The hub admin has registered an external-login provider config (see Admin: configure a provider).
- For Google: any member can log in (first-time creates an account using the verified email).
- For Facebook / Generic: the member has already connected their external account while logged in (see Connect flow).
Login flow (frontend implementation)
Step 1 — Redirect the member to the provider
The backend validates
return_to, generates PKCE + state + nonce, stores a server-side flow binding (10-minute TTL), sets an HttpOnly CSRF cookie, and redirects the browser (302) to the external provider’s authorization URL.
This is a browser redirect, not a fetch. Trigger it by setting window.location.href or using an <a> element.
Step 2 — Provider authenticates the member
The provider redirects back to:- Validates the CSRF cookie against the stored binding
- Exchanges the code for an access token / id_token via a pinned, SSRF-guarded server-to-server call
- Verifies the id_token signature (OIDC) or fetches userinfo (OAuth2)
- Resolves or creates the member’s contact (see Account linking)
- Stores a single-use bootstrap code (60-second TTL) bound to the member’s contact
400 JSON response. No internal details are exposed.
Step 3 — Exchange the bootstrap code for a token pair
Your frontend extractscode from window.location.hash and POSTs it:
mio_contact_refresh HttpOnly cookie is also set automatically. Use access_token as the Authorization: Bearer token for all subsequent member API calls.
Errors (400):
Connect flow
Facebook and generic providers (OIDC / OAuth2) do not auto-create accounts. A member must explicitly connect the provider to their existing contact while they are logged in.Initiate a connect
return_to is a query parameter (not a JSON body). The endpoint accepts no request body.
Response (200):
authorize_url. The backend sets the CSRF cookie as part of this response.
After the member authenticates at the provider, the callback runs the same verification path as the login flow. On success, the provider identity is linked to the member’s contact and the browser is redirected to return_to (no new token is issued — the member is already logged in).
After a successful connect, the member can use the normal login flow (Step 1–3 above) for all future sessions with that provider.
Account linking
The backend follows a strict account-linking decision table. The outcome is determined entirely by verified, server-side assertions — the browser never supplies identity.
Why Google only for rows 2/3: Google’s token endpoint and JWKS are hardcoded constants in the backend — they cannot be overridden by admin configuration. This makes Google’s
email_verified claim trustworthy. All other providers are “untrusted” because their identity endpoints are admin-supplied, and a malicious hub owner could otherwise forge email claims to take over arbitrary member accounts.
Security notes
- CSRF: every flow is CSRF-protected by a persistent
HttpOnlyCSRF cookie (__Host-extlogin_csrfin production). The binding stores a hash of the cookie; the callback re-hashes and compares in constant time. Concurrent flows from the same browser share the cookie — starting provider A then B does not cancel A’s flow. - PKCE S256 is always used. The
code_verifierlives only on the server in the flow binding. - No bearer token in URLs. The bootstrap code is a short-lived, single-use opaque value. Redeeming it requires an Origin header match against the code’s bound return-origin.
- Token scope: the issued contact token covers only the hub that initiated the login. A hostile hub cannot trigger a login that produces a token covering the member’s other hubs.
- Enumeration: all callback failures return one generic error response. The specific failure reason is written to the audit log only.
- Rate limits:
/start,/callback,/connect, and/sessionare rate-limited by IP.
Worked example: Google login
Worked example: Facebook connect then login
Verified-Domain SSO (v2)
By default, ageneric_oidc provider can only log in members who have already connected it (row 0 → row 1 in the account-linking table). With Verified-Domain SSO, a hub admin can unlock automatic account creation for members whose email belongs to a company domain the hub has proven it controls.
How it extends v1
The connect-first rule is permanent: if a member already has a member.dev account with
alice@acme.com, logging in through the OIDC provider will not silently adopt that account. Alice must sign in with her existing method and connect the provider while logged in.
Set up in three steps
- Register a domain —
POST /api/v1/teams/{team_id}/verified-domains {hub_id, domain}. Returns the DNS TXT record to publish. - Publish the TXT record — add
_member-dev-challenge.<domain>=member-dev-verify=<token>to your DNS provider. - Trigger verification —
POST /api/v1/teams/{team_id}/verified-domains/{id}/verify. The backend resolves the TXT; on success the domain moves toverified.
@<domain> member who authenticates through a generic_oidc provider on this hub gets an account created automatically (subject to the hub’s registration_enabled setting).
See the Verified-Domain SSO guide for the full walkthrough, DNS record format, lifecycle management, and security notes.
Admin: configure a provider
See the External Login Provider configuration guide for the full admin API reference, per-provider required fields, and where to obtain thecallback_url to register in the provider console.