Skip to main content

Google Service Accounts

A service account lets a Google Workspace admin grant EmailEngine access to any mailbox in the organization without asking each user to consent. This guide covers setting one up with domain-wide delegation.

Two authentication methods

EmailEngine supports two ways to authenticate a service account:

  • Service account key - the classic flow with a JSON key file containing a private RSA key. Covered in Steps 1-8 below.
  • Workload Identity Federation - a keyless flow: your runtime (GKE, EKS, AKS, or any OIDC source) provides a short-lived token that Google trusts; no long-lived key is stored. See Alternative: Workload Identity Federation.

Both methods use the same domain-wide delegation setup. Only the signing step differs. The form offers a third method, Attached service account, which works only for Cloud Pub/Sub applications and cannot be used for mailbox access; see Attached Service Account.

Overview​

What are Service Accounts?​

Service accounts are a special type of Google account intended for applications rather than users. With domain-wide delegation, a service account can act on behalf of any user in your Google Workspace organization.

How EmailEngine Uses Service Accounts​

EmailEngine supports service accounts for two distinct purposes, selected in the Base scope card when configuring the service account:

1. Direct Email Access (IMAP and SMTP)

Service accounts with domain-wide delegation can access any mailbox in your Google Workspace organization via IMAP and SMTP protocols. This is the primary use case covered in this guide.

  • Select IMAP and SMTP as the base scope
  • Requires https://mail.google.com/ scope in domain-wide delegation
  • Service account impersonates users to access their mailboxes

The impersonated mailboxes can also be reached through the Gmail REST API instead of IMAP and SMTP: select Gmail API as the base scope (baseScopes: "api"), delegate https://www.googleapis.com/auth/gmail.modify instead, enable the Gmail API in the project, and pick the Cloud Pub/Sub application that delivers change notifications under Pub/Sub app for real-time notifications, as for a Gmail API application. The admin form offers this choice since v2.82.1; earlier versions accepted it only through POST /v1/oauth2. The rest of this guide covers the IMAP and SMTP setup.

2. Push Notifications for Standard Gmail Accounts (Cloud Pub/Sub)

Service accounts can also be used to manage Gmail Push Notification subscriptions (Pub/Sub) for regular Gmail OAuth2 accounts. In this scenario:

  • Select Cloud Pub/Sub as the base scope
  • Requires Pub/Sub Admin role in Google Cloud (not domain-wide delegation)
  • Individual users authenticate with standard OAuth2 (not service accounts)
  • A single service account manages Pub/Sub topic subscriptions for all accounts
  • Email changes are delivered as push notifications instead of the 10-minute fallback poll
  • The service account only handles the notification infrastructure, not email access

For push notification setup, see Setting Up Gmail API.

Key Concepts​

Two-Legged OAuth2:

  • No user interaction required
  • No consent screens
  • Admin grants access once for entire domain
  • EmailEngine can access any user's mailbox

Domain-Wide Delegation:

  • Requires Google Workspace (not free Gmail)
  • Requires super admin privileges
  • Admin authorizes specific scopes for the service account
  • Service account can impersonate any user in the domain

When to Use Service Accounts​

Best For:

  • Enterprise Google Workspace deployments
  • Centralized email management
  • Accessing multiple user mailboxes from one application
  • Automated workflows without user interaction
  • Systems integration and monitoring

Not Suitable For:

  • Free Gmail accounts (Google Workspace only)
  • Public-facing applications (users outside your organization)
  • Applications where users should control access

Benefits and Limitations​

Benefits​

No User Consent Required:

  • Access granted by admin once
  • No OAuth2 flow for each user
  • No interactive login for any mailbox in the domain

Centralized Management:

  • Admin controls all access
  • Easy to revoke for entire organization
  • Audit trail of service account usage

Scalability:

  • Add new users without re-authentication
  • Access any user's mailbox with same credentials
  • Ideal for enterprise deployments

Limitations​

Google Workspace Only:

  • Does not work with free @gmail.com accounts
  • Requires paid Google Workspace subscription

