# Token-Mint Integrations


Some vendors don't take an API key on every request — they want an OAuth-style
**token dance**: present a durable credential at a token endpoint, get back a
short-lived access token, use that. The token-mint integrations run that dance
for you: exe.dev vaults the durable credential (a client secret, a service
account key, a refresh token) and your VM exchanges it for the short-lived
token without ever seeing it.

The catalog services with this shape today: `googlesa`, `keycloak`, `twitch`,
and `reddit-ads`. (Container registries use a GET-based cousin of this flow —
see [Container Registry Integrations](integrations-oci-registries).)

## How it works

1. `POST` to the vendor's documented token path on the integration hostname.
   Any body you send is ignored — the proxy discards it and synthesizes the
   real token request from the vaulted credential:

   ```
   curl -X POST http://<name>.int.exe.xyz/oauth2/token
   ```

2. The vendor's response comes back to you verbatim: a short-lived
   `access_token` (typically ~1 hour) with `expires_in`.

3. Use it as `Authorization: Bearer <token>` — depending on the service,
   either through the integration or directly against the vendor (see each
   section below).

4. When the token expires (the vendor answers 401), mint a fresh one. React
   to the 401 rather than tracking TTLs.

The durable credential never reaches the VM; only the scoped, expiring token
does. If a vendor echoes a durable secret back in the token response (Reddit
does), the proxy strips that field.

## Google service account (`googlesa`)

- Credential: a service-account JSON key
  ([create one](https://console.cloud.google.com/iam-admin/serviceaccounts)).
- Mint: `POST /token` with an empty body. The proxy signs the JWT assertion
  with the private key server-side and exchanges it at Google's token
  endpoint.
- Use the ~1h access token **directly against the Google API** (e.g.
  `Authorization: Bearer <token>` to `sheets.googleapis.com`) — those calls
  do not go through the integration.
- Consumer-style use: share the target resource (e.g. a Sheet) with the
  service account's `client_email`.
- Domain-wide delegation: pass `--subject` with the user to impersonate.

## Keycloak (`keycloak`)

- Credential: a confidential client's id + secret.
- Self-hosted: point `--base-url` at your Keycloak install.
- Mint: `POST /realms/<realm>/protocol/openid-connect/token` — the
  client-credentials grant. The integration is path-gated to `/realms/`, so
  the secret can only ever be presented to a token endpoint.
- Use the bearer token directly against your own API.
- Verify runs a client-credentials mint against `--realm` (default
  `master`); the id/secret ride HTTP Basic, so the body carries only
  `grant_type`.

## Twitch (`twitch`)

- Credential: an application's client_id + client_secret
  ([register an app](https://dev.twitch.tv/console/apps), then "New Secret"
  on its Manage page).
- Mint: `POST /oauth2/token` — Twitch's client-credentials grant. Twitch
  only accepts the credentials as form-body fields (HTTP Basic is rejected
  with `missing client id`), which is exactly why the body is synthesized
  server-side.
- Use the app access token on `/helix/...` requests **through the
  integration** — the proxy adds the required `Client-Id` header for you.
- App access tokens are server-to-server and carry no scopes. Endpoints that
  need a user context (e.g. a user's email) require a *user* access token,
  which this integration does not mint.

## Reddit Ads (`reddit-ads`)

- Credential: your app's client credentials plus a **refresh token**.
- Getting the refresh token: authorize your app once at
  `https://www.reddit.com/api/v1/authorize?client_id=...&response_type=code&state=...&redirect_uri=...&duration=permanent&scope=adsread`
  (add `adsedit` / `adsconversions` as needed), then exchange the code at
  Reddit's token endpoint; the response's `refresh_token` is what you paste
  into the integration.
- Scopes: `adsread` covers reporting and read endpoints; campaign writes
  need `adsedit`; conversion uploads need `adsconversions`.
- Mint: `POST /api/v1/access_token` (any body ignored). The proxy performs
  the refresh-token grant server-side and returns the ~1h `access_token`.
  Reddit echoes the permanent refresh token back in refresh responses — the
  proxy strips that field.
- Use the token on `/api/v3/...` requests, through the integration or
  directly against `ads-api.reddit.com`.
- Reddit requires a descriptive User-Agent; the proxy sets one on proxied
  requests.
