Skip to main content
External Login v1 lets members log in with Google (which can create new accounts), or connect a Facebook, generic-OIDC, or generic-OAuth2 identity while already signed in — all three require an authenticated connect step in v1. Verified-Domain SSO extends v2 to let a generic_oidc provider also create new accounts — but only for email addresses in a domain the hub has proven it controls via DNS. This is the “company SSO” pattern: your team registers acme.com, proves DNS ownership, and from then on any new @acme.com member who authenticates through your Okta / Azure AD / Auth0 provider gets a member.dev account created for them automatically.

How it works at a glance

The only new power v2 adds is: a verified-domain generic_oidc provider may create a brand-new account for an in-domain email. Everything else (connect flow, Facebook, generic OAuth2, row-1 returning logins) is unchanged from v1.

The connect-first rule

A verified-domain provider creates accounts — it never merges into an existing one. If a member already has a member.dev account with alice@acme.com (created via Google or a magic link, for example), logging in through the acme.com OIDC provider will not silently attach to that account. Alice will see a generic error and must sign in using her existing method, then connect the provider while logged in. This is intentional and permanent. Silent email-based merges from a custom IdP would allow any hub owner who controls a domain to take over arbitrary member accounts.

One owner per domain

A domain may be verified by at most one hub globally. If another hub tries to verify the same domain, the /verify call returns 409 domain_already_owned. Domains are owned at the hub level and managed under the team-admin API. Public email domains (gmail.com, outlook.com, yahoo.com, and all free-provider domains) cannot be verified — the backend rejects them at the POST /verified-domains step. Exact full-domain match — verifying acme.com does NOT cover eu.acme.com (each subdomain must be registered and verified separately).

Step 1 — Register a domain

Response (201):
The response tells you exactly what to publish in DNS. The txt_record_host and txt_record_value are pre-computed — copy them verbatim to your DNS provider.

Step 2 — Publish the DNS TXT record

In your DNS provider (Route 53, Cloudflare, Google Cloud DNS, etc.), create a TXT record: The record is published on a dedicated challenge subdomain — it does not interfere with other DNS records on acme.com (SPF, DKIM, existing TXT entries, etc.). Allow DNS propagation (typically seconds to a few minutes, though up to 48 hours in theory).

Step 3 — Verify ownership

Once the record is live, trigger the verification check:
Response (200 — verified):
Response (422 — record not yet found):
You can retry /verify as many times as needed until the DNS change propagates.

Step 4 — Enable a generic OIDC provider for the hub

If you haven’t already, register a generic_oidc provider for the same hub. See Configure an external-login provider for the full setup. The verified-domain trust attaches to the hub — all generic_oidc providers configured on that hub can auto-create accounts for verified domains. Once both the domain is verified and the provider is configured and enabled:
  • alice@acme.com visits the hub and clicks “Log in with Acme SSO”.
  • The backend redirects her to the OIDC provider, authenticates her, and receives a signed id_token with email: "alice@acme.com" and email_verified: true.
  • The backend checks: is acme.com verified for this hub? Yes. Does a contact for alice@acme.com already exist? No. → Creates a new contact (JIT) and issues a token pair.
  • Alice is logged in, no manual connect step required.

Domain lifecycle management

List all domains for a hub

Returns all domains including pending and revoked entries.

Retrieve a single domain

Remove a domain (revoke)

Deleting a domain immediately stops new account creation for that domain on the next login attempt. Members who already have accounts and have connected the provider continue to log in normally (their identity link still exists — revocation does not deprovision existing contacts).

Freshness checks

The backend re-verifies that the DNS TXT record is still present before creating any new account (approximately once per hour per domain). If the TXT record has been removed, the domain is automatically revoked and new-account creation fails. Existing members are not affected. Keep the TXT record in DNS for as long as you want the auto-create behavior to remain active.

Domain attributes reference


Common errors


Security notes

  • Exact-domain match only. Verifying acme.com does not grant trust to eu.acme.com or any other subdomain.
  • Signed email required. The OIDC provider must return email_verified: true in a signed id_token. Generic OAuth2 providers (no id_token) never earn domain trust even if a domain is verified.
  • No enumeration. All failed login attempts from a generic_oidc provider that find an existing account return the same generic error — the specific reason (account_exists_connect_required) is written to the audit log only.
  • Public domains blocked. The Public Suffix List is used to block free-provider and public-suffix domains at registration time.
  • registration_enabled still applies. If the hub has disabled open registration, the domain-verified auto-create path is also blocked — the verified domain is a trust mechanism, not a registration bypass.

See also