UptimeHunt Docs
OrganizationsSingle Sign-On

SSO with Google Workspace

Let members sign in with their Google Workspace accounts, restricted to your Workspace domain through OpenID Connect integration.

SSO with Google Workspace

Overview

Let members sign in with their Google Workspace accounts, restricted to your Workspace domain. Under the hood this is an OpenID Connect integration against Google, scoped to your organization, with the Google hd (hosted domain) claim validated against the workspace_domains you declare in the configuration — sign-ins from personal Gmail accounts or other Workspace domains are rejected.

Prerequisites:

That's all — client ID, client secret, and your declared Workspace domain(s) are a complete, working configuration. There is no DNS step: workspace_domains is declared in the configuration, not verified. (Email-routing domain verification is a separate, optional convenience that makes the login page advertise this IdP to anyone typing an email under your domain.)

Step 1 — Create OAuth Credentials in Google Cloud Console

  1. In Google Cloud Console, select (or create) a project for your Workspace.
  2. Configure the OAuth consent screen: User type Internal (restricts the app to your Workspace users).
  3. Create Credentials → OAuth client ID → Web application:
    • Authorized redirect URI: copy the exact value from Organization → Authentication in UptimeHunt — shown as soon as you select Google Workspace, before the configuration is first saved (form: https://auth.uptimehunt.io/accounts/oidc/{org-slug}/login/callback/).
  4. Note the client ID and client secret.

The redirect URI is also available programmatically: GET /api/v1/orgs/{slug}/sso/urls?provider_type=google (requires sso.view) computes it from the organization slug alone — no saved configuration required, so automation (Terraform, Ansible, scripts) can create the Google OAuth client before the first PUT:

curl "https://app.uptimehunt.io/api/v1/orgs/acme/sso/urls?provider_type=google" \
  -H "Authorization: Bearer {access_token}"

data.urls.callback is the authorized redirect URI to register; data.urls.login is the direct member sign-in URL.

Step 2 — Configure the Organization

UI: Organization → Authentication → Google Workspace — enter the client ID and secret.

API:

curl -X PUT https://app.uptimehunt.io/api/v1/orgs/acme/sso \
  -H "Authorization: Bearer {access_token}" \
  -H "Content-Type: application/json" \
  -d '{
    "provider_type": "google",
    "config": {
      "client_id": "1234567890-abc.apps.googleusercontent.com",
      "client_secret": "{client_secret}",
      "workspace_domains": ["acme.com"]
    },
    "jit_provisioning": true,
    "default_role": "member",
    "enabled": true
  }'

The OAuth credentials go inside the config object (Google's issuer is built in — no server_url needed); the top-level fields are the organization-side toggles.

FieldDescription
provider_typegoogle
config.client_id / config.client_secretFrom step 1; required when first creating the configuration. The secret is write-only: it is never returned by the API
config.workspace_domainsYour Workspace hosted domain(s) — required whenever jit_provisioning is on (below)
config.role_mappingOptional {source, rules, unmapped_policy, overage_action} group→role mapping. Native Google tokens carry no groups claimsource must be a custom Workspace claim, or use Google Workspace SAML instead (Group-Based Role Mapping)
config.additional_scopesOptional extra OAuth scopes (list, or a space-separated string) appended to the always-requested openid profile email base — the base scopes are never dropped, only added to. No standard Google OAuth scope adds groups to a native Google ID token or userinfo response, so this rarely helps with role mapping; pair a custom Workspace groups claim with the scope it requires, or use Google Workspace SAML. Default []
jit_provisioningAuto-create accounts on first sign-in (details)
jit_requires_invitationOnly JIT-provision users with a matching pending invitation (default false)
allowed_email_domainsOptional declared email-domain allowlist for JIT: when non-empty, sign-ins asserting an email whose domain is not listed are refused at provisioning. Distinct from config.workspace_domains, which scopes which Workspace accounts may authenticate (hd claim) — this list filters the asserted email address instead. Default [] (no filtering)
default_roleRole granted to JIT-provisioned members (default member)
skip_role_syncWhen true, config.role_mapping runs only at account creation, never on later sign-ins — so a manual role override is not reverted (default false; no effect without a configured role_mapping)
enabledActivates the IdP for login routing
enforced_ssoRequire every member to sign in through this IdP. Enabling it must pass the readiness guards and immediately signs all members out

workspace_domains scopes the shared issuer

config.workspace_domains lists the Workspace hosted domain(s) whose accounts belong to your organization. It is required whenever jit_provisioning is on — Google's issuer (accounts.google.com) authenticates every Google account in the world, so the declared list is what scopes sign-ins to your Workspace; the API refuses (400) a JIT-enabled configuration without it. The list is declared here, in the configuration — declaring a domain you don't own admits nobody, because Google only asserts the hd claim for accounts actually in that Workspace.

Step 3 — Test

Validate the stored configuration:

curl -X POST https://app.uptimehunt.io/api/v1/orgs/acme/sso/test \
  -H "Authorization: Bearer {access_token}"

This checks Google's OIDC discovery document (the issuer is built in) — response shape and check details in Testing the Configuration.

Then sign in from a private browser window with a Workspace email: enter the email on the login page and choose the organization route.

How Domain Restriction Works

Two independent gates keep sign-ins inside your Workspace:

  1. Google side: an Internal consent screen only allows accounts in your Workspace to authenticate at all.
  2. UptimeHunt side: the ID token's hd claim (the account's Workspace domain) must match one of the declared workspace_domains — defense in depth even if the Google-side restriction is misconfigured. Consumer Google accounts carry no hd claim and are always rejected.

