Skip to content

Allow Boundary to fetch Google Workspace group membership#2

Draft
dggreenbaum wants to merge 4 commits intomainfrom
google-workspace-group-awarenes
Draft

Allow Boundary to fetch Google Workspace group membership#2
dggreenbaum wants to merge 4 commits intomainfrom
google-workspace-group-awarenes

Conversation

@dggreenbaum
Copy link
Copy Markdown

@dggreenbaum dggreenbaum commented May 6, 2026

This is a first go at enabling Boundary to fetch group membership info about Google Workspace users. Google does not return group membership in OIDC tokens by default. Boundary needs to fetch this information from the Directory API in order to use it. The method in this PR is similar to how Hashicorp Vault does it.

Google Workspace Admin Console prerequisites

These steps must be completed by a Google Workspace super-admin before Boundary is configured.

a. Create a service account in Google Cloud Console

  • Create a new service account in the Google Cloud project associated with your Workspace domain
  • Generate and download a JSON key for it

b. Grant domain-wide delegation

  • In the service account settings, enable domain-wide delegation
  • In the Google Workspace Admin Console → Security → API controls → Domain-wide delegation, add the service account's Client ID with the scope: https://www.googleapis.com/auth/admin.directory.group.readonly

c. Identify an admin email to impersonate

  • This must be the email of an existing Google Workspace admin user in your domain. Boundary will impersonate this account when calling the Directory API.

Run the database migration

boundary database migrate -config /path/to/boundary.hcl

This applies migration 100/02_oidc_google_workspace.up.sql, adding the two new columns to auth_oidc_method. The migration is additive and backward-compatible — existing auth methods are unaffected (both columns are nullable).

Configure the OIDC auth method

Update or create the OIDC auth method with the two new fields. The service account JSON is encrypted at rest by Boundary's KMS.

Via Terraform:

resource "boundary_auth_method_oidc" "google_workspace" {
  scope_id   = boundary_scope.org.id
  issuer     = "https://accounts.google.com"
  client_id  = "<google-oauth-client-id>"
  client_secret = "<google-oauth-client-secret>"
  signing_algorithms = ["RS256"]
  api_url_prefix     = "https://boundary.example.com"

  # New fields — both must be set together
  google_workspace_service_account_json = file("/path/to/service-account.json")
  google_workspace_admin_email          = "admin@yourcompany.com"
}

Via CLI (update an existing auth method):

boundary auth-methods update oidc \
  -id amoidc_xxxxxxxxxxxx \
  -google-workspace-service-account-json "$(cat /path/to/service-account.json)" \
  -google-workspace-admin-email admin@yourcompany.com

Note: The Terraform provider and CLI handler for these new attributes are not part of this PR — they live in separate layers (internal/daemon/, the public API proto, and the Terraform provider). Wiring those up is the follow-on work required before operators can set the fields through standard tooling. For now the fields can be set directly via the Go API or a custom migration.

Create managed groups with group-based filters

boundary managed-groups create oidc \
  -auth-method-id amoidc_xxxxxxxxxxxx \
  -name "Engineering" \
  -filter '"engineering@yourcompany.com" in "/userinfo/groups"'

boundary managed-groups create oidc \
  -auth-method-id amoidc_xxxxxxxxxxxx \
  -name "Infra Admins" \
  -filter '"infra-admins@yourcompany.com" in "/userinfo/groups"'

Assign roles to managed groups

boundary roles add-principals \
  -id r_xxxxxxxxxxxx \
  -principal mgoidc_xxxxxxxxxxxx   # managed group ID

Notes

  • Group membership is evaluated at login time only. If a user's Google Workspace group memberships change, Boundary won't reflect that until their next login.
  • Directory API failures are non-fatal. If the API is unreachable or the service account credentials are invalid, Boundary logs the error and completes authentication with no group claims — users will not match any group-based managed groups, but login will succeed.
  • Both fields are all-or-nothing. Setting only google_workspace_service_account_json without google_workspace_admin_email (or vice versa) is rejected at validation time by both the Go layer and a database CHECK constraint.
  • The service account JSON is encrypted at rest using the same KMS wrapping path as the OIDC client_secret.

