# Configure Cloudflare Zero Trust

## Goal

Require a valid Cloudflare Access JWT on every pool call before a concurrency
slot is taken. Access decides who may authenticate. nthbouncer verifies the
application token at the edge, then applies pool keys, HMAC, and the concurrency limit as
usual.

Available on the **Team** plan and above. You can use Cloudflare Access **or**
generic JWT validation on a pool, not both.

Reference for headers and check order:
[Access → Cloudflare Access](/docs/pools/access#cloudflare-access-plan-gated).

## Create an Access application

1. In the [Cloudflare Zero Trust dashboard](https://one.dash.cloudflare.com/),
   open **Access controls → Applications**.
2. Add a **self-hosted** application for the hostname callers will hit
   (your [custom domain](/docs/domains), or another Access-protected app whose
   JWT you will forward).
3. Attach Access policies for people and services that should reach the pool
   (IdP groups, emails, or **Service Auth** with
   [service tokens](https://developers.cloudflare.com/cloudflare-one/access-controls/service-credentials/service-tokens/)).
4. Save the application.

## Copy team domain and AUD

1. Note your team name. The team domain is
   `https://‹team›.cloudflareaccess.com`.
2. Open the application → **Overview** (or **Additional settings**) and copy
   the **Application Audience (AUD)** tag.

Cloudflare's guide:
[Find your AUD tag](https://developers.cloudflare.com/cloudflare-one/access-controls/applications/http-apps/authorization-cookie/validating-json/#get-your-aud-tag).

## Configure the pool

1. Open **Pools → Access**.
2. Enable **Identity-aware access**.
3. Set **Provider** to **Cloudflare Access**.
4. Enter the team name (or full `https://‹team›.cloudflareaccess.com` URL) and
   the AUD tag.
5. Save. Changes usually take effect within a few seconds.

Keep a pool key (and optional HMAC / IP policy) as you already do. Access is an
extra identity check, not a replacement for keys.

## Call the pool

Send the Access application JWT plus your pool credentials:

```bash
curl -sS "https://‹pool›.nthpool.cloud/v1/resource" \
  -H "X-Nthpool-Key: $POOL_KEY" \
  -H "Cf-Access-Jwt-Assertion: $ACCESS_JWT"
```

`X-Nthpool-Jwt` is accepted when a trusted hop forwards the same application
JWT. Prefer `Cf-Access-Jwt-Assertion` when you control the client.

### Where the JWT comes from

- **Browser or Access-proxied hostname.** After Access authenticates the user,
  Cloudflare attaches `Cf-Access-Jwt-Assertion` on the request toward the
  origin. If the pool's custom domain is that Access application, callers get
  the header automatically once they pass Access.
- **Service tokens.** Authenticate to the Access application with
  `CF-Access-Client-Id` / `CF-Access-Client-Secret` (Service Auth policy). Use
  the resulting application JWT on pool calls the same way.
- **Forwarding from another Access app.** A service that already holds a JWT
  for this application's AUD can send it as `Cf-Access-Jwt-Assertion` or
  `X-Nthpool-Jwt`. The `aud` claim must match the AUD you configured on the
  pool.

nthbouncer verifies RS256 against
`https://‹team›.cloudflareaccess.com/cdn-cgi/access/certs`, and checks issuer,
audience, and expiry. The Access assertion is stripped before the request is
sent upstream.

## Troubleshooting

| Symptom | Likely cause |
|---|---|
| `401` missing Cloudflare Access JWT | No `Cf-Access-Jwt-Assertion` or `X-Nthpool-Jwt` |
| `401` audience mismatch | AUD on the pool does not match the Access application that issued the token |
| `401` JWT rejected (issuer / signature) | Wrong team domain, expired token, or token from a different Zero Trust team |
| Cannot enable Identity-aware access | Plan below Team, or generic JWT already configured on the pool |

Auth failures never take a concurrency slot. See
[Understand admission errors](/docs/knowledge-base/admission-errors).

## See also

- [Access](/docs/pools/access)
- [Domains](/docs/domains)
- [Billing](/docs/billing)
