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

# Google Cloud

> Call Reducto from Google Cloud with metadata server identity tokens or GKE service account tokens instead of a stored API key.

Google Cloud workloads can use either of two token sources:

* **Metadata server identity token**: Compute Engine, Cloud Run, Cloud Functions, App Engine and GKE pods with an attached Google service account request a Google-signed ID token.
* **Google Kubernetes Engine (GKE)**: a pod uses a projected Kubernetes service account token issued by the cluster.

## Metadata server identity tokens

### Set up the service account

Create a Google service account for the workload and attach it to the VM, Cloud Run service or function. Do not create a service account key.

```bash theme={null}
gcloud iam service-accounts create reducto-client \
  --display-name="Reducto client"
```

### Get and inspect a token

From the resource, request an ID token with the Reducto audience:

```bash theme={null}
SUBJECT_TOKEN=$(curl -sS -G -H "Metadata-Flavor: Google" \
  "http://metadata.google.internal/computeMetadata/v1/instance/service-accounts/default/identity" \
  --data-urlencode "audience=https://platform.reducto.ai" \
  --data-urlencode "format=full")
cut -d. -f2 <<< "$SUBJECT_TOKEN" | tr '_-' '/+' | base64 -d 2>/dev/null | jq .
```

```json theme={null}
{
  "iss": "https://accounts.google.com",
  "aud": "https://platform.reducto.ai",
  "azp": "104512345678901234567",
  "sub": "104512345678901234567",
  "email": "reducto-client@acme-payments.iam.gserviceaccount.com",
  "email_verified": true,
  "iat": 1793322245,
  "exp": 1793325845
}
```

`sub` is the service account's unique numeric ID. If you delete a service account and create one with the same email, the new one gets a new ID. Find it with `gcloud iam service-accounts describe reducto-client@acme-payments.iam.gserviceaccount.com --format='value(uniqueId)'`.

### Configure Reducto

In [Studio](https://studio.reducto.ai), open **Settings > Workload identity** and create a provider with the **Google Cloud** preset:

| Field | Value |
| - | - |
| **Name** | `gcp-prod` |
| **Issuer** | `https://accounts.google.com` |
| **Audience** | `https://platform.reducto.ai` |
| **Key source** | OIDC discovery |

Add a mapping for the service account:

| Key | Value |
| - | - |
| `sub` | `104512345678901234567` |

Always match `sub`. Every Google account can get a token with issuer `https://accounts.google.com` and your audience, so a mapping without `sub` is not safe. The `email` claim is present only when the token is requested with `format=full`, so match on it only if your token source requests that format.

### Call Reducto

```python Python theme={null}
import os

from reducto import Reducto
from reducto.auth import gcp_id_token_provider

client = Reducto(
    workload_identity={
        "identity_provider_id": os.environ["REDUCTO_IDENTITY_PROVIDER_ID"],
        "provider": gcp_id_token_provider(),
    }
)

result = client.parse.run(input="https://cdn.reducto.ai/samples/fidelity-example.pdf")
print(result.result.chunks[0].content[:500])
```

```javascript Node.js theme={null}
import Reducto from "reductoai";
import { gcpIdTokenProvider } from "reductoai/auth";

const client = new Reducto({
  workloadIdentity: {
    identityProviderId: process.env.REDUCTO_IDENTITY_PROVIDER_ID,
    provider: gcpIdTokenProvider(),
  },
});

const result = await client.parse.run({
  input: "https://cdn.reducto.ai/samples/fidelity-example.pdf",
});
console.log(result.result.chunks[0].content.slice(0, 500));
```

```bash cURL theme={null}
SUBJECT_TOKEN=$(curl -sS -G -H "Metadata-Flavor: Google" \
  "http://metadata.google.internal/computeMetadata/v1/instance/service-accounts/default/identity" \
  --data-urlencode "audience=https://platform.reducto.ai" \
  --data-urlencode "format=full")

ACCESS_TOKEN=$(jq -n \
  --arg subject_token "$SUBJECT_TOKEN" \
  --arg provider_id "$REDUCTO_IDENTITY_PROVIDER_ID" \
  '{
    grant_type: "urn:ietf:params:oauth:grant-type:token-exchange",
    subject_token_type: "urn:ietf:params:oauth:token-type:id_token",
    subject_token: $subject_token,
    identity_provider_id: $provider_id
  }' \
  | curl -sS --fail-with-body -X POST "https://platform.reducto.ai/oauth/token" \
      -H "Content-Type: application/json" -d @- \
  | jq -r .access_token)

curl -X POST "https://platform.reducto.ai/parse" \
  -H "Authorization: Bearer $ACCESS_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"input":"https://cdn.reducto.ai/samples/fidelity-example.pdf"}'
```

`gcp_id_token_provider(audience=...)` in Python and `gcpIdTokenProvider({ audience })` in Node.js set the audience. It defaults to `https://platform.reducto.ai`.

## Google Kubernetes Engine (GKE)

GKE pods can use either token source:

* If the pod uses [GKE Workload Identity Federation](https://cloud.google.com/kubernetes-engine/docs/concepts/workload-identity) with a Google service account, the metadata server returns Google ID tokens. Follow the [metadata server](#metadata-server-identity-tokens) steps.
* Otherwise, use a projected Kubernetes service account token, as below.

Get the cluster's issuer:

```bash theme={null}
kubectl get --raw /.well-known/openid-configuration | jq -r .issuer
```

```text theme={null}
https://container.googleapis.com/v1/projects/acme-payments/locations/us-central1/clusters/payments-prod
```

Create a service account and mount a projected token with the Reducto audience, as in the [Kubernetes guide](https://docs.reducto.ai/security/workload-identity-federation/kubernetes.md#set-up-the-cluster). GKE tokens use the standard Kubernetes claims:

```json theme={null}
{
  "iss": "https://container.googleapis.com/v1/projects/acme-payments/locations/us-central1/clusters/payments-prod",
  "aud": ["https://platform.reducto.ai"],
  "sub": "system:serviceaccount:payments:reducto-client",
  "iat": 1793322245,
  "exp": 1793325845,
  "kubernetes.io": {
    "namespace": "payments",
    "serviceaccount": {
      "name": "reducto-client",
      "uid": "2d3e4f5a-6b7c-4d8e-9f0a-1b2c3d4e5f6a"
    }
  }
}
```

Create a provider with the **GKE** preset:

| Field | Value |
| - | - |
| **Name** | `gke-payments-prod` |
| **Issuer** | `https://container.googleapis.com/v1/projects/acme-payments/locations/us-central1/clusters/payments-prod` |
| **Audience** | `https://platform.reducto.ai` |
| **Key source** | OIDC discovery |

Add a mapping with `sub` = `system:serviceaccount:payments:reducto-client`. Then use `k8s_service_account_token_provider("/var/run/secrets/reducto/token")` (Python) or `k8sServiceAccountTokenProvider("/var/run/secrets/reducto/token")` (Node.js), as shown in the [Kubernetes guide](https://docs.reducto.ai/security/workload-identity-federation/kubernetes.md#call-reducto-from-the-pod).

## Google Cloud best practices

* Use one Google service account per workload, and match its `sub`.
* Never download service account keys for this flow. The metadata server needs none.
* Keep production and non-production workloads in separate projects with separate service accounts and mappings.
* Review mappings when you delete or recreate service accounts. A recreated account has a new `sub`.