Doug Greenbaum added 4 commits May 6, 2026 10:11
This is a first go at enabling Boundary to fetch group membership info
about Google Workspace users. Google does not return group membership in
OIDC tokens by default. Boundary needs to fetch this information from the
Directory API in order to use it. The method in this PR is similar to how
Hashicorp Vault does it.

These steps must be completed by a Google Workspace super-admin before Boundary is configured.

**a. Create a service account in Google Cloud Console**
- Create a new service account in the Google Cloud project associated with your Workspace domain
- Generate and download a JSON key for it

**b. Grant domain-wide delegation**
- In the service account settings, enable domain-wide delegation
- In the **Google Workspace Admin Console** → Security → API controls → Domain-wide delegation, add the service account's Client ID with the scope:
  ```
  https://www.googleapis.com/auth/admin.directory.group.readonly
  ```

**c. Identify an admin email to impersonate**
- This must be the email of an existing Google Workspace admin user in your domain. Boundary will impersonate this account when calling the Directory API.

```bash
boundary database migrate -config /path/to/boundary.hcl
```

This applies migration `100/02_oidc_google_workspace.up.sql`, adding the two new columns to `auth_oidc_method`. The migration is additive and backward-compatible — existing auth methods are unaffected (both columns are nullable).

Update or create the OIDC auth method with the two new fields. The service account JSON is encrypted at rest by Boundary's KMS.

**Via Terraform:**
```hcl
resource "boundary_auth_method_oidc" "google_workspace" {
  scope_id   = boundary_scope.org.id
  issuer     = "https://accounts.google.com"
  client_id  = "<google-oauth-client-id>"
  client_secret = "<google-oauth-client-secret>"
  signing_algorithms = ["RS256"]
  api_url_prefix     = "https://boundary.example.com"

  # New fields — both must be set together
  google_workspace_service_account_json = file("/path/to/service-account.json")
  google_workspace_admin_email          = "admin@yourcompany.com"
}
```

**Via CLI (update an existing auth method):**
```bash
boundary auth-methods update oidc \
  -id amoidc_xxxxxxxxxxxx \
  -google-workspace-service-account-json "$(cat /path/to/service-account.json)" \
  -google-workspace-admin-email admin@yourcompany.com
```

> **Note:** The Terraform provider and CLI handler for these new attributes are not part of this PR — they live in separate layers (`internal/daemon/`, the public API proto, and the Terraform provider). Wiring those up is the follow-on work required before operators can set the fields through standard tooling. For now the fields can be set directly via the Go API or a custom migration.

```bash
boundary managed-groups create oidc \
  -auth-method-id amoidc_xxxxxxxxxxxx \
  -name "Engineering" \
  -filter '"engineering@yourcompany.com" in "/userinfo/groups"'

boundary managed-groups create oidc \
  -auth-method-id amoidc_xxxxxxxxxxxx \
  -name "Infra Admins" \
  -filter '"infra-admins@yourcompany.com" in "/userinfo/groups"'

```bash
boundary roles add-principals \
  -id r_xxxxxxxxxxxx \
  -principal mgoidc_xxxxxxxxxxxx   # managed group ID

- **Group membership is evaluated at login time only.** If a user's Google Workspace group memberships change, Boundary won't reflect that until their next login.
- **Directory API failures are non-fatal.** If the API is unreachable or the service account credentials are invalid, Boundary logs the error and completes authentication with no group claims — users will not match any group-based managed groups, but login will succeed.
- **Both fields are all-or-nothing.** Setting only `google_workspace_service_account_json` without `google_workspace_admin_email` (or vice versa) is rejected at validation time by both the Go layer and a database `CHECK` constraint.
- **The service account JSON is encrypted at rest** using the same KMS wrapping path as the OIDC `client_secret`.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant