---
name: nopan-authentication
description: Apply this guide when working on authentication for the Nopan Payments API — provisioning mTLS client certificates, generating and registering request-signing key pairs, building RFC-9421 HTTP message signatures, requesting OAuth `client_credentials` access tokens, or validating signed responses from Nopan. Also relevant when diagnosing any 401 or 403 returned by Nopan.
---

# Nopan authentication

Nopan stacks three independent authentication layers. **All three are required on every request, including the token request itself.** Skipping any layer returns an error before the request reaches business logic.

| Layer | What it proves | Where it lives on the request |
|---|---|---|
| Mutual TLS (mTLS) | The client is a known organization with a valid X.509 certificate | TLS handshake (TLS 1.3 required) |
| OAuth 2.0 access token | The client is authorized for the requested scope | `Authorization: Bearer <token>` header |
| HTTP Message Signature (RFC 9421) | The request has not been tampered with in transit | `Signature` and `Signature-Input` headers |

Authoritative overview: [Authentication Overview](https://docs.nopan.com/guides/authentication/authentication-overview).

> **Match existing patterns** — if the project already integrates other payment providers, follow their conventions for secret storage (KMS, vault paths, env-var naming) and HTTP-client wiring. The mTLS + signing + token requirements below are non-negotiable, but the *plumbing* that holds the credentials and issues the requests should match what's already in the repo, not introduce a Nopan-specific approach. See [`nopan-integration`](https://docs.nopan.com/skills/nopan-integration.md) Step 0.

## 1. Mutual TLS

- Use a client certificate signed by a CA Nopan trusts. Obtain via the portal flow at [Signed Certificate](https://docs.nopan.com/guides/authentication/signed-certificate).
- TLS 1.3 is required in production; TLS 1.2 is allowed only as an exception.
- The mTLS private key is **separate** from the request-signing private key — do not reuse one for the other.

## 2. Request signing — RFC 9421

Every request, **including `POST /auth/token`**, must include `Signature` and `Signature-Input` headers built from the request components.

### Supported algorithms

| Algorithm | EC curve | Hash | Use when key is… |
|---|---|---|---|
| `ECDSA_SHA_256` | P-256 | SHA-256 | the default recommendation |
| `ECDSA_SHA_384` | P-384 | SHA-384 | |
| `ECDSA_SHA_512` | P-521 | SHA-512 | |

Match the hash to the curve. Mixing (e.g. P-384 with SHA-256) will fail verification.

### Signature base — canonical components

Build the **signature base** by concatenating, in order, the lines for the components below — each line is `"<component>": <value>\n`. The final line is `@signature-params` with **no trailing newline**.

**With body** (POST, PUT, PATCH):

1. `@authority` — lowercased hostname, e.g. `api.nopan.io` or `api.sandbox.nopan.dev`
2. `@method` — uppercase HTTP method
3. `@request-target` — path and query string
4. `content-digest` — `sha-256=:<base64-hash>:` (also accepts `sha-384` / `sha-512`)
5. `content-type` — media type without parameters, lowercased
6. `content-length` — body length in bytes
7. `@signature-params` — see below

**Without body** (GET, DELETE without payload): omit `content-digest`, `content-type`, `content-length`.

### `@signature-params` shape

```
nopan_sig=("<component>" "<component>" …);keyid="<your-key-id>";created=<unix-seconds-utc>
```

- The list inside `(...)` must match exactly the components used to build the base, in the same order.
- `keyid` is the `Signing Key ID` shown in the portal after the public key is registered.
- `created` is a UNIX timestamp in seconds (UTC).

### Headers attached to the request

```
Signature: nopan_sig=:<base64-signature>:
Signature-Input: <same string as @signature-params line>
```

Worked examples (Java / JavaScript / cURL) live at [Request Signing](https://docs.nopan.com/guides/authentication/request-signing). Use them as templates — do not paste them blindly without matching the algorithm to the user's actual key curve.

### Signing key lifecycle

- Generate a fresh key pair for signing (do **not** reuse the mTLS key).
- Upload only the public key to [the portal](https://portal.nopan.com/sandbox/signing-keys).
- Keys are valid for up to **12 months**; rotate before expiry. Nopan notifies in advance.
- Store the private key encrypted (secret manager or KMS).

### Debugging signatures

- The portal ships a [Signature Debugger](https://portal.nopan.com/sandbox/signing-keys) that builds the base string for an arbitrary request.
- A bad signature returns **401 Unauthorized**. See [`nopan-errors`](https://docs.nopan.com/skills/nopan-errors.md).
- Common pitfalls: trailing newline on the base string; component name not lowercased; `@authority` includes the port or scheme; `content-type` includes `; charset=...`; key curve and signing hash mismatched; clock skew on `created`.
- Background concepts: [Understanding Signatures](https://docs.nopan.com/guides/authentication/understanding-signatures).

## 3. OAuth token — `client_credentials`

```
POST /auth/token
Content-Type: application/x-www-form-urlencoded

grant_type=client_credentials&client_id=<organizationId>&scope=<scope>
```

**This call still requires mTLS and a valid request signature.** That is the most common stumbling block — do not treat `/auth/token` as a bootstrap that skips signing.

### Scopes

| Scope | Default? | Purpose |
|---|---|---|
| `payments:read` | yes | Read-only access to payment objects and status |
| `payments:process` | no — request explicitly | Initiate, capture, refund, cancel payments |
| `data:reports` | no — request explicitly | Retrieve reports via the API |

### Response

```json
{
  "access_token": "...",
  "expires_in": 7200,
  "refresh_expires_in": 0,
  "token_type": "Bearer",
  "scope": "payments:process"
}
```

- There is **no refresh token** (`refresh_expires_in` is always `0`). Always request a new token when the current one nears expiry.
- Recommended renewal window: ~5 minutes before `expires_in` elapses.
- Same token may be reused across many requests within its scope and lifetime.

### Using the token

Attach to every API call:

```
Authorization: Bearer <access_token>
Idempotency-Key: <uuid>          # required on mutating endpoints
Signature: nopan_sig=:...:
Signature-Input: nopan_sig=(...);keyid="...";created=...
```

Full reference: [OAuth Token Authentication](https://docs.nopan.com/guides/authentication/access-token).

## 4. Validating responses from Nopan

Nopan **signs its responses** too. Verify them with Nopan's public keys exposed via JWKS to defend against MitM and replay.

- JWKS endpoint and verification flow: [Response Validation](https://docs.nopan.com/guides/authentication/response-validation).
- Public keys metadata: [JWKS](https://docs.nopan.com/guides/authentication/jwks).
- Default response-signing hash is `sha-512`.

## Decision rules

- **Building a signature base** → use the canonical component list above; lowercase component names; no trailing newline.
- **Getting `401` on `/auth/token`** → verify mTLS first (cert presented, not expired, signed by trusted CA), then signature (algorithm matches curve, `created` not skewed, base string byte-identical to what Nopan would build).
- **Getting `401` on a business endpoint** → token may be expired or wrong scope; re-fetch and retry.
- **Getting `403`** → token is valid but scope is missing or the certificate is not trusted for this organization.
- **Rotating keys** → register the new public key first, switch the client to use the new `keyid`, then deregister the old one. Plan for an overlap window.

## Related guides

- For the auth headers that get attached to actual payment calls → [`nopan-payments`](https://docs.nopan.com/skills/nopan-payments.md).
- For interpreting auth-related error codes → [`nopan-errors`](https://docs.nopan.com/skills/nopan-errors.md).
- For testing signing in sandbox → [`nopan-testing`](https://docs.nopan.com/skills/nopan-testing.md).