Changelog

View on GitHub →

Breaking: OAuth Token Credential Sources Restructured

The oauth_token transform now takes a discrete secret source per credential field instead of a single JSON blob. The previous blob shape was only compatible with Google's authorized_user file format; every other provider required hand-assembling credentials into that shape and storing them as a synthetic secret. token_endpoint is now required on all grants.

jwt_bearer has been removed from oauth_token. It consumed a Google service-account keyfile and duplicated the gcp_auth transform. gcp_auth gains a subject field for Workspace domain-wide delegation so no capability is lost. oauth_token is now a standard RFC 6749 implementation with no vendor coupling.

The oauth_token transform is marked experimental while the API stabilizes.

This is a breaking config change for existing oauth_token users. Update your config to use per-field secret sources and migrate any jwt_bearer grants to gcp_auth:

# Before
- name: oauth_token
  config:
    tokens:
      - grant: refresh_token
        credential:
          type: 1password_connect
          secret_ref: "op://Engineering/GSUITE-OAUTH/credential"
        token_endpoint: "https://oauth2.googleapis.com/token"
        scopes:
          - "https://www.googleapis.com/auth/gmail.readonly"
        rules:
          - host: "gmail.googleapis.com"
 
# After
- name: oauth_token
  config:
    tokens:
      - grant: refresh_token
        refresh_token:
          type: 1password_connect
          secret_ref: "op://Engineering/GSUITE-OAUTH/refresh-token"
        client_id:
          type: env
          var: GSUITE_OAUTH_CLIENT_ID
        client_secret:        # omit for public (PKCE) clients
          type: 1password_connect
          secret_ref: "op://Engineering/GSUITE-OAUTH/client-secret"
        token_endpoint: "https://oauth2.googleapis.com/token"
        scopes:
          - "https://www.googleapis.com/auth/gmail.readonly"
        rules:
          - host: "gmail.googleapis.com"

New: json_key Available on All Secret Sources

json_key extraction is now available on every secret source (previously limited to aws_sm and aws_ssm). It is applied centrally in resolveSource, so any JSON-structured secret can have individual fields extracted by key, regardless of which backend stores it. This pairs naturally with the new per-field credential sources:

# Pull client_id and client_secret out of one JSON secret in AWS Secrets Manager
- grant: client_credentials
  client_id:
    type: aws_sm
    secret_id: "arn:aws:secretsmanager:us-east-1:123:secret:oauth"
    json_key: "client_id"
  client_secret:
    type: aws_sm
    secret_id: "arn:aws:secretsmanager:us-east-1:123:secret:oauth"
    json_key: "client_secret"
  token_endpoint: "https://login.example.com/oauth2/token"
  rules:
    - host: "api.example.com"
View on GitHub →

New: OAuth2 Token Injection

The new oauth_token request transform mints short-lived OAuth2 access tokens and injects them as Authorization: Bearer headers on configured hosts. Three grant types are supported: refresh_token (RFC 6749), jwt_bearer (RFC 7523), and client_credentials (RFC 6749 §4.4). Token exchange, caching, refresh, and single-flight deduplication are all handled automatically.

Credentials resolve from any secrets-package source (env, 1Password, AWS Secrets Manager, etc.) and are re-read on their configured ttl, rebuilding the token source when they change. The configured token_endpoint is also stubbed so sandboxed client SDKs can complete their own OAuth2 handshake against the proxy using a placeholder token while the proxy injects the real one upstream. If token minting fails, the request is closed with a synthetic 502 rather than forwarding an unauthenticated request.

Migration: gcp_auth is superseded by oauth_token and will be removed in the next release. Migrate your gcp_auth config to an oauth_token transform with grant: jwt_bearer.

transforms:
  - type: oauth_token
    hosts: ["bigquery.googleapis.com"]
    grant: jwt_bearer
    credentials: { source: env, key: SERVICE_ACCOUNT_JSON }
    token_endpoint: "https://oauth2.googleapis.com/token"
View on GitHub →

New: GCP Auth Stubbing

When gcp_auth is active, the proxy now intercepts requests to GCP OAuth2 token endpoints (oauth2.googleapis.com/token and the GCE/GKE metadata server) and answers them with a synthetic stub token. Clients complete the OAuth2 handshake against the proxy without needing real credentials. The proxy mints the real access token from the keyfile it holds and injects it on the upstream API call. Stubbing is always-on whenever gcp_auth is configured, independent of the transform's host rules.

This adds a new ActionStub pipeline action. It short-circuits the request pipeline similarly to ActionReject, but renders as "stub" at INFO level in audit logs (with a stubbed_by field) so you can distinguish proxy-served responses from rejections.

View on GitHub →

New: Postgres Hot Reload

The /v1/reload management endpoint now rebuilds postgres listeners alongside pipeline and MCP config.

Validation runs for all three subsystems (pipeline, MCP, and postgres) before any state is mutated, so a 422 response leaves the proxy fully untouched. In-flight postgres sessions on closed listeners are not drained.

Postgres support is experimental.

View on GitHub →

New: Postgres Upstream DSN

Postgres support is experimental. The upstream connection is now configured as a single dsn block instead of six separate fields (host, port, sslmode, user_env, password_env, database). The DSN resolves through iron-proxy's shared secrets registry (the same sources available everywhere else: env, aws_sm, aws_ssm, 1password, 1password_connect) and is passed verbatim to pgconn.ParseConfig, so both URL and keyword/value forms are accepted.

The role field is now optional. When omitted, the proxy issues no SET ROLE and the upstream session runs as the connecting user.

Breaking change: the old six-field upstream config is no longer supported. Migrate to the dsn block before upgrading.

postgres:
  - name: primary
    listen: "127.0.0.1:5432"
    upstream:
      dsn:
        type: env
        var: PG_UPSTREAM_DSN
    client:
      user: app_user
      password_env: PG_PROXY_PASSWORD
    role: tenant_role  # optional