Requires Admin Access:

  • Must be Google Workspace super admin
  • Cannot be delegated to non-admin users

Organization-Scoped:

  • Only works for users in your organization
  • Cannot access external Gmail accounts

Credential Security:

  • Service account credentials provide access to all users
  • Must be stored very securely
  • Compromised credentials = access to entire organization's email
  • For deployments where storing long-lived JSON keys is unacceptable, use Workload Identity Federation instead

Step 1: Create a Google Cloud Project​

Go to Google Cloud Console and create a new project.

Click the project selector and then New Project.

Fill in the project name (e.g., "EmailEngine Service Account").

Wait for the project to be created, then click Select project to switch to it.

Even though service accounts don't show consent screens to users, the form allows us to configure required project details.

Consent Screen vs Domain-Wide Delegation

The OAuth consent screen and domain-wide delegation are separate configurations:

  • OAuth consent screen (this step): Configures project metadata and documents which scopes your project uses. Required to complete project setup, but service accounts bypass this screen during authentication.
  • Domain-wide delegation (Step 4): The actual authorization that grants your service account permission to access user mailboxes. This is configured in the Google Admin Console.

Service accounts authenticate using JWT tokens, not the OAuth consent flow, so users never see the consent screen.

Navigate to APIs & Services > OAuth consent screen.

Select User Type​

Select Internal and click Create.

User Type Selection

The "Internal" vs "External" choice primarily affects standard OAuth2 flows where users see consent screens. Since service accounts authenticate using JWT tokens and bypass the consent screen entirely, this setting has no direct impact on service account functionality. Select Internal for simplicity since service accounts are designed for organization-internal use.

Fill in App Information​

Fill in the required fields:

  • App name: "EmailEngine Service Account" (or your app name)
  • User support email: Your email address
  • Developer contact information: Your email address

These fields are required but won't be shown to users since service accounts don't use consent screens.

Click Save and continue.

Configure Scopes (Optional)​

Click Add or remove scopes.

For service accounts, the actual scope authorization happens in the Google Admin Console's domain-wide delegation settings (Step 4), not here. However, adding scopes here documents what your project uses and is required if you later add standard OAuth2 clients.

If desired, scroll to the end of the scopes list and manually add:

https://mail.google.com/

Click Add to table then Update.

The scope should now appear in the "Restricted scopes" section.

info

This step configures project-level scope documentation. The actual permissions for your service account are granted separately through domain-wide delegation in the Admin Console (Step 4). You can skip adding scopes here and proceed directly.

Click Save and continue to finish consent screen setup.

Step 3: Create Service Account​

Navigate to APIs & Services > Credentials and click the Manage service accounts link (bottom right corner).

Click Create service account.

Configure Service Account​

Service account name: Choose a descriptive name (e.g., "emailengine-service")

Service account ID: Auto-generated, but you can customize

Description: Optional description

Click Create and continue.

Grant Role​

Role: Select Owner

Role Selection

The "Owner" role is used here for simplicity. For production, you may want to use a more restrictive role, but "Owner" has been tested and confirmed working.

Click Continue.

Complete Setup​

Leave the optional fields empty and click Done.

Step 4: Enable Domain-Wide Delegation​

Now we need to authorize the service account to access user mailboxes.

Get Client ID​

From the service accounts list, copy the OAuth2 Client ID for your service account.

Finding Client ID

The Client ID is a long numeric string in the service account listing. You'll need this for domain-wide delegation setup.

Open Google Admin Console​

Open Google Admin Console for your domain and search for "API Controls".

Manage Domain-Wide Delegation​

Scroll down to find the "Domain-wide delegation" section and click Manage domain-wide delegation.

Click Add new.

Authorize API Client​

Client ID: Paste the Client ID you copied from the service account

OAuth scopes: Enter the required scope for IMAP and SMTP access:

https://mail.google.com/

Click Authorize.

This is the Actual Authorization

