> ## Documentation Index
> Fetch the complete documentation index at: https://mintlify.hoop.dev/docs/llms.txt
> Use this file to discover all available pages before exploring further.

# IAM Federation for GCP

> Mint per-user OAuth tokens for BigQuery and other GCP resources instead of sharing a single service-account key.

## What this gives you

IAM Federation stops Hoop from sharing one Google service-account key across your team. Each user's GCP activity runs under their own principal, so a `bq query` Alice runs through Hoop shows up in Cloud Logging as Alice.

You get:

* **Per-user attribution.** Every GCP operation ties to the person who ran it.
* **No shared keys.** Users never hold a long-lived service-account key. Hoop mints short-lived tokens at session time.
* **A clean audit trail.** GCP records who ran each query, and for `gcp_iam`, through whom.

## Two ways to federate

Hoop ships two built-in GCP providers. Both deliver per-user attribution with no shared static key. They differ in what you provision on the GCP side and whose identity lands in the audit log.

|                                  | `gcp_iam`                                                                  | `gcp_oauth`                                                                                        |
| -------------------------------- | -------------------------------------------------------------------------- | -------------------------------------------------------------------------------------------------- |
| **Identity in GCP audit log**    | A per-user service account (e.g. `alice@proj.iam.gserviceaccount.com`)     | The user's real Google account (e.g. `alice@acme.com`)                                             |
| **What you provision**           | One admin SA plus one SA per user (or a shared SA)                         | One OAuth client (app registration), zero service accounts                                         |
| **Admin credential Hoop stores** | Admin SA JSON key                                                          | OAuth `client_id` / `client_secret`                                                                |
| **How a token is minted**        | Admin SA impersonates the user SA via `iamcredentials.GenerateAccessToken` | Each user consents once; Hoop exchanges their refresh token for an access token                    |
| **Per-user setup**               | Admin grants `serviceAccountTokenCreator` plus data roles to each SA       | Each user clicks **Connect Google account** once; data roles go on their real Google identity      |
| **Web UI support**               | Configurable in the connection form                                        | Configurable in the connection form; each user clicks **Connect Google account** in the web client |

<Tip>
  Choose `gcp_iam` for Hoop-managed service-account identities and a delegation
  chain that proves every query went through Hoop. Choose `gcp_oauth` to show the
  real human email in the GCP audit log and provision no service accounts.
</Tip>

## How It Works

A user runs `hoop connect my-bq`. The gateway reads the connection's federation policy, mints a short-lived token for that user, and hands it to the agent for the session. The minting step depends on the provider:

<Tabs>
  <Tab title="gcp_iam (service-account impersonation)">
    <Steps>
      <Step title="Reads the federation policy">
        The policy defines the identity template, project id, and fallback rules that map the Hoop user to a GCP service account.
      </Step>

      <Step title="Mints a token as the user's principal">
        The gateway asks Google's `iamcredentials` API to mint a short-lived OAuth access token as the user's target service account, using the admin service account stored for your organization.
      </Step>

      <Step title="Hands the token to the agent">
        The agent uses that short-lived token for the session. Every operation after that runs as the user's service account, not the shared admin account.
      </Step>
    </Steps>

    Two things must be true on the GCP side:

    * The admin service account must be able to impersonate each user's target principal (the `roles/iam.serviceAccountTokenCreator` grant).
    * Each target principal holds the data-plane roles it needs (e.g. `roles/bigquery.user` for BigQuery).
  </Tab>

  <Tab title="gcp_oauth (per-user OAuth)">
    <Steps>
      <Step title="Each user consents once">
        The user opens the connection's consent URL and approves Google's OAuth screen with their own corporate Google account. Hoop stores their refresh token encrypted at rest, keyed to that user and connection.
      </Step>

      <Step title="Mints a token as the user">
        At session open, the gateway exchanges that refresh token for a fresh, short-lived user access token. No service account is involved.
      </Step>

      <Step title="Hands the token to the agent">
        The agent uses the token for the session. Every operation runs as the user's real Google identity.
      </Step>
    </Steps>

    On the GCP side:

    * You register one OAuth client (Web application). No service accounts.
    * Each user's Google account holds the data-plane roles it needs (e.g. `roles/bigquery.user`).
  </Tab>
