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:
- Owner role in the organization (
sso.edit) - Access to the Google Cloud Console for your Workspace
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
- In Google Cloud Console, select (or create) a project for your Workspace.
- Configure the OAuth consent screen: User type Internal (restricts the app to your Workspace users).
- 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/).
- 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:
- 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.
| Field | Description |
|---|---|
provider_type | google |
config.client_id / config.client_secret | From step 1; required when first creating the configuration. The secret is write-only: it is never returned by the API |
config.workspace_domains | Your Workspace hosted domain(s) — required whenever jit_provisioning is on (below) |
config.role_mapping | Optional {source, rules, unmapped_policy, overage_action} group→role mapping. Native Google tokens carry no groups claim — source must be a custom Workspace claim, or use Google Workspace SAML instead (Group-Based Role Mapping) |
config.additional_scopes | Optional 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_provisioning | Auto-create accounts on first sign-in (details) |
jit_requires_invitation | Only JIT-provision users with a matching pending invitation (default false) |
allowed_email_domains | Optional 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_role | Role granted to JIT-provisioned members (default member) |
skip_role_sync | When 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) |
enabled | Activates the IdP for login routing |
enforced_sso | Require 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:
- Google side: an Internal consent screen only allows accounts in your Workspace to authenticate at all.
- UptimeHunt side: the ID token's
hdclaim (the account's Workspace domain) must match one of the declaredworkspace_domains— defense in depth even if the Google-side restriction is misconfigured. Consumer Google accounts carry nohdclaim 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:
-
A custom Workspace OIDC claim. If your Workspace is configured to emit a custom claim carrying group membership, set the role mapping
sourceto 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. -
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 → Apps → Web 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 rulevalues must be the full group email addresses, andsourcemust 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_syncis set).
- In the Google Admin console → Apps → Web and mobile apps → your SAML app → SAML attribute mapping → add Group membership, choosing the groups to send and an App attribute name (enter
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.com → platform-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:
- Google Admin console → your SAML app → SAML attribute mapping → Group membership: re-add the group under its new email so Google keeps asserting it (the old email is gone from the directory).
- UptimeHunt → the SAML role-mapping
rules: replace the old group email in every affected rulevaluewith 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
| Symptom | Likely cause |
|---|---|
| Role mapping never matches on the Google (OIDC) provider | Native 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 renamed | A 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 rules — both edits are required |
Google error redirect_uri_mismatch | Authorized redirect URI in Google Cloud Console doesn't exactly match the value from the SSO settings page |
Error 403: org_internal at Google | The 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_domains | JIT 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 email | Expected 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 |