# JWT Identity Providers

## Summary

**Identity Providers** (`identity_providers`) declares *additional* identity providers as a map keyed by a name you choose. Today they are usable only to verify JWT bearer tokens issued by non-interactive workloads — Kubernetes service account tokens, GitHub Actions OIDC, SPIFFE JWT-SVIDs, and so on — on routes that use the `jwt` [bearer token format](https://www.pomerium.com/docs/reference/bearer-token-format.md). They do **not** replace the interactive single-sign-on provider you configure with the flat `idp_*` options; see [Identity Provider Settings](https://www.pomerium.com/docs/reference/identity-provider-settings.md).

For each incoming bearer token, Pomerium reads the token's `iss` claim, selects the matching provider, fetches that provider's public keys, and verifies the token's signature, `iss`, `exp`, `nbf`, and `aud`. A token is rejected if its issuer is not declared here, or if its `aud` is not one of the matched provider's `audiences`. Authorization on the verified claims is left to your [policy](https://www.pomerium.com/docs/internals/ppl.md) (`claim/…`).

A route using the `jwt` format requires at least one entry here, and each entry requires a non-empty `audiences`. A route may narrow which of these providers it accepts with the per-route [`identity_providers`](https://www.pomerium.com/docs/reference/routes/identity-providers-per-route.md) allowlist.

See [Machine-to-Machine Access with Bearer Tokens](https://www.pomerium.com/docs/capabilities/bearer-token-access.md) for an overview and examples.

Each provider is a map entry. The key is the provider name; the value has the following fields:

| Field | Type | Usage | Description |
| :-- | :-- | :-- | :-- |
| *(map key)* | `string` | **required** | The provider name. Must be non-empty and contain no `/`. Labels the provider in audit logs and namespaces the caller's user ID as `<provider-name>/<sub>`. Referenced by the per-route allowlist. |
| `issuer` | `string` | **required** | The `iss` claim tokens must carry, unique across providers. Used both to select the matching provider for an incoming token and, by default, to discover its signing keys via OIDC (`<issuer>/.well-known/openid-configuration`). |
| `jwks_url` | `string` | **optional** | An explicit JWKS endpoint. When set, OIDC discovery is skipped and keys are fetched directly from this URL. Useful when the issuer URL is not reachable from where Pomerium runs (for example, Kubernetes' `https://kubernetes.default.svc.cluster.local`). |
| `supported_algs` | array of `string` | **optional** | The JWT signing algorithms to accept. Defaults to `RS256`, `ES256`, and `EdDSA`. Only asymmetric algorithms are allowed (`RS`/`PS`/`ES` 256/384/512 and `EdDSA`); symmetric (`HS*`) and `none` are rejected. |
| `audiences` | array of `string` | **required** | The audiences accepted on this provider's tokens; a token's `aud` claim must include at least one. Must be non-empty — this is fail-closed, with no "accept any audience" mode. |

TLS for JWKS and discovery fetches uses the global [`certificate_authority` / `certificate_authority_file`](https://www.pomerium.com/docs/reference/certificates.md); there is no per-provider CA setting.

## How Pomerium identifies the caller

A verified token does not carry a Pomerium session; Pomerium derives one from the token so that policy, logging, and rate limiting have a stable identity to work with:

- The session's identity provider is the **provider name** (the `identity_providers` map key), and the caller's user ID is **`<provider-name>/<sub>`** — the provider name prefixes the token's `sub` claim so two providers can't collide on one identity. A token with no `sub` is rejected.
- The same token maps to the same session, so repeated requests are cheap. The session's lifetime is capped at the sooner of the token's `exp` and `cookie_expire`.
- Pomerium does not store the raw token. Because of this, `$pomerium.id_token` substitution and the `id-token` authorize-log field are empty for these workload sessions, and Pomerium does not observe revocation before the token expires — a leaked token is usable until its `exp` (or the session cap) is reached.

## How to Configure

**Core:**

| **Config file key**  | **Type**                |
| :------------------- | :---------------------- |
| `identity_providers` | Map of provider objects |

`identity_providers` is structured configuration; set it in the config file or the Console rather than as an environment variable.

### Examples

```yaml
identity_providers:
  kubernetes:
    issuer: https://kubernetes.default.svc.cluster.local
    jwks_url: https://kubernetes.default.svc.cluster.local/openid/v1/jwks
    supported_algs:
      - RS256
    audiences:
      - pomerium.example.com
  github-actions:
    issuer: https://token.actions.githubusercontent.com
    audiences:
      - pomerium.example.com
```

**Enterprise:**

**Kubernetes:**

```yaml
identityProviders:
  kubernetes:
    issuer: https://kubernetes.default.svc.cluster.local
    jwksUrl: https://kubernetes.default.svc.cluster.local/openid/v1/jwks
    supportedAlgs:
      - RS256
    audiences:
      - pomerium.example.com
```

See [Kubernetes - Global Configuration](https://www.pomerium.com/docs/deploy/k8s/configure.md) for more information.
