> ## Documentation Index
> Fetch the complete documentation index at: https://docs.withleaf.io/llms.txt
> Use this file to discover all available pages before exploring further.

# Authentication

> OAuth2 token endpoints, including password and refresh grants, and `/authenticate`, with request fields, responses, and errors.

Every Leaf API request uses `Authorization: Bearer <token>`. There are two ways to get that token:

* **OAuth2** (recommended) — send your API owner email, password, and a Leaf-provisioned `client_id` to get an `access_token` and a `refresh_token`. Put the `access_token` in the Bearer header and call `https://api-v2.withleaf.io`. When it expires, exchange the `refresh_token` for a new access token without re-sending your password.
* **`/authenticate`** — send your API owner email and password to get an `id_token`. There is no refresh token; when it expires, authenticate again. Call `https://api.withleaf.io`.

For conceptual background, see [Authentication](/getting-started/authentication).

## OAuth2 token endpoint

`POST https://auth.withleaf.io/realms/leaf/protocol/openid-connect/token`

Requests use `Content-Type: application/x-www-form-urlencoded`. Two grant types are supported: Username/Password and refresh token.

<Warning>
  Never expose credentials in browser or mobile clients, and store access and refresh tokens securely.
</Warning>

### Username/Password Grant

Uses your API owner credentials together with a `client_id`.

| Field        | Type   | Required | Description                                |
| ------------ | ------ | -------- | ------------------------------------------ |
| `grant_type` | string | Yes      | `password`.                                |
| `client_id`  | string | Yes      | The client identifier provisioned by Leaf. |
| `username`   | string | Yes      | Your API owner email address.              |
| `password`   | string | Yes      | Your API owner password.                   |

<CodeGroup>
  ```bash cURL theme={null}
  curl -X POST \
    -H 'Content-Type: application/x-www-form-urlencoded' \
    -d 'grant_type=password' \
    -d 'client_id=your-client-id' \
    -d 'username=your-email@example.com' \
    -d 'password=your-password' \
    'https://auth.withleaf.io/realms/leaf/protocol/openid-connect/token'
  ```

  ```python Python theme={null}
  import requests

  response = requests.post(
      "https://auth.withleaf.io/realms/leaf/protocol/openid-connect/token",
      data={
          "grant_type": "password",
          "client_id": "your-client-id",
          "username": "your-email@example.com",
          "password": "your-password",
      },
  )
  token = response.json()["access_token"]
  ```

  ```javascript JavaScript theme={null}
  const axios = require("axios");

  axios.post(
    "https://auth.withleaf.io/realms/leaf/protocol/openid-connect/token",
    new URLSearchParams({
      grant_type: "password",
      client_id: "your-client-id",
      username: "your-email@example.com",
      password: "your-password",
    })
  )
    .then(({ data }) => console.log(data.access_token))
    .catch(console.error);
  ```
</CodeGroup>

### Refresh token grant

Exchange a refresh token for a new access token without re-sending credentials.

| Field           | Type   | Required | Description                                 |
| --------------- | ------ | -------- | ------------------------------------------- |
| `grant_type`    | string | Yes      | `refresh_token`.                            |
| `client_id`     | string | Yes      | The client identifier.                      |
| `refresh_token` | string | Yes      | The refresh token from a previous response. |

<CodeGroup>
  ```bash cURL theme={null}
  curl -X POST \
    -H 'Content-Type: application/x-www-form-urlencoded' \
    -d 'grant_type=refresh_token' \
    -d 'client_id=your-client-id' \
    -d 'refresh_token=your-refresh-token' \
    'https://auth.withleaf.io/realms/leaf/protocol/openid-connect/token'
  ```

  ```python Python theme={null}
  import requests

  response = requests.post(
      "https://auth.withleaf.io/realms/leaf/protocol/openid-connect/token",
      data={
          "grant_type": "refresh_token",
          "client_id": "your-client-id",
          "refresh_token": "your-refresh-token",
      },
  )
  token = response.json()["access_token"]
  ```

  ```javascript JavaScript theme={null}
  const axios = require("axios");

  axios.post(
    "https://auth.withleaf.io/realms/leaf/protocol/openid-connect/token",
    new URLSearchParams({
      grant_type: "refresh_token",
      client_id: "your-client-id",
      refresh_token: "your-refresh-token",
    })
  )
    .then(({ data }) => console.log(data.access_token))
    .catch(console.error);
  ```
