Skip to content

SSO (Single Sign-On)

Whatomate supports SSO via OAuth 2.0 / OIDC, configured per organization. Each organization can enable any combination of providers, and SSO can either be restricted to existing users or set to auto-create accounts on first login.

Google

Google Workspace and personal accounts.

Microsoft

Azure AD / Entra ID — works with both work and personal Microsoft accounts via the common endpoint.

GitHub

GitHub.com accounts. Pulls primary verified email if private.

Facebook

Facebook accounts with email permission.

Custom (OIDC)

Any OIDC-compatible provider — Okta, Auth0, Keycloak, Authentik, etc. Requires you to supply the auth/token/userinfo URLs.

  1. User clicks “Sign in with <provider>” on the login page. The list of buttons is driven by which providers are enabled in any org via GET /api/auth/sso/providers (public endpoint, no secrets exposed).

  2. Frontend hits /api/auth/sso/{provider}/init. The server generates a random state nonce, stores {org_id, provider, nonce, expires_at} in Redis with a 5-minute TTL, and returns the provider’s authorization URL.

  3. User authenticates with the provider and grants consent.

  4. Provider redirects to /api/auth/sso/{provider}/callback?code=...&state=.... The server:

    • Validates and immediately deletes the state from Redis (one-shot, prevents replay).
    • Exchanges the authorization code for an access token.
    • Calls the provider’s userinfo endpoint to get id, email, name.
    • Checks the email domain against allowed_domains if configured.
    • Looks up the user by email and either logs them in, auto-creates an account, or rejects them (see Auto-create vs. invite-only).
    • Sets httpOnly JWT cookies and redirects to /auth/sso/callback in the SPA.

Settings live under Settings → SSO (admin-only). All providers share the same core fields; Custom adds three more.

FieldRequiredNotes
client_idyesOAuth client ID from the provider’s developer console.
client_secretyesStored encrypted at rest using app.encryption_key. Cannot be retrieved after save — only re-set.
is_enabledMaster switch. Disabled providers don’t appear on the login page.
allow_auto_createIf on, unknown users get an account on first SSO login. See below.
default_roleRole name assigned to auto-created users. Defaults to agent. Must be a real role for the org (look it up under Settings → Roles).
allowed_domainsComma-separated email domains, e.g. acme.com,acme.co.in. Empty = any domain accepted.
auth_urlcustom onlyOAuth 2.0 authorization endpoint.
token_urlcustom onlyOAuth 2.0 token endpoint.
user_info_urlcustom onlyOIDC userinfo endpoint. Must return sub (or id), email, and name (or preferred_username).

When you create the OAuth app at the provider, set the redirect URI (a.k.a. callback URL) to:

https://<your-host>/api/auth/sso/{provider}/callback

Replace {provider} with google, microsoft, github, facebook, or custom. If you serve Whatomate at a sub-path (server.base_path = "/whatomate"), include it: https://example.com/whatomate/api/auth/sso/google/callback.

Two operating modes, controlled by allow_auto_create:

Invite-only (allow_auto_create = false, default)

Section titled “Invite-only (allow_auto_create = false, default)”

Only users whose email already exists in the system can sign in via SSO. Unknown emails are rejected with “User not found. Contact your administrator.” This is the recommended mode for production — it pairs SSO convenience with explicit user provisioning.

Workflow:

  1. Admin creates the user under Settings → Members (or invites via the UI).
  2. User signs in with their SSO provider for the first time. The user record is matched by email and the SSO provider/ID are stored on the user for future logins.

Any user who completes SSO is automatically given an account in this organization with the role specified by default_role, provided their email domain matches allowed_domains (if set).

This is convenient for closed environments — e.g. a Google Workspace tenant where every @acme.com email is a trusted employee. Always pair with allowed_domains when enabling, otherwise anyone with a valid Google/GitHub/etc. account can self-provision into your org.

Every SSO config is scoped to an organization. A few consequences worth understanding:

  • Different orgs can use different OAuth apps for the same provider (different client_id per tenant).
  • A single user’s email belongs to exactly one user record across the system. If that user is a member of multiple orgs, SSO logs them into whichever org’s config matched the callback. They can then switch organizations via the UI if they have access.
  • Disabling a provider in one org doesn’t affect any other org.
ConcernMitigation
Client secret leakStored AES-256 encrypted using app.encryption_key. Set this in production — without it, secrets are stored in plaintext.
CSRF / replay on callbackState nonce stored in Redis (5-min TTL) and deleted on first use. Replays fail.
Brute force on callbackrate_limit.sso_max_attempts (default 10/min/IP) caps both init and callback. Set rate_limit.trust_proxy = true if behind a reverse proxy.
Domain spoofingUse allowed_domains when allow_auto_create is on.
Disabled accountsUsers with is_active = false are rejected at the end of the SSO flow.

For Okta, Auth0, Keycloak, Authentik, etc., choose Custom and supply:

FieldExample (Keycloak)
auth_urlhttps://kc.example.com/realms/main/protocol/openid-connect/auth
token_urlhttps://kc.example.com/realms/main/protocol/openid-connect/token
user_info_urlhttps://kc.example.com/realms/main/protocol/openid-connect/userinfo

Whatomate requests the openid email profile scopes and expects the userinfo response to contain at least one of: sub or id, plus email, plus name or preferred_username. Most OIDC providers return this out of the box.

Configure the OAuth 2.0 client at console.cloud.google.com/apis/credentials. Set the Authorized redirect URI to your Whatomate callback. Standard Google sign-in flow — no extra setup.

Uses Azure AD’s common endpoint, so both work and personal Microsoft accounts can sign in. Register the app at entra.microsoft.com → App registrations. Email is read from mail, falling back to userPrincipalName if the user has no primary email set.

Register an OAuth App at github.com/settings/developers. Whatomate requests the user:email scope and will fetch the user’s primary verified email via /user/emails if the public profile email is empty.

Register the app at developers.facebook.com/apps. Add the Facebook Login product. Note that Facebook may not return an email if the user has no verified email on file — the SSO will fail with “email not provided” in that case.

SymptomLikely cause
User not found. Contact your administrator.allow_auto_create = false and the user isn’t in the system yet. Add them under Members, or enable auto-create.
Email domain not allowed for this organizationThe user’s email domain isn’t in allowed_domains. Either add their domain or remove the restriction.
Invalid or expired stateThe state nonce expired (>5 min between init and callback) or was already used. Have the user start the login again.
Failed to authenticate with providerThe OAuth code exchange failed — usually a redirect URI mismatch or an invalid client secret. Re-check both at the provider.
email not provided by SSO providerProvider returned no email. For GitHub, ensure the user has a verified email; for Facebook, ensure email permission was granted.
Account is disabledUser exists but is_active = false. Re-enable under Settings → Members.
Provider button missing on login pageProvider isn’t enabled in any organization, or the SSO config is missing client_id / client_secret.