Skip to content

Quick Start: Sign in with Google or Microsoft

A platform superadmin can put Continue with Google and Continue with Microsoft buttons on the login and register pages. They work for anyone, before any organization is known: a new user gets an account without typing a password, and an existing user signs in with the provider account they linked.

Everything is off by default. A deployment that never configures a provider shows exactly the login page it always has.

Dashboard: Platform Admin → Settings → Sign-in Providers. The page has a tab for each part:

  • General holds the master switch and the account policy: whether a sign-in may create an account, and whether it may link to an existing account by email.
  • Google and Microsoft each hold that provider’s own switch, client ID and client secret. Each also shows the exact redirect URI to register with the provider, built from the Public API URL, with a copy button, and a Try it button.

Each tab label shows what that provider will be once you save. It follows your edits as you make them:

LabelMeaning
LiveIts button is on the login page
ReadyFully configured, but hidden while the master switch is off
IncompleteSwitched on, but its client ID or secret is missing, so no button is shown
OffSwitched off

Edits on every tab go out together in the one Save settings. Switching tabs doesn’t drop an edit, and a tab with unsaved edits is marked with a dot. Until you save, the label describes the form, not what’s live.

Each setting’s switch means “override the default”. Turning it on for a yes/no setting also picks the non-default value, so switching on Offer Google sign-in selects Enabled.

Or use the API, with any superadmin credential:

Terminal window
curl -X PATCH http://localhost:8000/admin/settings \
-H "X-API-Key: change-me-to-a-strong-secret" \
-H "Content-Type: application/json" \
-d '{
"social_login_enabled": true,
"social_login_google_enabled": true,
"social_login_google_client_id": "1234567890-abc.apps.googleusercontent.com",
"social_login_google_client_secret": "GOCSPX-..."
}'

A provider’s button appears once all three hold: the master switch social_login_enabled is on, that provider’s own *_enabled is on, and both its client ID and client secret are set. Turning the master switch off hides every button and refuses every provider sign-in, but keeps the client IDs and secrets stored, so turning it back on needs nothing re-entered.

The client secrets are write-only: GET /admin/settings never returns them, only secrets_set.social_login_google_client_secret: true. Like every field on the Settings page, none of this can be set from the config file or an environment variable — see Configuration Reference.

Every setting takes effect on the very next request. There’s no restart.

Try it on a provider’s tab runs a real sign-in against the saved settings: the same redirect, token exchange and ID-token checks a user’s sign-in goes through. You sign in at the provider with any account. Nothing is created or linked, and your own session is left alone. You come back to the same tab with the result.

It works before the provider, or the master switch, is turned on, so you can prove a configuration before anyone sees the button. It needs a saved client ID and secret, and it’s disabled while the tab has unsaved edits.

ResultUsual cause
Sign-in worksEverything agrees. The result also says if the button isn’t live yet, or if the email’s domain belongs to an organization’s SSO connection
token_exchange_failed with invalid_clientWrong client ID or client secret. For Microsoft, pasting the secret’s ID instead of its value, or an expired secret
email_unverified (Microsoft)The app registration lacks the optional ID-token claim xms_edov
email_missing (Microsoft)The app registration lacks the optional ID-token claim email
id_token_invalid (Microsoft)The account you used isn’t admitted by Accounts admitted
discovery_failedFor Microsoft, Accounts admitted isn’t common, organizations, consumers or a real tenant ID
provider_errorThe provider refused the request. Its own error is shown alongside

A redirect URI that isn’t registered exactly is reported by the provider itself, as redirect_uri_mismatch on its own page, because it won’t send the browser back to an unregistered address.

  1. In Google Cloud, open APIs & Services → Credentials → Create credentials → OAuth client ID, with application type Web application.

  2. Under Authorized redirect URIs, add exactly:

    {Public API URL}/auth/oauth/google/callback

    where {Public API URL} is the server.public_base_url setting, the API’s own public address, not the dashboard’s. The provider redirects the browser back to the API.

  3. The OAuth consent screen needs the openid, email and profile scopes only.

  4. Copy the client ID and client secret into the settings above.

  1. In Microsoft Entra, open App registrations → New registration. Choose the supported account types to match social_login_microsoft_tenant below: any organizational directory and personal Microsoft accounts for common.

  2. Add a Web redirect URI, exactly:

    {Public API URL}/auth/oauth/microsoft/callback
  3. Under Token configuration → Add optional claim → ID, add email and xms_edov.

  4. Under Certificates & secrets, create a client secret. Copy its value (not its ID) and the app’s Application (client) ID into social_login_microsoft_client_secret / social_login_microsoft_client_id.