</CodeGroup>

### Response

```json theme={null}
{
  "access_token": "eyJhbGciOi...",
  "expires_in": 300,
  "refresh_expires_in": 1800,
  "refresh_token": "eyJhbGciOi...",
  "token_type": "Bearer"
}
```

Use the `access_token` in the `Authorization: Bearer <token>` header of every API request. When it expires, use the refresh token grant to obtain a new one.

<Warning>
  When using OAuth2, send API requests to the new base URL `https://api-v2.withleaf.io` instead of `https://api.withleaf.io`.

  For example, Get All Leaf Users: `https://api-v2.withleaf.io/services/usermanagement/api/users`
</Warning>

<Note>
  Token lifetimes (`expires_in`, `refresh_expires_in`) are configured per client; the values above are examples and may differ for yours.
</Note>

### Error responses

| Status             | Error             | Meaning                                                  |
| ------------------ | ----------------- | -------------------------------------------------------- |
| `400 Bad Request`  | `invalid_request` | A required parameter is missing or malformed.            |
| `400 Bad Request`  | `invalid_grant`   | Credentials or the refresh token are invalid or expired. |
| `401 Unauthorized` | `invalid_client`  | The `client_id` is invalid.                              |

***

## Legacy `/authenticate` method

Email and password for an `id_token`. No refresh token. API calls go to `https://api.withleaf.io`.

`POST /authenticate`

### Base URL

```
https://api.withleaf.io/api
```

### Request body

| Field        | Type   | Required | Description                                                                        |
| ------------ | ------ | -------- | ---------------------------------------------------------------------------------- |
| `username`   | string | Yes      | Your API owner email address.                                                      |
| `password`   | string | Yes      | Your API owner password.                                                           |
| `rememberMe` | string | No       | `"true"` for a 30-day token, `"false"` for a 24-hour token. Defaults to `"false"`. |

### Token duration

| `rememberMe` | Token duration |
| ------------ | -------------- |
| `"true"`     | 30 days        |
| `"false"`    | 24 hours       |

When a token expires, request a new one from the same endpoint. There is no refresh token flow.

### Request

<CodeGroup>
  ```bash cURL theme={null}
  curl -X POST \
    -H 'Content-Type: application/json' \
    -d '{"username":"your-email@example.com","password":"your-password","rememberMe":"true"}' \
    'https://api.withleaf.io/api/authenticate'
  ```

  ```python Python theme={null}
  import requests

  response = requests.post(
      "https://api.withleaf.io/api/authenticate",
      headers={"Content-Type": "application/json"},
      json={
          "username": "your-email@example.com",
          "password": "your-password",
          "rememberMe": "true"
      }
  )
  token = response.json()["id_token"]
  ```

  ```javascript JavaScript theme={null}
  const axios = require("axios");

  axios.post("https://api.withleaf.io/api/authenticate", {
    username: "your-email@example.com",
    password: "your-password",
    rememberMe: "true",
  })
    .then(({ data }) => {
      const token = data.id_token;
      console.log(token);
    })
    .catch(console.error);
  ```
</CodeGroup>

### Response

```json theme={null}
{
  "id_token": "eyJhbGciOi..."
}
```

### Using the token

Include the token in the `Authorization` header of every API request:

```
Authorization: Bearer eyJhbGciOi...
```

### Error responses

| Status             | Meaning                                                                  |
| ------------------ | ------------------------------------------------------------------------ |
| `401 Unauthorized` | Credentials are invalid, or the token is missing, expired, or malformed. |
