dotCMS / dotCMS/core

dotAuth: support post-auth group fetch for IdPs that cannot emit groups in claims (Google Workspace)

Open
#37,195 1 comment 0 reactions 0 assignees View on GitHub

Nobody has claimed this yet.

dotCMS : Authentication Team : Platform Type : Task
Dominant language
Java
Stars
970
Forks
486
Avg merge
3d 33m
Merged PRs (30d)
170

Description

Description

Google Workspace is the only major enterprise IdP that has no way to put group memberships into OAuth/OIDC token claims — not in the ID token and not via its userinfo endpoint. Unlike Entra, Okta, Keycloak, Cognito, Auth0, Ping, or OneLogin (which all support a groups claim natively or via configuration), Google requires a separate API call after authentication (Cloud Identity API or Admin Directory API) to learn a user's groups. This is a documented, deliberate gap that every relying party works around (Vault, Boundary, Cloudflare Access, GitLab all document it).

dotAuth's merged OAuth support already has a two-tier group resolution in OAuthProvider.getGroups (OIDCProvider / GenericOAuth2Provider):

  1. groupsClaim — read a configurable claim from the ID token / userinfo.
  2. groupsUrl — GET a configurable endpoint with the user's bearer token.

Tier 2 was designed for simple endpoints and cannot serve Google's group APIs (nor GitHub's teams API, the other claims-incapable provider). Concrete gaps in OIDCProvider.fetchGroupsFromUrl:

  • No user identity in the URL. Google's Cloud Identity searchDirectGroups needs the member key in a query parameter (query=member_key_id=='user@example.com'); the Directory API needs ?userKey={email}. groupsUrl is a static string with no placeholder substitution.
  • Response shape. The parser accepts only a JSON array of strings or {"groups": [strings]}. Google returns arrays of objects: {"memberships":[{"groupKey":{"id":"team@example.com"},...}]} (Cloud Identity) or {"groups":[{"email":"team@example.com",...}]} (Directory). GitHub returns [{"slug":"my-team",...}].
  • No pagination. Google pages results via nextPageToken; a user in many groups gets silently truncated group membership, which under buildRolesStrategy=ALL/IDP strips roles on login.

Proposed change

Extend the existing tier-2 groupsUrl mechanism generically (no Google-specific provider class):

  • Support {email} and {sub} placeholders in groupsUrl, substituted (URL-encoded) from the authenticated user's verified claims.
  • Add one optional config key (e.g. groupsResponsePath) — a dot-path such as memberships[].groupKey.id or groups[].email selecting the array and the string field per element. Absent → current behavior unchanged.
  • Follow nextPageToken pagination with a sane page cap.
  • Keep the existing failure semantics: any fetch failure aborts login before the role wipe (groups-endpoint outage must never silently strip roles).

The extra Google scope (https://www.googleapis.com/auth/cloud-identity.groups.readonly) already works via the existing configurable scopes field — docs only.

Acceptance Criteria

  • groupsUrl supports {email} and {sub} placeholders, substituted URL-encoded from verified claims (both OIDCProvider and GenericOAuth2Provider).
  • New optional dot-path config extracts group names from object-array responses; existing string-array and {"groups":[...]} responses keep working with no config change.
  • nextPageToken-style pagination is followed with a bounded page limit; the bound is logged when hit.
  • A group-fetch failure (including mid-pagination) still aborts login with roles untouched — covered by tests.
  • Works identically in the interceptor flow and the headless exchange flow (shared getGroups path) — covered by tests.
  • Unit tests cover placeholder substitution, dot-path extraction for the Google Cloud Identity and Directory API response shapes, pagination, and backward compatibility.
  • Documentation includes a worked Google Workspace configuration (scope, groupsUrl, response path) for the Cloud Identity user-token approach.

Additional Context

Research summary (Aug 2026): claims-in-token is supported by Keycloak, Okta (≤100 groups, hard-fails above), Entra (≤200 per JWT, overage → Graph API), Cognito (cognito:groups), Auth0 (namespaced custom claim via Action), Ping/OneLogin/ADFS (attribute mapping). GitLab serves full groups only via userinfo. Google Workspace and GitHub have no claims option at all.

Explicitly out of scope / follow-ups:

  • Service-account (domain-wide delegation) Admin Directory API variant — needed only when user-token group visibility is restricted; requires new credential storage.
  • Entra groups-overage (_claim_names/_claim_sources) detection with Microsoft Graph fallback.
  • GitHub Link-header pagination (GitHub teams work for ≤100 teams via the dot-path change alone).

Contributor guide

Open the contributing guide

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. Open a pull request that references the issue number.

Research direction

Start at OAuthProvider.getGroups and OIDCProvider.fetchGroupsFromUrl, including GenericOAuth2Provider, then trace the shared interceptor and headless exchange flows. Review the existing groupsUrl parsing and configurable scopes behavior against the requested Google Cloud Identity and Directory response shapes. Done means the acceptance-criteria tests pass and the worked Google Workspace configuration is documented.

Written by the indexing model from the issue text.

Assessment

Tech stack
google-cloud, java
Domain
authentication, backend-api-design, documentation
Issue type
Feature
Difficulty
5/5
Estimated time
Over a week
Activity status
Active
Clarity
Mostly clear
Newbie friendliness
35/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.