From c00711360c4e26c100ca477d499f6b5d9376a876 Mon Sep 17 00:00:00 2001 From: Bryan Frimin Date: Wed, 6 May 2026 15:57:40 +0200 Subject: [PATCH] Support Google Cloud Identity in SCIM bridge The SCIM bridge requested admin.directory.userschema.readonly during OAuth consent, which is a Google Workspace-only entitlement. Cloud Identity-only admins could not grant it, so the connect flow failed before any sync ran. The scope was also unused: the provider only calls Users.List, never the schemas, groups, or customers endpoints. Trim the requested scopes down to admin.directory.user.readonly so the integration works for Workspace and Cloud Identity (Free and Premium) tenants. Switch Users.List to projection=full so standard extended fields (Organizations, ExternalIds, Relations, Languages) are populated on synced users; full projection does not require any extra OAuth scope. Relabel the connector UI to "Google Workspace / Cloud Identity" to reflect the broader support. Signed-off-by: Bryan Frimin --- .../_components/GoogleWorkspaceConnector.tsx | 18 +++++++++--------- .../provider/googleworkspace/oauth2_scopes.go | 15 +++++++++------ .../provider/googleworkspace/provider.go | 6 +++++- 3 files changed, 23 insertions(+), 16 deletions(-) diff --git a/apps/console/src/pages/iam/organizations/settings/_components/GoogleWorkspaceConnector.tsx b/apps/console/src/pages/iam/organizations/settings/_components/GoogleWorkspaceConnector.tsx index b9982d242..08cefcf5b 100644 --- a/apps/console/src/pages/iam/organizations/settings/_components/GoogleWorkspaceConnector.tsx +++ b/apps/console/src/pages/iam/organizations/settings/_components/GoogleWorkspaceConnector.tsx @@ -135,7 +135,7 @@ export function GoogleWorkspaceConnector(props: { } toast({ title: __("Success"), - description: __("Google Workspace disconnected successfully"), + description: __("Google Workspace / Cloud Identity disconnected successfully"), variant: "success", }); dialogRef.current?.close(); @@ -208,10 +208,10 @@ export function GoogleWorkspaceConnector(props: {
-

{__("Google Workspace")}

+

{__("Google Workspace / Cloud Identity")}

{__( - "Connect Google Workspace to automatically sync users via SCIM.", + "Connect Google Workspace or Google Cloud Identity to automatically sync users via SCIM.", )}

@@ -229,7 +229,7 @@ export function GoogleWorkspaceConnector(props: {
-

{__("Google Workspace")}

+

{__("Google Workspace / Cloud Identity")}

{sprintf(__("Connected on %s"), dateTimeFormat(connector.createdAt))}

@@ -245,7 +245,7 @@ export function GoogleWorkspaceConnector(props: { {__("Settings")} )} - title={__("Google Workspace Settings")} + title={__("Google Workspace / Cloud Identity Settings")} className="max-w-lg" > @@ -253,7 +253,7 @@ export function GoogleWorkspaceConnector(props: {

{__("Excluded user names")}

- {__("Users with these user names will not be synced from Google Workspace.")} + {__("Users with these user names will not be synced from Google Workspace / Cloud Identity.")}

@@ -298,7 +298,7 @@ export function GoogleWorkspaceConnector(props: { {currentExcludedUserNames.length === 0 && (

- {__("No excluded user names. All Google Workspace users will be synced.")} + {__("No excluded user names. All Google Workspace / Cloud Identity users will be synced.")}

)}
@@ -311,13 +311,13 @@ export function GoogleWorkspaceConnector(props: { {__("Disconnect")} )} - title={__("Disconnect Google Workspace")} + title={__("Disconnect Google Workspace / Cloud Identity")} className="max-w-lg" >

{__( - "This will disconnect your Google Workspace integration. Users will no longer be automatically synced via SCIM.", + "This will disconnect your Google Workspace / Cloud Identity integration. Users will no longer be automatically synced via SCIM.", )}

diff --git a/pkg/iam/scim/bridge/provider/googleworkspace/oauth2_scopes.go b/pkg/iam/scim/bridge/provider/googleworkspace/oauth2_scopes.go index 692486129..6b1d913c5 100644 --- a/pkg/iam/scim/bridge/provider/googleworkspace/oauth2_scopes.go +++ b/pkg/iam/scim/bridge/provider/googleworkspace/oauth2_scopes.go @@ -15,13 +15,16 @@ package googleworkspace var ( - // OAuth2Scopes are the Google Workspace OAuth2 scopes required by the - // SCIM provisioning bridge. The bridge reads users, user schemas, group - // members, and customer info from the Admin Directory API. + // OAuth2Scopes are the OAuth2 scopes required by the SCIM provisioning + // bridge to read users from the Admin Directory API. The scopes are + // intentionally limited to what the bridge actually consumes so the + // integration also works for Google Cloud Identity (Free or Premium) + // customers, not only Google Workspace customers. In particular, + // admin.directory.userschema is a Workspace-only entitlement (custom + // user fields are not available on Cloud Identity) and must not be + // requested here, otherwise Cloud Identity-only admins cannot complete + // the OAuth consent flow. OAuth2Scopes = []string{ "https://www.googleapis.com/auth/admin.directory.user.readonly", - "https://www.googleapis.com/auth/admin.directory.userschema.readonly", - "https://www.googleapis.com/auth/admin.directory.group.member.readonly", - "https://www.googleapis.com/auth/admin.directory.customer.readonly", } ) diff --git a/pkg/iam/scim/bridge/provider/googleworkspace/provider.go b/pkg/iam/scim/bridge/provider/googleworkspace/provider.go index 283cb99e0..81b4acee5 100644 --- a/pkg/iam/scim/bridge/provider/googleworkspace/provider.go +++ b/pkg/iam/scim/bridge/provider/googleworkspace/provider.go @@ -68,7 +68,11 @@ func (p *Provider) ListUsers(ctx context.Context) (scimclient.Users, error) { pageToken := "" for { - call := adminService.Users.List().Customer("my_customer").MaxResults(500).Context(ctx) + call := adminService.Users.List(). + Customer("my_customer"). + MaxResults(500). + Projection("full"). + Context(ctx) if pageToken != "" { call = call.PageToken(pageToken) }