For an overview of what IAM Federation does and the audit trail it produces, see the IAM Federation for GCP overview.
You configure federation on a connection in the Hoop web UI: pick IAM
Federation as the connection method, paste the admin service account, and map
identities. The flow has three parts:
- Complete the GCP IAM setup on this page (gcloud): mint the admin service account, grant it Token Creator on the user principals, and verify impersonation from a shell.
- In the Hoop UI, create or edit the connection and choose IAM Federation as the connection method. Paste the admin SA JSON, set the GCP project, and define the identity mapping.
- Click Test as user to dry-run the mapping for a specific Hoop user. No session is opened and no audit record is written.
Prerequisites
GenerateAccessToken call with PERMISSION_DENIED: IAM Service Account Credentials API has not been used.
Step 1: Create the admin service account
The admin SA is the only identity Hoop’s gateway authenticates with. It never reads user data; it only mints tokens for the user principals.hoop-admin-sa.json into the connection’s IAM Federation form later (the Admin role credentials → Service account JSON field). Hoop encrypts it at rest, and the gateway becomes the only place it lives, so delete the local file once the connection is saved.
Step 2: Grant Token Creator (pick one pattern)
Federation involves two kinds of service account. Mixing up which one needs which role is the most common setup mistake, so settle the mental model before running any command:| Service account | Role it needs | Queries BigQuery? | Who holds its key |
|---|---|---|---|
Admin SA (e.g. hoop-admin) | roles/iam.serviceAccountTokenCreator on each user SA | No | Hoop’s gateway |
User SA (e.g. alice) | roles/bigquery.user + roles/bigquery.dataViewer (or finer) | Yes | Nobody; only ever impersonated |
Pattern A: Per-user grant (recommended)
Create one GCP service account per Hoop user, then let the admin SA impersonate only those accounts. This gives the cleanest audit trail and the smallest blast radius if the admin key ever leaks.Pattern B: Project-wide grant (simpler)
Grant the admin SAserviceAccountTokenCreator at the project level once. It can then impersonate any SA in the project. This is faster to set up, but a compromised admin key reaches every SA in the project.
Step 3: Verify the grant before saving in Hoop
Before pasting the admin key into Hoop, prove the impersonation works from a regular shell. Hoop makes this same call on every session open, so a green light here means a green light in Hoop.| Output | Meaning | Fix |
|---|---|---|
A long ya29.… token | All grants correct. Ready to feed hoop-admin-sa.json to Hoop. | None |
403: The caller does not have permission | Admin SA is missing roles/iam.serviceAccountTokenCreator on the target. | Re-run step 2. |
404: Service account … does not exist | Typo in the target email or the user-SA was never created. | Re-check gcloud iam service-accounts list. |
400: Invalid form of account ID | The identity template would produce an illegal SA name (dots, plus signs, too short). | Adjust the template (see the warning under Pattern A). |
Configure federation on the connection
With the GCP side verified, configure federation on the connection in the Hoop web UI. Create a new connection (or edit an existing one) and, under Role connection method, choose IAM Federation (the alternative to Manual Input and Secrets Manager).
Pick an agent for the connection before testing. The Test as user
button stays disabled until an agent is selected, because the dry-run probe
runs through that agent.
- Service account JSON: paste the full contents of
hoop-admin-sa.jsonfrom Step 1. This is the admin SA that mints tokens; it never touches end-user sessions. On an existing connection the field is masked, so click Replace credentials to rotate it. - GCP Project ID: your project, e.g.
my-proj.
- Source attribute (Hoop): the JSONPath into the Hoop session context, default
$.user.email. - Target principal template (GCP): how the Hoop identity becomes a GCP principal, e.g.
hoop-{user.email}@my-proj.iam.gserviceaccount.com. The{user.email}placeholder follows the same expansion and naming rules as Pattern A above. It renders the email local part, which must form a legal SA name (6–30 chars; add a literal prefix likehoop-if your local parts are too short). - Fallback method: Deny the session (block access when the identity can’t be mapped) or Use the connection’s static credentials (fall back to the credentials stored on the connection). These correspond to
fallback_policy: deny/static.
HOOP_FEDERATED_PRINCIPAL, CLOUDSDK_CORE_PROJECT, and the federated credentials). Your queries use them the same way they would with statically configured credentials.
Save the connection to persist the policy and store the admin SA encrypted at rest.
Test as user
Click Test as user (top-right of Federation setup) to dry-run the mapping. The button enables once the admin credentials, GCP project ID, source attribute, target template, and an agent are all set.
SELECT 1 probe through the connection’s agent, so it catches agent-side problems too.
A successful test reports “Session would start as <email>” along with:
| Field | Meaning |
|---|---|
| Resolved principal | The GCP SA the user maps to, e.g. alice@my-proj.iam.gserviceaccount.com |
| Impersonated via | The admin SA that minted the token, e.g. hoop-admin@my-proj.iam.gserviceaccount.com |
| Token TTL | Lifetime of the minted token, in minutes |
| Env vars emitted | The credential env vars injected into the session |
403 ... getAccessToken denied you’d see when the Token Creator grant from Step 2 is missing or hasn’t propagated. See Common pitfalls below.
On a successful session, Hoop supersedes any static
GOOGLE_APPLICATION_CREDENTIALS set on the connection. It ignores the legacy
SA key file in favor of the federated token and strips it server-side on every
federated session. You don’t need to remove it from the connection manually.Enabling and querying GCP audit logs
After federation is wired up, every BigQuery query produced through Hoop generates two correlated log entries:| Log type | Service | Event | Default? |
|---|---|---|---|
| Admin Activity | iamcredentials.googleapis.com | GenerateAccessToken, proves impersonation happened | On by default, free |
| Data Access | bigquery.googleapis.com | jobservice.insert, jobservice.jobcompleted; proves the query ran as Alice | Off by default, billed per GiB |
Enable Data Access logs first
protoPayload.serviceName: bigquery.googleapis.com entry. Data Access logs bill at standard Cloud Logging rates. That stays small for query metadata, but scope it by sink filter if your project runs millions of jobs per day.
Quick verification with gcloud logging read
Right after a hoop connect my-bq + bq query session as Alice, fetch the two correlated entries:
The
-v-1H argument is the BSD date syntax used on macOS. On Linux
use --date '1 hour ago' instead:
timestamp >= "$(date -u --date='1 hour ago' +%Y-%m-%dT%H:%M:%SZ)".Browse in Cloud Logging Explorer
Console → Logging → Logs Explorer. The canonical filter for “every event that went through Hoop federation, regardless of user” pivots on the delegation chain:principalEmail is the admin SA itself (with no delegation chain) are the ones that bypassed federation. Alert on those to enforce “all access goes through Hoop”.
Long-term audit: sink to BigQuery
For history and analytics, route the audit logs into a BigQuery dataset and query them with SQL. This is the production pattern for compliance reporting._* wildcard covers Cloud Logging’s daily-partitioned sink tables (cloudaudit_googleapis_com_data_access_20260528, ..._20260529, etc.).
Common pitfalls
I see GenerateAccessToken events but no BigQuery query entries
I see GenerateAccessToken events but no BigQuery query entries
BigQuery Data Access logs are off by default. The
GenerateAccessToken
event lives in the always-on Admin Activity tier, but the query itself
(jobservice.insert) lives in Data Access, which you have to opt into.
Enable it project-wide with the auditConfigs snippet under
Enable Data Access logs first, then
re-run a query. New entries appear in Cloud Logging within seconds.403 IAM_SERVICE_ACCOUNT_TOKEN_CREATOR after granting the role
403 IAM_SERVICE_ACCOUNT_TOKEN_CREATOR after granting the role
GCP IAM grants are eventually consistent. Wait 1–2 minutes after running
You should see your admin SA listed with
add-iam-policy-binding, then retry. If it still fails, double-check the
grant is on the target SA (the per-user one), not on the admin SA:roles/iam.serviceAccountTokenCreator.BigQuery returns Access Denied even though impersonation works
BigQuery returns Access Denied even though impersonation works
Impersonation produces the token, but the target principal also needs
BigQuery roles. Verify with:You should see
roles/bigquery.user and roles/bigquery.dataViewer (or finer-grained equivalents).warning: both HOOP_GCP_ACCESS_TOKEN and GOOGLE_APPLICATION_CREDENTIALS are set
warning: both HOOP_GCP_ACCESS_TOKEN and GOOGLE_APPLICATION_CREDENTIALS are set
Harmless on older gateway versions. On current versions the gateway
strips
GOOGLE_APPLICATION_CREDENTIALS from the federated session, so
this warning never fires from a normal session. If you see it now,
your agent is being driven by an older gateway that hasn’t been
updated yet.Identity template renders to an empty principal
Identity template renders to an empty principal
fallback_policy: deny aborts the session with a clear error. Pick
fallback_policy: static if you’d rather the session fall back to the
connection’s existing static credentials instead of failing.PERMISSION_DENIED on getIamPolicy / getAccessToken when running add-iam-policy-binding
PERMISSION_DENIED on getIamPolicy / getAccessToken when running add-iam-policy-binding
You’re still authenticated as the admin SA from the Step 3 verify.
Then re-run the grant. If it still runs as the SA, a
gcloud auth activate-service-account makes it your active account, and it
sticks until you switch back. Service accounts can’t administer IAM, so
the grant is refused. Check who’s active and switch to a human account
with project-admin rights:CLOUDSDK_CORE_ACCOUNT env var (or GOOGLE_APPLICATION_CREDENTIALS
pointing at the SA key) is overriding gcloud config. unset it.Invalid form of account ID: principal looks like name@.iam.gserviceaccount.com
Invalid form of account ID: principal looks like name@.iam.gserviceaccount.com
The project is missing from the email: there’s nothing between
@ and
.iam. Your $PROJECT_ID shell variable is unset, and env vars don’t
survive across new terminal tabs. Re-export and verify before reusing it: