# How to Set Up gogcli with a Service Account and Domain-Wide Delegation

> Learn to set up gogcli with a service account and domain-wide delegation. Impersonate Google Workspace users by configuring your service account key and using the account flag.

- Repository: [Peter Steinberger/gogcli](https://github.com/steipete/gogcli)
- Tags: how-to-guide
- Published: 2026-02-16

---

**Configure gogcli to impersonate any Google Workspace user by storing a domain-wide delegated service account key and running commands with the `--account` flag.**

Setting up gogcli with a service account and domain-wide delegation enables headless automation across Google Workspace without interactive OAuth flows. This configuration allows the CLI to act on behalf of any user in your domain by impersonating their accounts with a stored JSON key. The setup involves creating a delegated service account in Google Cloud, securely storing the key locally via [`internal/cmd/auth_service_account.go`](https://github.com/steipete/gogcli/blob/main/internal/cmd/auth_service_account.go), and invoking gogcli commands with the appropriate account flag.

## Create a Service Account with Domain-Wide Delegation

Domain-wide delegation grants a service account the authority to access user data across your entire Google Workspace domain. You must configure this in both Google Cloud and the Google Workspace Admin console.

### Google Cloud Console Steps

1. Navigate to **IAM & Admin → Service accounts** and click **Create Service Account**.
2. After creation, click the three-dot menu (⋮) → **Manage keys** → **Add key → Create new key** → **JSON**. Download the file (e.g., [`service-account.json`](https://github.com/steipete/gogcli/blob/main/service-account.json)).
3. Return to the service account list, click ⋮ → **Enable G Suite Domain-wide Delegation**, then copy the **Client ID** displayed.

### Google Workspace Admin Console Steps

1. Go to **Security → API controls → Domain-wide delegation**.
2. Click **Add new** and paste the **Client ID** from the previous step.
3. Enter the OAuth scopes that gogcli requires. According to `googleauth.Scopes` in the source, typical scopes include:
   - `https://www.googleapis.com/auth/drive`
   - `https://www.googleapis.com/auth/gmail.readonly`
   - `https://www.googleapis.com/auth/calendar`
   - `https://www.googleapis.com/auth/keep`
   - `https://www.googleapis.com/auth/contacts.readonly`

The service account's client ID must be authorized to impersonate users in the domain; otherwise token creation will fail when gogcli attempts to set the JWT subject.

## Store the Service Account Key in gogcli

Use the **`gog auth service-account set`** command to securely store the JSON key. This command validates the file, extracts the `client_email` and `client_id`, and writes it to the user configuration directory with restricted permissions (`0600`).

```bash
gog auth service-account set \
    john.doe@example.com \
    --key /path/to/service-account.json

```

- `john.doe@example.com` — The **user to impersonate** (any valid email in your Workspace domain).
- `--key` — Absolute path to the downloaded JSON key.

**Implementation details:**

- **`parseServiceAccountJSON`** in [`internal/cmd/auth_service_account.go`](https://github.com/steipete/gogcli/blob/main/internal/cmd/auth_service_account.go) (lines 21-42) validates the JSON structure.
- **`config.ServiceAccountPath(email)`** in [`internal/config/paths.go`](https://github.com/steipete/gogcli/blob/main/internal/config/paths.go) (lines 58-66) generates the storage path using a base64-encoded filename.
- The file is written with mode `0600` (owner read/write only) at lines 92-98 in [`auth_service_account.go`](https://github.com/steipete/gogcli/blob/main/auth_service_account.go).

After successful execution, you will see output confirming the stored path and client details. Verify the configuration with:

```bash
gog auth service-account status john.doe@example.com

```

## Run gogcli Commands with Domain-Wide Delegation

Once stored, invoke any gogcli command using the **`--account`** flag to impersonate the configured user.

```bash

# List Drive files as the impersonated user

gog drive list --account john.doe@example.com

# Create a Calendar event

gog calendar add \
    --account john.doe@example.com \
    --title "Team Sync" \
    --start "2026-03-01T10:00:00Z" \
    --end "2026-03-01T11:00:00Z"

# Watch Gmail for new messages

gog gmail watch --account john.doe@example.com

```

If only one service account is configured, you may omit the `--account` flag; gogcli automatically selects the stored key.

**Token source selection logic:**

- **`optionsForAccountScopes`** in [`internal/googleapi/client.go`](https://github.com/steipete/gogcli/blob/main/internal/googleapi/client.go) (lines 95-105) first attempts to use `tokenSourceForServiceAccountScopes`.
- **`newServiceAccountTokenSource`** in [`internal/googleapi/service_account.go`](https://github.com/steipete/gogcli/blob/main/internal/googleapi/service_account.go) (lines 15-25) constructs the token source by calling `google.JWTConfigFromJSON` and setting the `Subject` field to the impersonated user email.
- If no service account file exists, the code falls back to standard interactive OAuth flow.

## Legacy Keep Service Account (Optional)

Earlier versions of gogcli stored Keep-specific service account keys under `keep-sa-<email>.json`. The current implementation maintains backward compatibility by checking these legacy paths (see lines 48-64 in [`internal/googleapi/service_account.go`](https://github.com/steipete/gogcli/blob/main/internal/googleapi/service_account.go)). New installations should use the generic `auth service-account` command described above.

## Key Implementation Files

| File | Role |
|------|------|
| [`internal/cmd/auth_service_account.go`](https://github.com/steipete/gogcli/blob/main/internal/cmd/auth_service_account.go) | CLI sub-command that stores, removes, and reports service-account JSON. [View source](https://github.com/steipete/gogcli/blob/main/internal/cmd/auth_service_account.go) |
| [`internal/googleapi/service_account.go`](https://github.com/steipete/gogcli/blob/main/internal/googleapi/service_account.go) | Reads stored files and creates JWT token sources with domain-wide delegation. [View source](https://github.com/steipete/gogcli/blob/main/internal/googleapi/service_account.go) |
| [`internal/googleapi/client.go`](https://github.com/steipete/gogcli/blob/main/internal/googleapi/client.go) | Selects service-account token source or falls back to OAuth. [View source](https://github.com/steipete/gogcli/blob/main/internal/googleapi/client.go) |
| [`internal/config/paths.go`](https://github.com/steipete/gogcli/blob/main/internal/config/paths.go) | Determines safe config-directory paths for stored keys. [View source](https://github.com/steipete/gogcli/blob/main/internal/config/paths.go) |
| [`internal/errfmt/errfmt.go`](https://github.com/steipete/gogcli/blob/main/internal/errfmt/errfmt.go) | Error formatting when service-account authentication fails. [View source](https://github.com/steipete/gogcli/blob/main/internal/errfmt/errfmt.go) |

## Summary

- **Domain-wide delegation** requires enabling the feature in both Google Cloud (service account settings) and the Google Workspace Admin console (API controls).
- **Store the JSON key** using `gog auth service-account set <email> --key <path>`; the file is saved with `0600` permissions in the user config directory.
- **Impersonate users** by passing `--account <email>` to any gogcli command, or omit the flag if only one account is configured.
- The implementation relies on `google.JWTConfigFromJSON` with a `Subject` field set to the target user, handled in [`internal/googleapi/service_account.go`](https://github.com/steipete/gogcli/blob/main/internal/googleapi/service_account.go).

## Frequently Asked Questions

### What is domain-wide delegation and why do I need it for gogcli?

Domain-wide delegation is a Google Workspace feature that allows a service account to impersonate any user within the organization without their individual consent. You need it for gogcli when you want to automate operations across Drive, Calendar, Gmail, or other Workspace services without triggering interactive OAuth flows for every user. According to the source code in [`internal/googleapi/service_account.go`](https://github.com/steipete/gogcli/blob/main/internal/googleapi/service_account.go), the CLI sets the JWT `Subject` field to the target user email, which only succeeds if the service account client ID is authorized in the Admin console.

### Can I configure multiple service accounts with gogcli?

Yes, you can store multiple service account keys by running `gog auth service-account set` with different user emails. Each key is stored separately in the configuration directory using a base64-encoded filename derived from the email address, as implemented in [`internal/config/paths.go`](https://github.com/steipete/gogcli/blob/main/internal/config/paths.go). When executing commands, use the `--account` flag to specify which impersonated user to use. If only one account is configured, gogcli automatically selects it without requiring the flag.

### How do I revoke or remove a stored service account?

To remove a service account configuration, use the `unset` subcommand:

```bash
gog auth service-account unset user@example.com

```

This deletes the JSON file from the configuration directory. The operation is handled by the same [`auth_service_account.go`](https://github.com/steipete/gogcli/blob/main/auth_service_account.go) file that manages the `set` and `status` commands. After removal, commands requiring that account will fall back to standard OAuth authentication or return an error if no valid credentials remain.

### What OAuth scopes does gogcli require for full functionality?

The specific scopes depend on which Google services you intend to automate. According to the source analysis, typical scopes include `https://www.googleapis.com/auth/drive` for Drive operations, `https://www.googleapis.com/auth/gmail.readonly` for Gmail access, `https://www.googleapis.com/auth/calendar` for Calendar management, `https://www.googleapis.com/auth/keep` for Google Keep, and `https://www.googleapis.com/auth/contacts.readonly` for Contacts. When configuring domain-wide delegation in the Google Workspace Admin console, you must authorize the service account's Client ID with the specific scopes you plan to use, as the JWT token source in [`internal/googleapi/service_account.go`](https://github.com/steipete/gogcli/blob/main/internal/googleapi/service_account.go) requests these scopes during authentication.