This step in the Admin Console is where the service account actually receives permission to access user mailboxes. The scope entered here determines what the service account can do. The OAuth consent screen configuration (Step 2) is separate and primarily for project documentation.

Step 5: Generate Service Account Key​

Prefer a keyless setup?

If your clients prohibit long-lived service-account JSON keys for compliance reasons, you can skip generating a key entirely. See Alternative: Workload Identity Federation below. The federation flow replaces only the signing key - every other step in this guide stays the same.

Return to the Google Cloud Console service accounts page.

Open the context menu for your service account (three dots) and click Manage keys.

Click Add Key > Create new key.

Select JSON as the key type and click Create.

The browser will automatically download the key file as a .json file.

Store Key Securely

This key file provides access to all email accounts in your organization. Store it very securely:

  • Use secret management systems (AWS Secrets Manager, Azure Key Vault, etc.)
  • Never commit to version control
  • Restrict file permissions
  • Rotate keys periodically

Key File Format​

The downloaded JSON file contains:

{
"type": "service_account",
"project_id": "service-test-338412",
"private_key_id": "83fd56801b0e46d21ad88300b73d3727e6d46961",
"private_key": "-----BEGIN PRIVATE KEY-----\nMIIEvQIBADANBgkqhkiG9w...\n",
"client_email": "emailengine-2lo-test@service-....iam.gserviceaccount.com",
"client_id": "103965568215821627203",
"auth_uri": "https://accounts.google.com/o/oauth2/auth",
"token_uri": "https://oauth2.googleapis.com/token",
"auth_provider_x509_cert_url": "https://www.googleapis.com/oauth2/v1/certs",
"client_x509_cert_url": "https://www.googleapis.com/robot/.../x509/emaile..."
}

The important fields for EmailEngine are:

  • client_id: Maps to Service account client ID in EmailEngine (API field serviceClient)
  • client_email: Maps to Service account email in EmailEngine (serviceClientEmail)
  • private_key: Maps to Private key in EmailEngine (serviceKey)
  • project_id: Maps to Google Cloud project ID in EmailEngine (googleProjectId, used for Gmail push notifications)

Step 6: Enable Gmail API (Optional)​

Enabling the Gmail API is not needed for the IMAP and SMTP base scope: EmailEngine obtains a token for the impersonated user with a signed JWT and logs in to imap.gmail.com and smtp.gmail.com with IMAP and SMTP XOAUTH2. Enable it if you want the Gmail scopes listed on the consent screen or use the same project for a Gmail API OAuth2 application.

Navigate to APIs & Services > Dashboard and click Enable APIs and Services.

Search for "mail" to find Gmail API.

Click Enable.

Step 7: Configure EmailEngine​

Now configure EmailEngine to use the service account.

Add Gmail Service Account Application​

  1. Open EmailEngine dashboard
  2. Navigate to Integrations > OAuth2 Apps
  3. Click the Create OAuth2 app dropdown
  4. Select Gmail Service Accounts

Give the app a name in the Application card, then pick the base scope and load the credentials as described below. The form lists the cards in that order: Application, Base scope, Credentials.

Select Base Scope​

The Base scope card offers one tab per mode ("What the app connects with"), and the selection determines how EmailEngine uses this service account. It cannot be changed once the app is created:

TabPurposeRequired Scope/Role
IMAP and SMTPDirect email access via IMAP/SMTP protocolshttps://mail.google.com/ (domain-wide delegation)
Gmail APIDirect email access via the Gmail REST API (in the admin form since v2.82.1)https://www.googleapis.com/auth/gmail.modify (domain-wide delegation)
Cloud Pub/SubWebhook management for Gmail API accountsPub/Sub Admin role in Google Cloud

For direct email access (the primary use case in this guide), select IMAP and SMTP. This allows the service account to access any mailbox in your Google Workspace organization via IMAP and SMTP protocols.

For push notification management, select Cloud Pub/Sub. This is used when you have regular Gmail OAuth2 accounts (where users authenticate individually) but want a single service account to manage Pub/Sub topic subscriptions for receiving email change notifications. See Setting Up Gmail API for this use case.