social_login_microsoft_tenant decides whose accounts are admitted:

ValueAdmits
common (default)Work or school accounts from any tenant, and personal Microsoft accounts
organizationsWork or school accounts from any tenant
consumersPersonal Microsoft accounts only
a tenant ID (GUID)Users of that one tenant only

What happens when someone clicks the button

Section titled “What happens when someone clicks the button”

The browser goes to the provider, and the provider sends it back to GET /auth/oauth/{provider}/callback, which resolves the account in this order:

  1. Is the email’s domain claimed by an organization’s SSO connection? Then the browser is sent into that org’s SSO sign-in instead. See SSO takes precedence.
  2. Is this provider account already linked to an OpenTremor account? Then that account is signed in. Accounts are matched on the provider’s own stable ID for the person (Google’s sub; Microsoft’s tenant ID + object ID), never on the email, so a provider-side email change doesn’t detach anyone.
  3. Does an account already exist with this email? By default the sign-in is refused, and the user is told to sign in with their password and link the provider from their Account page. With social_login_allow_auto_link on, the provider is attached to that account automatically.
  4. No account at all? One is created, without an organization, and the user continues on the onboarding page, where registration.allow_public_org_creation still decides whether they can create an org. Turn social_login_allow_signup off to stop new accounts being created this way; existing accounts and invitation links keep working.

A deactivated account is refused, the same as a password sign-in. An email the provider didn’t verify is refused too: Google’s email_verified, Microsoft’s xms_edov.

Invitation links work with the buttons. Opening an invite link and choosing Continue with Google carries the invitation through the round trip, so the account lands in the inviting org at the invitation’s role. An invitation addressed to a specific email is only accepted if the provider account has that email.

An account with its own two-factor authentication still needs it. If a user turned on TOTP for their account, signing in with a provider takes them to the same code prompt a password sign-in would. A compromised Google account is not a way around a factor they chose.

If an organization’s enabled SSO connection claims an email domain (allowed_domains), a provider sign-in with an address at that domain is always sent to that org’s SSO connection. This holds even for an account that used the provider button before the org connected SSO.

This rule is what stops the button being a way around an org’s identity provider. Without it, [email protected] could skip Acme’s Entra tenant — its conditional-access policies, its offboarding, its default_role — by choosing Continue with Google on a Google account using the same address. For the same reason, linking a provider account at an SSO-claimed domain is refused.

Linking and unlinking from the Account page

Section titled “Linking and unlinking from the Account page”

A signed-in user can link a provider from Account → Sign-in providers → Link Google. They sign in at the provider, and that provider account is attached to their OpenTremor account, even if its email is different, since they’ve just proved they control both. Then they can sign in either way.

Unlink removes it. Removing the account’s only way to sign in is refused: no password, no org SSO connection, and no other linked provider.

An account created through a provider has no password. Its Account page doesn’t offer password or two-factor settings, because the provider owns both.

Organizations that require two-factor authentication

Section titled “Organizations that require two-factor authentication”

A session that signed in through a provider, or through an org’s SSO connection, satisfies an organization’s require 2FA policy. The provider owns the second factor, and TOTP enrollment is only available for password accounts, so demanding it would lock these users out with nothing they could do. The same user signing in with their password still needs TOTP.

Whether a Google or Microsoft account actually uses MFA is that provider’s policy, not OpenTremor’s. An organization that needs to enforce it should connect its own SSO, where its identity provider’s conditional-access rules apply.

A failed sign-in lands back on the login page (or the Account page, for a link) with a message. The oauth_error query parameter carries its key:

oauth_errorMeaning
account_existsAn account with this email exists and auto-linking is off — sign in with the password, then link
signup_disabledNo matching account, and social_login_allow_signup is off
email_unverifiedThe provider didn’t verify the email (for Microsoft, usually a missing xms_edov optional claim)
email_missingThe provider shared no email address
identity_in_useThat provider account is already linked to a different OpenTremor account
provider_already_linkedThis account already has a different account from that provider linked
domain_managed_by_ssoLink refused: the email’s domain belongs to an org’s SSO connection
deactivatedThe account is deactivated
provider_disabledThe provider was switched off mid-sign-in
cancelledThe user backed out at the provider
expiredThe sign-in took longer than 10 minutes, or cookies are blocked
rate_limitedToo many accounts created from one network
failedAnything else — the API log has the cause (most often a redirect URI mismatch or a wrong client secret)

See API Endpoints — Social login for the endpoint reference.