The identity anchor is the Google sub claim, scoped to your organization's configuration (identity realm). The same Google account signing in through another organization's Google Workspace integration is a separate UptimeHunt account — organizations never share identities.

Group-Based Role Mapping

Role mapping grants per-group roles from the groups an IdP asserts. With Google there is one important caveat:

Native Google OIDC tokens carry no groups claim

Standard Google sign-in (this Google Workspace provider type, OIDC against accounts.google.com) returns no group membership — the only domain-related claim is hd (the single hosted-domain string), which scopes who may sign in, not what role they get. There is no OAuth scope that adds groups to a native Google ID token or userinfo response. So group-based role mapping needs one of the two approaches below.

Two ways to get group-based roles with Google:

  1. A custom Workspace OIDC claim. If your Workspace is configured to emit a custom claim carrying group membership, set the role mapping source to that claim name and add rules as for any other OIDC provider. Advanced Google group sourcing (Admin SDK / Cloud Identity Directory lookups) is not built in yet.

  2. Google Workspace SAML (recommended for groups). Configure Google as a SAML 2.0 IdP instead of OIDC and use Google's Group membership attribute mapping:

    • In the Google Admin console → AppsWeb and mobile apps → your SAML app → SAML attribute mapping → add Group membership, choosing the groups to send and an App attribute name (enter groups).
    • Google emits each group as the group's email address (e.g. engineers@acme.com), one <AttributeValue> per group — so your SAML role-mapping rule values must be the full group email addresses, and source must match the App-attribute name you chose.
    • Hard limit: 75 groups configurable per SAML app; groups beyond that are silently dropped — keep your role-granting groups within the configured set.
    • Nested (transitive) membership is included, so a parent group can grant a base role — but adding a user to a subgroup can inadvertently grant the parent's role. Roles re-sync on every login (unless skip_role_sync is set).

Renaming a Google group (changing its email) breaks the mapping — update BOTH sides

Google identifies a group by its email address, and that is exactly what each <AttributeValue> carries. If a group is renamed in the Google Admin console its email changes (e.g. sre-team@corp.complatform-sre@corp.com), and the role mapping silently stops matching — affected members fall through to your unmapped_policy at their next sign-in. Recovering requires two edits, not one:

  1. Google Admin console → your SAML app → SAML attribute mappingGroup membership: re-add the group under its new email so Google keeps asserting it (the old email is gone from the directory).
  2. UptimeHunt → the SAML role-mapping rules: replace the old group email in every affected rule value with the new one.

Miss either step and the rule never fires. (Group display-name changes are harmless — only the email address is the identifier.)

Troubleshooting

SymptomLikely cause
Role mapping never matches on the Google (OIDC) providerNative Google tokens carry no groups claim — use a custom Workspace claim or Google Workspace SAML
Group-based roles stopped matching after a Google group was renamedA renamed Google group has a new email address, which is its identifier. Re-add the new email under the Admin console's Group membership SAML attribute mapping and replace the old email in your UptimeHunt role-mapping rulesboth edits are required
Google error redirect_uri_mismatchAuthorized redirect URI in Google Cloud Console doesn't exactly match the value from the SSO settings page
Error 403: org_internal at GoogleThe signing-in account is outside your Workspace (consent screen is Internal — expected behavior)
Sign-in succeeds at Google but UptimeHunt rejects it (hd_mismatch)The account's Workspace domain is not in the declared workspace_domains, or it is a consumer Google account (no hd claim)
Saving the configuration returns 400 mentioning workspace_domainsJIT provisioning is on without a declared workspace_domains — Google's shared issuer needs the declared scope
Login page never offers the Google route after entering an emailExpected without an email-routing domain (until the user's SSO account exists), or the configuration is not enabled. Members of an enforcing organization are routed regardless; anyone can use the organization sign-in link or the Use single sign-on option

On this page