Load the Credentials File​

In the Credentials card, keep the Service account key authentication method, click Load from the key file... and select the service account key JSON file.

EmailEngine extracts and fills in:

  • Service account client ID (from client_id)
  • Service account email (from client_email)
  • Private key (from private_key)
  • Google Cloud project ID (from project_id)

Click Register app to save. Service account applications never appear in the hosted authentication form, so the form has no Enable this app checkbox for them; the app is listed as always enabled.

JSON Key File After Setup

Once EmailEngine has been configured with the service account credentials, the JSON key file is no longer needed. EmailEngine stores the credentials in its database. You can delete the JSON file from your local system to reduce security risk.

If you need to configure the same service account in another EmailEngine instance in the future, generate a fresh key from Google Cloud Console rather than reusing an old key file. This follows the security best practice of not storing or sharing key files.

Enable Field Encryption

Unless EENGINE_SECRET is set, EmailEngine stores credentials in cleartext in Redis. To protect sensitive data like service account private keys, enable field encryption. See Setting Up Encryption for configuration instructions.

Find Your App ID​

After registering the app, note the App ID displayed in the OAuth2 application list. This is a base64url-encoded identifier (e.g., AAABkTn2CRQAAAAB) that you'll need when adding accounts via the API.

You can also find the App ID by calling the List OAuth2 Apps API endpoint:

curl https://emailengine.example.com/v1/oauth2 \
-H "Authorization: Bearer YOUR_EMAILENGINE_TOKEN"

Verify the Setup​

Before adding accounts, use the Verify setup button on the OAuth2 app's page (or POST /v1/oauth2/{app}/verify) to test the whole authentication chain. The verifier checks the service account configuration, signs and exchanges the JWT assertion, and - if you provide a test mailbox address in account - obtains a domain-wide delegation token for that user and performs a read-only IMAP login (pass "testConnection": false to skip the login). Each step is reported as ok, fail, or skip with a hint for fixing failures, so problems like a missing delegation scope surface immediately instead of when the first account fails to connect.

Alternative: Workload Identity Federation (Keyless)​

Workload Identity Federation (WIF) lets EmailEngine authenticate as a Google service account without storing a long-lived JSON key. Instead, the workload it runs on (a GKE pod, an EKS pod, a VM with managed identity, or any OIDC-emitting source) presents a short-lived token that Google's Security Token Service exchanges for a federated access token. EmailEngine then asks Google to sign the JWT bearer assertion server-side via the IAM Credentials API.

The end result is identical to the key-based flow: a signed JWT is exchanged at oauth2.googleapis.com/token for an IMAP XOAUTH2 access token. Only the signing step changes.

Why use WIF​

  • No JSON keys to rotate, leak, or revoke. The signing key never leaves Google.
  • Compliance. Many security frameworks now flag long-lived service-account keys; WIF removes them entirely.
  • Audit trail. Every signJwt call is recorded in Google Cloud audit logs against the impersonated service account.

When to use WIF​

WIF is only useful when EmailEngine runs in an environment that can produce a credential Google trusts:

  • Google Kubernetes Engine (GKE) with Workload Identity bound to a Google IAM service account
  • Amazon EKS with IAM Roles for Service Accounts (IRSA) projecting a service-account token
  • Azure Kubernetes Service (AKS) with workload identity / managed identity
  • Self-managed Kubernetes with a projected service-account token volume
  • Any OIDC identity provider exposing tokens over HTTP

If you self-host EmailEngine on a plain VM or on the EmailEngine Hosted service, stick with the key-based flow described above.

What stays the same​

