google: add support for groups access without service account
Overview
Add support for retrieving groups the user is a member of without needing to configure a Service Account with Domain-Wide Delegation.
The Google connector has support for retrieving groups the user is a member of using the Admin SDK API. This API requires using an authenticated client sourced from a service account granted domain-wide delegation. This account would be highly-privileged and give access to all Workspace users, which is less than ideal.
This change adds an alternative method to retrieve group membership using the Cloud Identity API. When using the SearchDirectGroups API endpoint, we can provide an access token that has been authorized with the appropriate Cloud Identity API scope and search for direct and transitive membership.
In contrast to the existing method where the user is expected to specify the groups scope, this one relies on the user setting one of the required Cloud Identity scope explicitly:
- https://www.googleapis.com/auth/cloud-platform
- https://www.googleapis.com/auth/cloud-identity.groups
- https://www.googleapis.com/auth/cloud-identity.groups.readonly
This is similar to how Grafana provides this feature. Alternatively, we could also decide to support this feature by providing an explicit way in the config to choose which method to use.
This PR supersedes: https://github.com/dexidp/dex/pull/1896 which is now 4+ years old.
NOTE: contrary to what was mentioned in the above PR, this feature does NOT require Google Workspace Enterprise Standard, Enterprise Plus, Enterprise for Education, and Cloud Identity Premium accounts.
TODO: update documentation (supersede: https://github.com/dexidp/website/pull/74).
Fixes: #3517
Google connector now supports retrieving group information without a service account.
Testing
I added a basic unit test that mirrors the coverage that the existing TestGetGroups provide.
Additionally, I tested the new changes with a free version of Google Workspace + Google Cloud
I configured ArgoCD with the following:
configs:
cm:
dex.config: |
connectors:
- id: google
name: Google
type: google
config:
issuer: https://accounts.google.com
clientID: clientID
clientSecret: clientSecret
scopes: ["openid", "email", "profile", "https://www.googleapis.com/auth/cloud-identity.groups.readonly"]
fetchTransitiveGroupMembership: true
rbac:
policy.default: role:readonly
policy.csv: |
g, [email protected], role:admin
scopes: "[groups]"
dex:
enabled: true
image:
repository: devodev/dex
tag: v2.42.1-alpine-connectors-google-sa-less-groups
imagePullPolicy: Always
To validate transitive group membership, I created the following group hierarchy:
- group:
[email protected]- group:
[email protected]- user:
[email protected]
- user:
- group:
And after login, I see the following log (+ admin role assigned):
time=2025-05-18T04:00:01.646Z level=INFO msg="login successful" connector_id=google username="Alexandre Barone" preferred_username="" [email protected] groups="[[email protected] [email protected] [email protected] [email protected] [email protected] [email protected] [email protected] [email protected] [email protected] [email protected]]" request_id=00084f87-49f0-40b0-bc0d-b114d2e721a4
Special notes for your reviewer
Couple of changes on this PR that are not directly related to the new feature:
- Update from recursive to iterative algorithm for fetching transitive dependencies.
- Some cleanup/refactoring in tests (remove unused vars, use t.Context(), etc).
- Update
golang.org/x/expto get access to new functions (slices.ContainsFunc). - Fix a failing test on Windows.