</Tabs>

## Configure per-user OAuth (`gcp_oauth`)

This walkthrough sets up the no-service-account mode. For the `gcp_iam` setup
(admin SA, Token Creator grants, and the connection form), see the
[configuration guide](/setup/configuration/iam-federation-gcp-configuration).

<Note>
  `gcp_oauth` works in the web UI. An admin configures it on the connection form
  (Step 3), and each user connects their own Google account from the web client
  (Step 4). Each step also shows the `hoop admin federation` CLI flow for
  automation.
</Note>

### Step 1: Create an OAuth client in GCP (no service accounts)

In the [GCP Console](https://console.cloud.google.com/) for the project that
holds your BigQuery data:

<Steps>
  <Step title="Open Credentials → Create credentials">
    Go to **APIs & Services → Credentials** and click **+ Create credentials**.

    <Frame>
      <img src="https://mintcdn.com/hoopdev/GVVkHvor8qTxgGh6/images/configure/iam-federation/gcp-oauth/01-credentials-create.png?fit=max&auto=format&n=GVVkHvor8qTxgGh6&q=85&s=f618cc97b57dacb23303b92a3e0b540f" alt="GCP APIs & Services Credentials page with the Create credentials button highlighted" width="1011" height="165" data-path="images/configure/iam-federation/gcp-oauth/01-credentials-create.png" />
    </Frame>
  </Step>

  <Step title="Choose OAuth client ID">
    From the dropdown, pick **OAuth client ID** (not API key). It requests user
    consent so Hoop can act as each user.

    <Frame>
      <img src="https://mintcdn.com/hoopdev/GVVkHvor8qTxgGh6/images/configure/iam-federation/gcp-oauth/02-create-oauth-client-id.png?fit=max&auto=format&n=GVVkHvor8qTxgGh6&q=85&s=3882d2315635486c42a0c1e3862250c7" alt="Create credentials dropdown showing API key and OAuth client ID options, with OAuth client ID selected" width="1011" height="165" data-path="images/configure/iam-federation/gcp-oauth/02-create-oauth-client-id.png" />
    </Frame>
  </Step>

  <Step title="Configure the client and add the redirect URI">
    Set **Application type** to **Web application**, give it a name (e.g.
    `OAuth Bq federation connection`), and under **Authorized redirect URIs**
    add your gateway's API URL plus `/api/federation/oauth/callback`:

    ```
    https://GATEWAYURI.com/api/federation/oauth/callback
    ```

    Replace `GATEWAYURI.com` with your gateway host (local dev:
    `http://localhost:8009/api/federation/oauth/callback`). The URI must match
    exactly. Click **Create**.

    <Frame>
      <img src="https://mintcdn.com/hoopdev/GVVkHvor8qTxgGh6/images/configure/iam-federation/gcp-oauth/03-oauth-client-form.png?fit=max&auto=format&n=GVVkHvor8qTxgGh6&q=85&s=b17e2555d63253390cb1b41459846d5b" alt="Create OAuth client ID form with Web application type, a name, and the authorized redirect URI pointing at /api/federation/oauth/callback" width="864" height="879" data-path="images/configure/iam-federation/gcp-oauth/03-oauth-client-form.png" />
    </Frame>
  </Step>

  <Step title="Configure the consent screen scopes">
    On the **OAuth consent screen**, add the scope
    `https://www.googleapis.com/auth/cloud-platform` (or a narrower data scope,
    e.g. BigQuery). Add your users as test users, or publish the app.
  </Step>

  <Step title="Copy the Client ID and Client secret (or download the JSON)">
    After you create the client, Google shows the **Client ID** and **Client
    secret**. Copy them, or click **Download JSON**.

    <Frame>
      <img src="https://mintcdn.com/hoopdev/GVVkHvor8qTxgGh6/images/configure/iam-federation/gcp-oauth/04-oauth-client-created.png?fit=max&auto=format&n=GVVkHvor8qTxgGh6&q=85&s=eaecaf396961f33b4bcb19c75c624600" alt="OAuth client created dialog showing the Client ID, Client secret, and a Download JSON button" width="1024" height="732" data-path="images/configure/iam-federation/gcp-oauth/04-oauth-client-created.png" />
    </Frame>
  </Step>
</Steps>

The downloaded file wraps everything under a `"web"` key with extra fields Hoop
doesn't use. Hoop needs only `client_id` and `client_secret`. Pull those two
values into a minimal JSON object:

```json theme={"dark"}
{
  "client_id": "968792117476-0nom9m8e8...apps.googleusercontent.com",
  "client_secret": "GOCSPX-kdf3IWCfH-..."
}
```

You'll paste this in Step 3 (or save it as `oauth-client.json` for the CLI flow).

<Warning>
  In **Testing** mode, Google expires refresh tokens after 7 days, so federated
  sessions break weekly until each user re-consents. Publish the app to remove
  that cap.
</Warning>

### Step 2: Grant data roles to each user's Google account

There are no service accounts to grant. Give each user's Google identity the
data-plane roles it needs. For BigQuery:

```bash theme={"dark"}
export PROJECT_ID="my-proj"

gcloud projects add-iam-policy-binding "$PROJECT_ID" \
  --member="user:alice@acme.com" \
  --role="roles/bigquery.user"

gcloud projects add-iam-policy-binding "$PROJECT_ID" \
  --member="user:alice@acme.com" \
  --role="roles/bigquery.dataViewer"
```

### Step 3: Wire federation onto the connection (admin, one-time)

<Tabs>
  <Tab title="Web UI">
    Open the BigQuery connection (or create one), and under **Role connection
    method** choose **IAM Federation**. Then in **Federation setup**:

    <Steps>
      <Step title="Select the GCP OAuth provider">
        Set **Federation provider** to **GCP OAuth (per-user Google account)**.

        <Frame>
          <img src="https://mintcdn.com/hoopdev/GVVkHvor8qTxgGh6/images/configure/iam-federation/gcp-oauth/05-hoop-select-gcp-oauth.png?fit=max&auto=format&n=GVVkHvor8qTxgGh6&q=85&s=8065c3f40a0dd294186dcf26fb26d70e" alt="Hoop connection form with IAM Federation selected and the Federation provider dropdown open, showing GCP OAuth (per-user Google account)" width="728" height="1024" data-path="images/configure/iam-federation/gcp-oauth/05-hoop-select-gcp-oauth.png" />
        </Frame>
      </Step>

      <Step title="Paste the OAuth client credentials">
        In **OAuth client credentials (JSON)**, paste the minimal
        `client_id` / `client_secret` JSON from Step 1:

        ```json theme={"dark"}
        {
          "client_id": "968792117476-0nom9m8e8...apps.googleusercontent.com",
          "client_secret": "GOCSPX-kdf3IWCfH-..."
        }
        ```

        The gateway uses the client secret only to exchange tokens. It never
        reaches end-user sessions.
      </Step>

      <Step title="Set the project and save">
        Enter your **GCP Project ID** (e.g. `my-proj`) and **Save** the
        connection. Selecting `gcp_oauth` forces the fallback method to **Deny
        the session**, so a session won't run without a per-user token.

        <Frame>
          <img src="https://mintcdn.com/hoopdev/CAe0Y3Y0hx6FBXdH/images/configure/iam-federation/gcp-oauth/06-hoop-gcp-oauth-setup.png?fit=max&auto=format&n=CAe0Y3Y0hx6FBXdH&q=85&s=9911cd60e9985cfd594a8869fb3bacda" alt="Hoop connection form Federation setup panel after selecting GCP OAuth, showing the OAuth client credentials (JSON) field with the client_id and client_secret pasted, the GCP Project ID field, a Connect your Google account callout, and an Output preview listing HOOP_GCP_ACCESS_TOKEN, CLOUDSDK_CORE_PROJECT, and HOOP_FEDERATED_PRINCIPAL" width="561" height="1024" data-path="images/configure/iam-federation/gcp-oauth/06-hoop-gcp-oauth-setup.png" />
        </Frame>

        The **Connect your Google account** callout reminds you to save first;
        every user (including you, as the admin) connects their own account from
        this same screen before running a session — that's Step 4.
      </Step>
    </Steps>
  </Tab>

  <Tab title="CLI">
    Write a policy file selecting the `gcp_oauth` provider:

    ```yaml theme={"dark"}
    # federation-oauth.yaml
    builtin_provider: gcp_oauth
    fallback_policy: deny          # or "static" to fall back to the connection's existing envs
    token_ttl_seconds: 3600        # informational; Google controls the real token lifetime
    extra_config:
      project_id: my-proj
      # optional, defaults to cloud-platform:
      # scopes:
      #   - https://www.googleapis.com/auth/bigquery
    ```

    Apply it, passing the OAuth client JSON from Step 1 as the admin credential.
    It replaces the service-account key that `gcp_iam` uses:

    ```bash theme={"dark"}
    hoop admin federation set my-bq \
      --file federation-oauth.yaml \
      --credentials-file ./oauth-client.json

    hoop admin federation get my-bq   # verify: Provider: gcp_oauth, Has Admin Credentials: yes
    ```
  </Tab>
</Tabs>

### Step 4: Each user connects their Google account (one-time)

Each user grants Hoop a refresh token once by approving the Google consent
screen with the Google account that should be attributed.

<Tabs>
  <Tab title="Web client">
    Open the federated connection in the web client. If you haven't connected
    yet, a **Connect your Google account** prompt appears in the editor. Run a
    query first and the run is blocked with the same **Connect Google account**
    action inline ("This resource runs queries as you. Connect your Google
    account once to authorize access, then run again.").

    <Frame>
      <img src="https://mintcdn.com/hoopdev/CAe0Y3Y0hx6FBXdH/images/configure/iam-federation/gcp-oauth/07-web-terminal-connect-google.png?fit=max&auto=format&n=CAe0Y3Y0hx6FBXdH&q=85&s=18d4d93fd5e0a2b9ce55c3dcb2369f46" alt="Hoop web terminal running a bq query against a gcp_oauth connection, with a Connect your Google account callout explaining the resource runs queries as you and a Connect Google account button" width="1024" height="761" data-path="images/configure/iam-federation/gcp-oauth/07-web-terminal-connect-google.png" />
    </Frame>

    Click it to reach Google's consent screen. **Approve with the same Google
    account you use to sign in to Hoop** — Hoop attributes the session to the
    consented identity, so it must match your Hoop user. After you approve, Hoop
    returns you to the same page, shows a "Google account connected"
    confirmation, and clears the prompt; rerun the query and it executes as you.

    Admins can also connect or disconnect from the connection's **Federation
    setup** section in the connection form.
  </Tab>

  <Tab title="CLI / API">
    Ask Hoop for the consent URL (it uses the user's own Hoop token, not an admin
    token), then open it in a browser:

    ```bash theme={"dark"}
    # The user's Hoop access token (from `hoop login`)
    TOKEN="<hoop_access_token>"

    curl -s -H "Authorization: Bearer $TOKEN" \
      "https://hoop.your-company.com/api/connections/my-bq/federation/oauth/authorize"
    # → {"url":"https://accounts.google.com/o/oauth2/auth?...&state=..."}
    ```

    Open the returned `url` and approve consent. Google redirects back to the
    gateway, which stores the encrypted refresh token and returns the browser to
    the app with `?federation_oauth=success`.
  </Tab>
</Tabs>

<Warning>
  Approve the consent screen with the same identity you use in Hoop. Hoop records
  the consented Google account as the session's principal, so it appears in every
  BigQuery audit entry. Connecting a different Google account misattributes that
  user's activity.
</Warning>

To revoke (disconnect the Google account for a connection), use the
**Disconnect** action in the web client, or the API:

```bash theme={"dark"}
curl -s -X DELETE -H "Authorization: Bearer $TOKEN" \
  "https://hoop.your-company.com/api/connections/my-bq/federation/oauth"
```

Once connected, `hoop connect my-bq` mints a fresh user token per session and
injects `HOOP_GCP_ACCESS_TOKEN`, `CLOUDSDK_CORE_PROJECT`, and
`HOOP_FEDERATED_PRINCIPAL` (and strips any legacy
`GOOGLE_APPLICATION_CREDENTIALS`).

<Note>
  **Test as user** doesn't apply to `gcp_oauth`, and the connection form hides it
  for this provider. A dry run can't consent on the user's behalf, so there's
  nothing to validate ahead of time. Validate by running a real session after
  Step 4.
</Note>

## Project scope and cross-project queries

The Project ID you set on the connection (`extra_config.project_id`, injected
into the session as `CLOUDSDK_CORE_PROJECT`) is not a data boundary. It sets the
default billing and job project: where BigQuery runs and bills each query, and
what unqualified table names resolve against.

It does not limit which data a user can read. The minted token authenticates as
the user (with the `https://www.googleapis.com/auth/cloud-platform` scope by
default), so a session can query any project the user already has IAM access to.
The user's own grants govern that, not the connection's project.

Two things follow:

* **One OAuth client covers every connection.** For `gcp_oauth`, the project
  where you registered the OAuth client is just the app's home and has no bearing
  on data access. Reuse the same `client_id` / `client_secret` across your
  BigQuery connections.
* **One connection reads across projects.** You don't need a connection per data
  project. Only the billing project is pinned. Add a separate connection when you
  want jobs billed to a different project.

### Querying a dataset in another project

Qualify the table with the other project's ID: `` `project.dataset.table` ``
(BigQuery Standard SQL; the connection runs with `--use_legacy_sql=false`). The
job still bills to the connection's `project_id`. The reference points wherever
the data lives.

The connection below is pinned to `analytics-prod`; the data lives in
`raw-data-project`:

```sql theme={"dark"}
SELECT *
FROM `raw-data-project.events.logs`
LIMIT 100;
```

Cross-project join (billed to `analytics-prod`, reading from two other projects):

```sql theme={"dark"}
SELECT u.id, e.event_name
FROM `raw-data-project.events.logs` AS e
JOIN `customer-project.crm.users` AS u
  ON u.id = e.user_id
WHERE e.event_date = CURRENT_DATE();
```

The user's identity (their Google account for `gcp_oauth`, or their target
service account for `gcp_iam`) needs:

* `roles/bigquery.jobUser` on the billing project (`analytics-prod`) to create
  the job, and
* `roles/bigquery.dataViewer` (or finer) on the data datasets in
  `raw-data-project` / `customer-project` to read them.

<Tip>
  Unqualified names like `` `events.logs` `` resolve against the connection's
  `project_id`. Qualify with a project prefix when the data lives elsewhere.
  Backticks are required because project IDs contain hyphens.
</Tip>

## The Audit Trail

Both providers attribute every operation to the individual. The shape of the
audit record differs.

`gcp_iam` records a `serviceAccountDelegationInfo` chain showing who ran the
query (the user's service account) and through whom (the Hoop admin SA):

```yaml theme={"dark"}
protoPayload.authenticationInfo.principalEmail: alice@my-proj.iam.gserviceaccount.com
protoPayload.authenticationInfo.serviceAccountDelegationInfo:
  - firstPartyPrincipal:
      principalEmail: hoop-admin@my-proj.iam.gserviceaccount.com
```

That chain proves every query in a project went through Hoop and flags any that
bypassed it. The configuration guide covers enabling, querying, and sinking these
logs.

`gcp_oauth` records the user's Google account as the principal, with no
delegation chain. The human is the caller:

```yaml theme={"dark"}
protoPayload.authenticationInfo.principalEmail: alice@acme.com
```

***

<Note>
  For `gcp_iam`, the [IAM Federation for GCP configuration guide](/setup/configuration/iam-federation-gcp-configuration) covers the GCP IAM setup (admin service account, Token Creator grants, verification), wiring the policy onto a connection, and enabling the audit logs. For `gcp_oauth`, follow [Configure per-user OAuth](#configure-per-user-oauth-gcp-oauth) above.
</Note>

## Next Steps

<CardGroup cols={2}>
  <Card title="Configuration Guide" icon="gear" href="/setup/configuration/iam-federation-gcp-configuration">
    Grant Token Creator and wire federation onto a connection
  </Card>

  <Card title="CLI Reference" icon="terminal" href="/clients/cli-federation">
    `hoop admin federation set` and `test` flag reference
  </Card>

  <Card title="Connect to BigQuery" icon="google" href="/quickstart/databases/bigquery">
    Create the connection federation attaches to
  </Card>

  <Card title="Session Recording" icon="film" href="/learn/features/session-recording">
    Audit every federated session inside Hoop
  </Card>
</CardGroup>