Steps 1-4 and 6 of this guide are unchanged. You still:

  • Create the Google Cloud project
  • Configure the OAuth consent screen
  • Create the service account
  • Enable domain-wide delegation in the Google Workspace admin console with the same scopes (https://mail.google.com/)
  • Enable the Gmail API

Instead of Step 5 (generating and downloading a JSON key), federation replaces the signing key with a chain you set up once: enable two APIs, create a workload identity pool and provider, grant the pool one IAM binding on the service account, and point EmailEngine at an external account credential configuration that reads your workload's identity token. The following subsections walk through each part.

Enable the required APIs​

In the Google Cloud project, enable the two APIs the federation flow calls:

  • Security Token Service API (sts.googleapis.com) - exchanges the workload's OIDC token for a federated Google access token.
  • IAM Service Account Credentials API (iamcredentials.googleapis.com) - signs the JWT bearer assertion (signJwt).
gcloud services enable sts.googleapis.com iamcredentials.googleapis.com --project=PROJECT_ID

Create a Workload Identity Pool and provider​

Create a workload identity pool and an OIDC provider that trusts the OIDC issuer of the environment EmailEngine runs in (for GKE, the cluster's OIDC issuer URL; for EKS, the cluster OIDC provider URL; for any other source, its issuer).

# Create the pool
gcloud iam workload-identity-pools create POOL_ID \
--project=PROJECT_ID \
--location=global \
--display-name="EmailEngine WIF pool"

# Create an OIDC provider in the pool
gcloud iam workload-identity-pools providers create-oidc PROVIDER_ID \
--project=PROJECT_ID \
--location=global \
--workload-identity-pool=POOL_ID \
--issuer-uri="https://ISSUER_OF_YOUR_WORKLOAD" \
--attribute-mapping="google.subject=assertion.sub"
Attribute mapping

google.subject=assertion.sub maps the sub claim of the workload's OIDC token to the federated principal that the IAM binding below references. Add an --attribute-condition if you want to restrict which workload identities the pool accepts.

You will also need the project number (not the ID) - it is part of the pool audience used in the binding and the credential configuration:

gcloud projects describe PROJECT_ID --format="value(projectNumber)"

Grant the pool access to the service account​

This is the one federation-specific binding the service account needs. EmailEngine calls iamcredentials.googleapis.com signJwt as the federated pool principal - it does not first impersonate the service account - so that principal must hold Service Account Token Creator (roles/iam.serviceAccountTokenCreator) on the target service account:

gcloud iam service-accounts add-iam-policy-binding GSA_EMAIL \
--project=PROJECT_ID \
--role=roles/iam.serviceAccountTokenCreator \
--member="principalSet://iam.googleapis.com/projects/PROJECT_NUMBER/locations/global/workloadIdentityPools/POOL_ID/*"

The principalSet://.../* member grants every identity from the pool. To scope it to a single workload identity, use a principal://.../subject/SUBJECT_VALUE member instead, where SUBJECT_VALUE is the sub claim of the workload's OIDC token.

Workload Identity User is not sufficient

A common mistake - including in Google's own "Grant access" helper - is to grant roles/iam.workloadIdentityUser. That role permits generateAccessToken but not signJwt, so EmailEngine's token acquisition fails with a 403 on signJwt (surfaced as the ESignJwt error). Because EmailEngine signs the assertion directly via signJwt, the pool principal must have roles/iam.serviceAccountTokenCreator on the service account. Do not grant the service account this role on itself - the federated pool principal is the caller, not the service account.

Generate the external account credential configuration​

Use gcloud iam workload-identity-pools create-cred-config to produce the JSON file. The exact arguments depend on your workload identity provider, but the output always conforms to the same external-account schema.

Example for GKE:

gcloud iam workload-identity-pools create-cred-config \
projects/PROJECT_NUMBER/locations/global/workloadIdentityPools/POOL_ID/providers/PROVIDER_ID \
--service-account=GSA_EMAIL \
--credential-source-file=/var/run/secrets/tokens/gcp-ksa/token \
--output-file=external-account.json

The resulting external-account.json looks like:

{
"type": "external_account",
"audience": "//iam.googleapis.com/projects/PROJECT_NUMBER/locations/global/workloadIdentityPools/POOL_ID/providers/PROVIDER_ID",
"subject_token_type": "urn:ietf:params:oauth:token-type:jwt",
"token_url": "https://sts.googleapis.com/v1/token",
"service_account_impersonation_url": "https://iamcredentials.googleapis.com/v1/projects/-/serviceAccounts/GSA_EMAIL:generateAccessToken",
"credential_source": {
"file": "/var/run/secrets/tokens/gcp-ksa/token",
"format": { "type": "text" }
}
}

Example for an OIDC provider exposing tokens over HTTP (Azure IMDS, custom OIDC issuer, etc.):

{
"type": "external_account",
"audience": "//iam.googleapis.com/projects/PROJECT_NUMBER/locations/global/workloadIdentityPools/POOL_ID/providers/PROVIDER_ID",
"subject_token_type": "urn:ietf:params:oauth:token-type:id_token",
"token_url": "https://sts.googleapis.com/v1/token",
"service_account_impersonation_url": "https://iamcredentials.googleapis.com/v1/projects/-/serviceAccounts/GSA_EMAIL:generateAccessToken",
"credential_source": {
"url": "http://169.254.169.254/metadata/identity/oauth2/token?api-version=2018-02-01&resource=AUDIENCE",
"headers": { "Metadata": "true" },
"format": { "type": "json", "subject_token_field_name": "access_token" }
}
}
Supported credential sources

EmailEngine supports the file and url credential sources. The executable and AWS sigv4 aws4_request variants are not implemented; EKS users should use the projected service-account token at the standard mount path, which uses the file source.

Mount the projected token (GKE)​

Your pod spec must mount a projected service-account token volume at the path referenced in credential_source.file:

spec:
serviceAccountName: KSA_NAME
containers:
- name: emailengine
volumeMounts:
- name: gcp-ksa
mountPath: /var/run/secrets/tokens/gcp-ksa
readOnly: true
volumes:
- name: gcp-ksa
projected:
sources:
- serviceAccountToken:
path: token
audience: https://iam.googleapis.com/projects/PROJECT_NUMBER/locations/global/workloadIdentityPools/POOL_ID/providers/PROVIDER_ID
expirationSeconds: 3600

The Kubernetes kubelet rotates this token automatically before expiry; EmailEngine re-reads the file on every cold token refresh.

Configure EmailEngine​

In the EmailEngine OAuth2 app form:

  1. Select Gmail Service Accounts as the provider.
  2. Choose the Workload Identity Federation tab as the authentication method in the Credentials card.
  3. Fill in the Google Cloud project ID, Service account email, and Service account client ID fields with the same values you would use for the key-based flow. The Load from the configuration file... button auto-fills the service account email from the impersonation URL.
  4. Paste the contents of external-account.json into the External account configuration textarea, or load it with Load from the configuration file....
  5. Click Register app.

EmailEngine validates the configuration on save: it must be JSON with "type": "external_account", carry audience, subject_token_type, token_url and service_account_impersonation_url, and token_url must point at Google's STS endpoint. A credential source it cannot read is reported when the token is first requested, as ESubjectTokenRead.

Service account email must match the impersonated service account

In federation mode EmailEngine issues the JWT bearer assertion as the service account named in the configuration's service_account_impersonation_url. The Service account email field must be that same service account address. If they differ, EmailEngine reports EServiceAccountMismatch the first time it uses the application (the Verify setup check, or the first token request), rather than failing at Google with an opaque invalid_grant.

The authentication method (service account key vs Workload Identity Federation) is fixed once the application is created. To switch methods, create a new application.

Verify the setup​

After registering the app, use the built-in setup verifier to confirm every step works before pointing real accounts at it. On the OAuth2 application page click Verify setup, or call the API:

curl -X POST https://emailengine.example.com/v1/oauth2/APP_ID/verify \
-H "Authorization: Bearer YOUR_EMAILENGINE_TOKEN" \
-H "Content-Type: application/json" \
-d '{ "account": "user@yourdomain.com" }'

The verifier runs the real authentication chain and reports each step as ok, fail, or skip with an actionable hint:

  1. Configuration - the external account JSON is structurally valid.
  2. Read OIDC subject token - the credential source (file or url) is readable.
  3. STS token exchange - the pool, provider, audience, and issuer line up.
  4. Sign assertion (signJwt) - the pool principal can sign as the service account. This is what catches the Token Creator binding mistake described above.
  5. Domain-wide delegation token - Google issues an access token for the impersonated user (requires the account field).
  6. Mailbox access - a live, read-only IMAP login confirms the token works and that IMAP access is enabled for the domain.

Steps 1-4 run without an account; supply a mailbox address in your Workspace domain to also exercise steps 5-6. The verifier is read-only - it never modifies a mailbox or sends mail.

Troubleshooting​

When token acquisition fails, EmailEngine surfaces a stable error code that points at the failing step. The Verify setup action above reports the same codes per step.

Error codeWhat it meansLikely fix
EExternalAccountConfigThe stored JSON is malformed, has the wrong type, or uses an unsupported credential source.Re-run gcloud iam workload-identity-pools create-cred-config and re-paste the JSON. Ensure type is external_account and credential_source uses file or url.
EServiceAccountMismatchThe Service account email field does not match the service account in the service_account_impersonation_url.Set the service account email to the same address the impersonation URL points at.
ESubjectTokenReadEmailEngine could not read the subject token (file missing, URL unreachable, empty response).Verify the projected token volume is mounted at the path in credential_source.file. For url sources, confirm the endpoint is reachable and any required headers are set.
ESTSExchangeGoogle STS rejected the token exchange.Common causes: wrong audience, the workload's OIDC issuer does not match the provider's issuer-uri, the subject token's audience does not match the provider, or the pool/provider is disabled. Check the error_description in the EmailEngine error log.
ESignJwtThe iamcredentials.googleapis.com signJwt call failed.A 403 means the federated pool principal is missing roles/iam.serviceAccountTokenCreator on the service account (see the binding above - workloadIdentityUser is not enough). A 429 means requests are being rate-limited, which is rare under normal token-refresh cadence.
EUnknownAuthMethodEmailEngine received an unrecognized authMethod value.Should not occur via the admin UI; indicates direct API misuse.

If domain-wide delegation has not been authorized in the Workspace admin console for the impersonating service account, the final token exchange at oauth2.googleapis.com/token returns unauthorized_client regardless of WIF status. That error path is identical to the key-based flow.

Hosted EmailEngine​

The hosted EmailEngine service does not run inside your workload-identity boundary, so WIF is not available there. Hosted customers continue with the key-based flow.

Step 8: Add Email Accounts​

With the service account configured, you can now add email accounts without any user interaction.

Add Account via the Admin UI​

Since service-account apps authenticate without an interactive consent screen, the OAuth2 app's detail page in the EmailEngine dashboard provides an Add account button (since v2.68.0). It opens a dialog asking for the full name, the email address and an optional account ID, and registers the account directly - no API call needed. Leave the ID blank to have one generated; an existing ID updates that account.

The button is available for service apps with the IMAP and SMTP base scope. Pub/Sub-scoped service apps grant no mailbox access, so they do not offer it.

Add Account via API​

Add accounts using the Register Account API endpoint:

curl -X POST https://emailengine.example.com/v1/account \
-H "Authorization: Bearer YOUR_EMAILENGINE_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"account": "user123",
"name": "John Doe",
"email": "john@company.com",
"oauth2": {
"provider": "AAABkTn2CRQAAAAB",
"auth": {
"user": "john@company.com"
}
}
}'

Replace AAABkTn2CRQAAAAB with your actual App ID from EmailEngine.

Key Points:

  • No accessToken or refreshToken needed
  • No passwords required
  • Just specify the user email address
  • Service account handles authentication automatically

Response:

{
"account": "user123",
"state": "new"
}

The account will be created and should transition to "connected" state within moments.

Add Multiple Accounts​

You can add any user in your organization using the same App ID:

# Add sales team accounts (replace AAABkTn2CRQAAAAB with your App ID)
curl -X POST https://emailengine.example.com/v1/account \
-H "Authorization: Bearer YOUR_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"account": "sales-1",
"email": "alice@company.com",
"oauth2": {
"provider": "AAABkTn2CRQAAAAB",
"auth": { "user": "alice@company.com" }
}
}'

