Google Workspace SSO for AlmaForge
AlmaForge authenticates users against Google Workspace through the standard OIDC flow and maps Google Groups membership to AlmaForge roles using the Google Cloud Identity API. Membership changes affect role assignment on the next sign-in. For an immediate cutoff, lock the user as well.
The integration is built into almad. Configure it with
an OIDCConnector
resource. See the generic OIDC guide for the full
field list and the shared verification, high-availability, and
break-glass content. This page covers only the Google-specific
clicks.
Unlike plain OIDC providers, Google does not put the user's full
group membership on the ID token. AlmaForge has to fetch it out
of band, using a service account with domain-wide delegation. The
setup includes both an OAuth client and a delegated service account. Everything after
Step 6 is normal
OIDCConnector work.
The preceding click-through is specific to Google Cloud.
TL;DR
- Configure the OAuth consent screen in Google Cloud (Step 1).
- Enable the Cloud Identity API (Step 2).
- Create an OAuth client ID and copy the client ID and secret (Step 3).
- Create a service account and download its JSON key (Step 4).
- Grant the service account domain-wide delegation with the Cloud Identity scope (Step 5).
- Deploy the SA JSON to every auth host and apply the
OIDCConnectorfrom Step 6. - Verify with a real login (Step 7).
Allow time to create the OAuth client and delegated service account before configuring the connector. Google Cloud and Workspace admin permissions are both required for this group-based setup.
Limitations
Test recovery before changing SSO. AlmaForge has no local
accounts. If this is the only connector, a failure prevents new logins. Review
SSO-first behavior on the OIDC page,
and confirm your break-glass procedure works
before running apply on a production cluster.
Cloud Identity API is required. AlmaForge fetches group membership at login time by calling Google's Cloud Identity API, not by reading the ID token. If your Workspace edition does not include the Cloud Identity API, this integration cannot resolve Google Groups for this mapping. Transitive membership lookup requires an eligible Workspace Enterprise or Cloud Identity Premium edition. Check Google's supported editions before you start (see Prerequisites).
External group membership fails closed. AlmaForge rejects a Google group lookup that includes an external identity-mapped group. Block external group access at the workspace level, or move affected users out of external groups. See External group lockout in Troubleshooting.
One admin identity to impersonate. The service account reads
groups by impersonating a real Workspace admin user named in
googleAdminEmail. If that admin is disabled (the person leaves
the company, the account is suspended), group sync breaks
silently on the next login. Track this alongside your other
privileged-account lifecycle events.
The SA JSON must be on every auth host at the same path. AlmaForge reads the service-account JSON lazily on each login, on whichever auth instance catches the callback. A missing or stale file on one auth node produces login flaps. See High availability in Operations.
Prerequisites
Before you start, make sure you have:
- A running AlmaForge cluster with an Auth Service that you
can reach over the network and a working
almaCLI pointed at it. - A Google Workspace super administrator account. Use a dedicated admin account with MFA, not your day-to-day user. Some of the steps below require privileges that are deliberately not available to ordinary group owners.
- A Google Cloud project. It can be a new one created specifically for AlmaForge or an existing project you already use for workspace automation. None of the steps below incur paid Google Cloud usage.
- A Google Workspace or Cloud Identity edition that supports transitive group queries. Verify the edition requirements and availability before you start: open Inspect Groups in the Workspace admin console, select List all groups for a member, and enter the email of any Workspace user. If the tool returns a group list, you are good. If it refuses or the page is not available, check both the account's permissions and your edition before continuing with group-based mapping.
- One or more Google Groups that you will map to AlmaForge roles.
- The public address of your AlmaForge Proxy Service, for
example
almaforge.example.com. - Outbound HTTPS egress from
almadtoaccounts.google.com,oauth2.googleapis.com, andcloudidentity.googleapis.comfrom every auth host. - A break-glass procedure in place and tested.
Step 1. Configure the OAuth consent screen
In the Google Cloud console, select or create the project you will use for AlmaForge. Open APIs & Services → OAuth consent screen and configure:
- User Type: Internal. This restricts sign-in to accounts in your Workspace domain and skips Google's public-app verification process.
- App name: something recognizable, for example
AlmaForge. - User support email and Developer contact email: as required by Google's form.
- Scopes: add
openidand.../auth/userinfo.email. You do not need to add anything else here. Groups are fetched via a different API with its own scope.
Step 2. Enable the Cloud Identity API
Open APIs & Services → Library and enable the Cloud Identity API. AlmaForge uses Cloud Identity to query group membership, including transitive membership (a user in a child group is counted as a member of the parent group). Availability was verified in Prerequisites. This step enables the API on your project.
Step 3. Create an OAuth client ID
Open APIs & Services → Credentials → Create credentials → OAuth client ID. Configure:
- Application type: Web application.
- Name:
AlmaForge(any label is fine). - Authorized redirect URIs: add
https://almaforge.example.com/api/v1/oidc/callback, substituting your own proxy host.
Click Create. Google shows a dialog with the Client ID and Client secret. Copy both. The client secret is not shown again after you close the dialog.
Anyone with the client secret can impersonate AlmaForge to Google's OAuth endpoints. Store it in the same secret manager you use for production credentials. Do not commit it to Git.
Step 4. Create a service account
Open IAM & Admin → Service Accounts → Create service account.
Give it a recognizable name (for example almaforge-group-sync)
and click Create and continue → Done. No IAM project roles
are required. The service account does not need access to any
Google Cloud resource. Its power comes from Workspace
domain-wide delegation, configured in the next step.
Open the service account you just created and go to Details. Copy the numeric Unique ID (also called OAuth 2 Client ID). You will paste it into the Workspace admin console in Step 5.
Under Keys → Add key → Create new key, select JSON and click Create. Google downloads a JSON file containing the private key. Keep it safe. You will place it on every auth host in Step 6.
The file contains an RSA private key. Anyone who reads it can impersonate the service account and, through domain-wide delegation, any Workspace admin you grant it. Store it with the same care you give production SSH private keys.
Step 5. Grant domain-wide delegation
Open the Google Workspace admin console at admin.google.com/ac/owl/domainwidedelegation and click Add new. Paste:
- Client ID: the numeric Unique ID you copied in Step 4. Not the OAuth client ID from Step 3, which is a different value.
- OAuth scopes:
https://www.googleapis.com/auth/cloud-identity.groups.readonly
Click Authorize. The service account now has read-only access
to Google Groups on behalf of a Workspace admin, specifically on
behalf of whichever account you name in the connector's
googleAdminEmail field in
Step 6.
The googleAdminEmail on the connector must be the email of a
real Workspace admin user (for example, admin@example.com),
not the ...@project.iam.gserviceaccount.com email of the
service account itself. The service account impersonates that
admin to read groups. Without a real admin identity to
impersonate, the API call fails with an unauthorized_client
error.
Step 6. Apply the OIDCConnector
Place the service account JSON file from
Step 4 on every AlmaForge
auth host at the same path, for example
/etc/almaforge/google-sa.json. almad runs as root, so
the file is owned by root, readable only by root (chmod 600), and
identical across hosts.
sudo install -m 0600 -o root -g root \ ./google-sa.json /etc/almaforge/google-sa.jsonWrite a YAML file describing the connector. Set
googleServiceAccount to a ${file:PATH} reference. The Auth Service
resolves the reference when it builds the connector, so the key stays
on disk:
# Google Workspace connector with Cloud Identity group lookup.
apiVersion: almaforge.com/v1
kind: OIDCConnector
metadata:
name: google
spec:
display: Google
issuerURL: https://accounts.google.com
clientID: <oauth-client-id>
clientSecret: <oauth-client-secret>
googleAdminEmail: admin@example.com
googleServiceAccount: ${file:/etc/almaforge/google-sa.json}
claimsToRoles:
- claim: groups
value: admins@example.com
roles: [admin, access]
- claim: groups
value: developers@example.com
roles: [access]
Replace:
<oauth-client-id>and<oauth-client-secret>with the values from Step 3.googleAdminEmailwith a real Workspace admin email (see the warning in Step 5).- The path inside
${file:...}with the path at which you placed the SA JSON on every auth host. - The
claimsToRolesentries with your own Google Group addresses and AlmaForge roles. Google Groups are matched by full email address, not by name. Useadmins@example.com, not justadmins.
AlmaForge has no local accounts. If this is the only connector, a wrong claim mapping or expired client secret prevents new logins. Confirm your break-glass procedure works before applying the connector to a production cluster.
Apply the connector:
alma apply -f google.yamlAlternative: embed the service account JSON inline
For dev clusters, lab setups, or single-node deployments where you do not want to manage a file on disk, embed the full service-account JSON directly in the connector resource instead:
google-inline-service-account.yaml:
# Dev-only Google Workspace connector with the service-account key inline.
apiVersion: almaforge.com/v1
kind: OIDCConnector
metadata:
name: google
spec:
display: Google
issuerURL: https://accounts.google.com
clientID: <oauth-client-id>
clientSecret: <oauth-client-secret>
googleAdminEmail: admin@example.com
googleServiceAccount: |
{
"type": "service_account",
"project_id": "example-alma-sso",
"private_key_id": "f06e...",
"private_key": "-----BEGIN PRIVATE KEY-----\n...\n-----END PRIVATE KEY-----\n",
"client_email": "almaforge-group-sync@example-alma-sso.iam.gserviceaccount.com",
"client_id": "11...76",
"auth_uri": "https://accounts.google.com/o/oauth2/auth",
"token_uri": "https://oauth2.googleapis.com/token",
"auth_provider_x509_cert_url": "https://www.googleapis.com/oauth2/v1/certs",
"client_x509_cert_url": "https://www.googleapis.com/robot/v1/metadata/x509/almaforge-group-sync%40example-alma-sso.iam.gserviceaccount.com"
}
claimsToRoles:
- claim: groups
value: admins@example.com
roles: [admin, access]
googleServiceAccount accepts either a file reference or an inline
value. An inline value places a high-value secret directly in the
cluster state store. It is returned by
alma get oidcconnector/google -o yaml --with-secrets
and included in any backup of the backend. Prefer the
${file:PATH} form for production. Keep the inline form for dev
stands only.
Step 7. Verify the connector
Open the cluster's audit log for your test account. See SSO verification for event fields and the distinction from service logs.
Sign out and log in through Google:
alma logoutalma login --proxy=almaforge.example.comA browser opens the cluster's sign-in page. Choose Google. After
authentication, alma prints the assigned roles.
A successful first-time login emits a UserLogin event and the
CertificateCreate event for the certificate it mints:
{
"kind": "AuditEvent",
"apiVersion": "almaforge.com/v1",
"spec": {
"id": "7c9e6679-7425-40de-944b-e07fc1f90ae7",
"index": 0,
"timestamp": "2026-03-30T15:58:58.999000Z",
"clusterName": "almaforge.example.com",
"code": "T1001I",
"type": "UserLogin",
"verb": "login",
"actor": {
"name": "alice@example.com",
"type": "user"
},
"status": {
"success": true
},
"userLogin": {
"method": "oidc",
"attributes": {
"connector_id": "google",
"connector_type": "oidc",
"email": "alice@example.com",
"name": "Alice Example",
"request_id": "8f0c1e6a-2f7e-4a7a-9f1e-1f0b6d0f1a2b",
"username": "alice@example.com"
}
}
}
}
{
"kind": "AuditEvent",
"apiVersion": "almaforge.com/v1",
"spec": {
"id": "2f1a8b3c-5d6e-4f70-8a91-b2c3d4e5f607",
"index": 0,
"timestamp": "2026-03-30T15:58:58.999000Z",
"clusterName": "almaforge.example.com",
"code": "TC000I",
"type": "CertificateCreate",
"verb": "create",
"certificateCreate": {
"certType": "user",
"identity": {
"user": "alice@example.com",
"roles": [
"access"
],
"logins": [
"tester",
"ec2-user",
"root"
],
"traits": {
"email": [
"alice@example.com"
],
"groups": [
"admins@example.com",
"developers@example.com"
],
"name": [
"Alice Example"
]
}
}
}
}
}
The key field to check is the groups trait on the
CertificateCreate event. It contains the full Google Group
email addresses (not display names) returned by the Cloud
Identity API for that user. Each value in your
claimsToRoles.value must match one of these entries verbatim.
If groups is empty or contains null:
- The
googleAdminEmailon the connector is not a real Workspace admin, or domain-wide delegation is not configured for the service account. - The user is a member of groups outside your Workspace domain and Cloud Identity is failing closed (see External group lockout).
- The Cloud Identity API is not enabled on the Google Cloud project.
Read the full audit-event breakdown on the generic OIDC verification section.
Troubleshooting
unauthorized_client from Google
Google returned unauthorized_client: Client is unauthorized to retrieve access tokens using this method. Domain-wide
delegation was not configured for the service account, or the
client ID in the admin console does not match the service
account's Unique ID. Re-check
Step 5 and confirm the
numeric Unique ID from the service account is the one in the
Workspace admin console. Not the OAuth client ID from
Step 3.
No roles match the login
The login is rejected when no mapping assigns a role. The user is not a member of any of the groups in
claimsToRoles, or the values are not full group email
addresses. Set ALMA_LOG_LEVEL=debug on the Auth host, sign in
again, and read the claims the IdP actually sent:
journalctl -u almad -f | grep 'Extracted claims'Extracted claims shows the ID-token claims before Workspace group
lookup, so it may not contain the group email addresses used for role
mapping. Check Workspace membership and the group-lookup errors in
the Auth journal. After a successful login, inspect spec.traits.groups
in the user record for the resolved group email addresses, as described in
Claims, traits, and role templates.
googleAdminEmail is required when googleServiceAccount is set
The connector validation caught that you provided a service
account without naming the admin to impersonate. Add a
Workspace admin email to spec.googleAdminEmail and re-apply.
External group lockout
The auth journal reports that the user is a member of an external Google Workspace group. AlmaForge rejects a lookup containing an external identity-mapped group instead of using an incomplete group list. Either block access to external groups at the workspace level (see Google's Allow or block access to external groups documentation) or move affected users out of the external groups.
googleAdminEmail account is disabled
Group lookup can fail when the admin account named in
googleAdminEmail is disabled. With the group-based mappings in this
guide, a missing group list leaves no matching role and the login is
rejected. Inspect the auth journal for the Google API error. Track the admin account lifecycle
alongside your other privileged accounts, and rotate the
googleAdminEmail value on the connector when the owner
changes.
Operations
Lifecycle
Apply connector changes before testing the next login. No
almad restart is needed. See
When connector changes take effect
for discovery caching and IdP-side changes.
Service account key rotation
Rotate service-account keys according to your credential policy:
- Generate a new JSON key in IAM & Admin → Service Accounts → your-sa → Keys → Add key → Create new key → JSON.
- Replace the file referenced by
${file:...}on every auth host. Use the same configuration management tool you used to deploy it originally (Ansible, Puppet, cloud-init, Kubernetes secret mounts). - Confirm a test login works.
- Delete the old key in Google Cloud.
The connector reads the file on the next login. No
almad restart is required. If you use inline
googleServiceAccount instead, re-apply the connector with the
new JSON.
High availability
AlmaForge reads the service-account JSON lazily, on whichever auth instance catches a given login callback. In a multi-node auth cluster, a missing or stale file on one node produces login flaps. The user succeeds or fails depending on which auth instance the load balancer routed the callback to. Deploy the file through configuration management so every auth host has an identical copy at the same path.
Every auth node also calls the Cloud Identity API and the Google OAuth endpoints independently, and caches the OIDC discovery document per-instance for one hour. See High availability on the generic OIDC page for the full list of per-instance assumptions.
Secret storage
Use ${file:PATH} to keep the service-account private key on disk.
Inline googleServiceAccount and clientSecret values are stored in
the backend and its backups. Follow
Secret storage to protect them or load
the client secret from a file or environment variable.
Break-glass access
Test break-glass access before relying
on this connector. Google-specific recovery cases include a rotated
service-account key or revoked domain-wide delegation, alongside
expired client secrets and incorrect claimsToRoles mappings.
Monitoring
Monitor the audit log for login outcomes. For diagnostics, inspect the Auth journal for token validation and group lookup, and the Proxy journal for browser callbacks. On each service host, tail OIDC-related messages with:
journalctl -u almad -f | grep -Ei 'google|oidc'Check UserLogin failures and their spec.status.error field. See
SSO verification for event codes,
certificate fields, and debug-only journal copies. Collect records from
audit event storage for SIEM ingestion.
Collect service diagnostics from the journal separately.
Removal
Delete the connector resource:
alma delete oidcconnector/googleExisting sessions issued through the connector remain valid until their certificates expire. No new sessions will be issued.
On the Google side, you can also decommission the supporting resources: revoke the domain-wide delegation in the Workspace admin console, delete the service account and its keys in Google Cloud IAM, and delete the OAuth client credentials. Disabling the Cloud Identity API on the project is optional.