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 <bryan@getprobo.com>
This commit is contained in:
Bryan Frimin
2026-05-06 15:57:40 +02:00
parent 25f8ade712
commit c00711360c
3 changed files with 23 additions and 16 deletions

View File

@@ -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 className="w-6 h-6" />
</div>
<div className="mr-auto">
<h3 className="font-medium">{__("Google Workspace")}</h3>
<h3 className="font-medium">{__("Google Workspace / Cloud Identity")}</h3>
<p className="text-sm text-txt-secondary">
{__(
"Connect Google Workspace to automatically sync users via SCIM.",
"Connect Google Workspace or Google Cloud Identity to automatically sync users via SCIM.",
)}
</p>
</div>
@@ -229,7 +229,7 @@ export function GoogleWorkspaceConnector(props: {
<Google className="w-6 h-6" />
</div>
<div className="mr-auto">
<h3 className="font-medium">{__("Google Workspace")}</h3>
<h3 className="font-medium">{__("Google Workspace / Cloud Identity")}</h3>
<p className="text-sm text-txt-secondary">
{sprintf(__("Connected on %s"), dateTimeFormat(connector.createdAt))}
</p>
@@ -245,7 +245,7 @@ export function GoogleWorkspaceConnector(props: {
{__("Settings")}
</Button>
)}
title={__("Google Workspace Settings")}
title={__("Google Workspace / Cloud Identity Settings")}
className="max-w-lg"
>
<DialogContent padded className="space-y-6">
@@ -253,7 +253,7 @@ export function GoogleWorkspaceConnector(props: {
<div>
<h4 className="text-sm font-medium">{__("Excluded user names")}</h4>
<p className="text-sm text-txt-secondary mt-1">
{__("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.")}
</p>
</div>
<div className="flex gap-2">
@@ -298,7 +298,7 @@ export function GoogleWorkspaceConnector(props: {
{currentExcludedUserNames.length === 0 && (
<p className="text-sm text-txt-secondary text-center py-4">
{__("No excluded user names. All Google Workspace users will be synced.")}
{__("No excluded user names. All Google Workspace / Cloud Identity users will be synced.")}
</p>
)}
</div>
@@ -311,13 +311,13 @@ export function GoogleWorkspaceConnector(props: {
{__("Disconnect")}
</Button>
)}
title={__("Disconnect Google Workspace")}
title={__("Disconnect Google Workspace / Cloud Identity")}
className="max-w-lg"
>
<DialogContent padded className="space-y-4">
<p className="text-txt-secondary text-sm">
{__(
"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.",
)}
</p>
<p className="text-red-600 text-sm font-medium">

View File

@@ -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",
}
)

View File

@@ -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)
}