curl -X POST https://emailengine.example.com/v1/account \
-H "Authorization: Bearer YOUR_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"account": "sales-2",
"email": "bob@company.com",
"oauth2": {
"provider": "AAABkTn2CRQAAAAB",
"auth": { "user": "bob@company.com" }
}
}'

Verify Accounts​

Check the accounts list in EmailEngine:

Accounts registered through a service account show Gmail Service Accounts as their provider in the list.

Account Management​

Listing Service Account Accounts​

Service account accounts appear like any other OAuth2 account:

curl https://emailengine.example.com/v1/accounts \
-H "Authorization: Bearer YOUR_TOKEN"

Updating Service Account Accounts​

Update account settings using the Update Account API endpoint:

curl -X PUT https://emailengine.example.com/v1/account/user123 \
-H "Authorization: Bearer YOUR_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"name": "John Doe (Updated)",
"subconnections": ["\\Sent"]
}'

Recovering a Switched-Off Account​

If domain-wide delegation is revoked or the key is rotated, every account using the application fails authentication. A Google token endpoint that answers 408, 429 or a 5xx is not counted as a failure: the attempt is retried and no authenticationError is sent (since v2.80.0). After the failures have run for longer than EENGINE_MAX_IMAP_AUTH_FAILURE_TIME (three days by default), EmailEngine switches the account off: it reports the unset state with a non-null authFailureDisabledAt, and a reconnect request returns {"reconnect": false}.

