# Authentication

Authenticate with an x-api-key (merchant-scoped) or Auth0 M2M tokens.

Benji Pays uses two authentication methods, and each route accepts exactly one of them:

* **Merchant endpoints** (Gateways, Invoices, Customers, Payment Methods, Settings, Transactions, Users) authenticate with an **`x-api-key`** header issued from the merchant app. The key is scoped to one organization and inherits the permissions of the login that created it.
* **Partner endpoints** (Whoami, Plans, Partners, Organizations, Usage, Billing) authenticate with an **Auth0 M2M Bearer token**: exchange client credentials for a JWT and send `Authorization: Bearer <jwt>`.

The sections below cover both flows.

## API key (merchant-scoped)

Merchant-facing routes authenticate with an `x-api-key` header tied to a single organization. The API validates the key, loads the owning login, and resolves the request actor from it.

```http
GET https://api.benjipays.com/v2/gateways
x-api-key: YOUR_API_KEY
Content-Type: application/json
```

API keys are issued from the merchant app and inherit the live mapping permissions of their owning login. Revoke a key from the merchant app to immediately cut off access.

Organization-scoped routes additionally require the target organization to be **enabled** and not blocked by an expired free trial without billing. Disabled organizations receive `401` with detail `API access is not active for this organization`; expired trials receive `403` with detail `Free trial has ended and billing is not enabled`. Benji Pays internal support keys bypass these organization lifecycle checks.

### Available scopes

| Scope                                | Description                | Required mapping                   |
| ------------------------------------ | -------------------------- | ---------------------------------- |
| `organizations:gateways:read`        | Read payment gateways      | `companyAdmin`                     |
| `organizations:invoices:read`        | Read invoices              | `companyAdmin`                     |
| `organizations:customers:read`       | Read customers             | `companyAdmin`                     |
| `organizations:payment-methods:read` | Read saved payment methods | `companyAdmin` or `manageProfiles` |
| `organizations:settings:read`        | Read organization settings | `companyAdmin`                     |
| `organizations:transactions:read`    | Read payment transactions  | `companyAdmin`                     |
| `organizations:users:read`           | Read organization users    | `companyAdmin`                     |

## Auth0 M2M (partners and distributors)

The partner/distributor surface uses the OAuth 2.0 client-credentials grant against Auth0. The token's `actor` claim (`distributor`, `partner`, or `organization`) and OAuth scopes determine which routes the caller can reach.

### Prerequisites

* **Client ID** — your Auth0 M2M application client ID.
* **Client Secret** — your Auth0 M2M application client secret.
* **Auth0 domain** — your tenant domain (e.g. `your-tenant.auth0.com`).
* **API audience** — the API identifier configured in Auth0 (typically `https://api.benjipays.com`).

### 1. Obtain an access token

```http
POST https://YOUR_TENANT.auth0.com/oauth/token
Content-Type: application/json

{
  "client_id": "YOUR_CLIENT_ID",
  "client_secret": "YOUR_CLIENT_SECRET",
  "audience": "https://api.benjipays.com",
  "grant_type": "client_credentials"
}
```

Response:

```json
{
  "access_token": "eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9...",
  "token_type": "Bearer",
  "expires_in": 3600
}
```

The token is valid for one hour by default. Refresh it before expiry.

### 2. Call the API

```http
GET https://api.benjipays.com/v2/whoami
Authorization: Bearer YOUR_ACCESS_TOKEN
Content-Type: application/json
```

Successful responses return JSON. See [Errors](./errors.md) for failure responses.

### Examples

**curl**

```bash
curl -X POST https://YOUR_TENANT.auth0.com/oauth/token \
  -H "Content-Type: application/json" \
  -d '{
    "client_id": "YOUR_CLIENT_ID",
    "client_secret": "YOUR_CLIENT_SECRET",
    "audience": "https://api.benjipays.com",
    "grant_type": "client_credentials"
  }'
```

**Node.js**

```javascript
const axios = require('axios');

async function getAccessToken() {
  const response = await axios.post('https://YOUR_TENANT.auth0.com/oauth/token', {
    client_id: 'YOUR_CLIENT_ID',
    client_secret: 'YOUR_CLIENT_SECRET',
    audience: 'https://api.benjipays.com',
    grant_type: 'client_credentials'
  });
  return response.data.access_token;
}
```

## Failure responses

Failures use [RFC 7807](https://www.rfc-editor.org/rfc/rfc7807) `application/problem+json`. See the [Errors](./errors.md) guide for the full envelope.

| Status             | When                                                                                                                           |
| ------------------ | ------------------------------------------------------------------------------------------------------------------------------ |
| `401 Unauthorized` | Missing, invalid, or expired credentials; or organization API access is not active.                                            |
| `403 Forbidden`    | Wrong actor type, missing OAuth scope, insufficient mapping permission for the key owner, or free trial ended without billing. |

Example 401 body:

```json
{
  "type": "https://api.benjipays.com/problems/unauthorized",
  "title": "Unauthorized",
  "status": 401,
  "detail": "Invalid or missing authentication token",
  "instance": "/v2/whoami"
}
```

## Best practices

* Treat client secrets and access tokens as secrets. Never commit them.
* Implement token refresh before the one-hour expiry rather than reactively.
* Use environment variables or a secret manager (never source).
* Always call over HTTPS.
* Handle 401 and 403 distinctly — 401 is "re-authenticate", 403 is "you can't do this with these credentials".