Once the delegation or key is fixed, bring the account back with Resume syncing on its page in the admin interface, or by registering it again with POST /v1/account under the same account ID, which lifts the switch and reconnects (since v2.79.4). See Accounts switched off after authentication failures.

Deleting Service Account Accounts​

Delete accounts using the Delete Account API endpoint:

curl -X DELETE https://emailengine.example.com/v1/account/user123 \
-H "Authorization: Bearer YOUR_TOKEN"

Security Best Practices​

Key Storage​

Do:

  • Store keys in secret management systems (AWS Secrets Manager, Azure Key Vault, HashiCorp Vault)
  • Encrypt key files at rest
  • Use environment variables for production
  • Restrict file permissions (0600)

Don't:

  • Commit keys to version control
  • Store keys in plain text
  • Share keys via email or chat
  • Store keys in public locations

Access Control​

Limit Access:

  • Only admins should access service account keys
  • Use separate keys for different environments (dev/staging/prod)
  • Audit key usage regularly
  • Rotate keys periodically (e.g., every 90 days)

Monitor Usage:

  • Enable Google Cloud audit logs
  • Monitor EmailEngine access logs
  • Alert on unusual account access patterns
  • Review service account activity regularly

Scope Limitation​

Principle of Least Privilege:

  • Only grant scopes actually needed
  • For IMAP access, https://mail.google.com/ is required
  • Consider separate service accounts for different purposes
  • Document why each scope is needed

Use Cases​

Enterprise Email Management​

Monitor and manage all employee email accounts:

  • Compliance and archiving
  • Security scanning
  • Backup solutions
  • E-discovery

Help Desk Integration​

Access user mailboxes for support:

  • Troubleshoot email issues
  • Recover deleted messages
  • Investigate delivery problems
  • Provide user assistance

Automated Workflows​

Build automated systems:

  • Invoice processing from shared mailbox
  • Customer support ticket creation
  • Email-based data extraction
  • Notification routing

Migration and Sync​

Synchronize email data:

  • Migrate between systems
  • Backup to external storage
  • Mirror to CRM systems
  • Sync with document management

Official Google Documentation​

For the most up-to-date information, refer to Google's official documentation:

Domain-Wide Delegation​

Service Accounts​

Gmail IMAP/SMTP OAuth​

See Also​