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

# Authentication

> Authenticate with the PropOps API using session cookies, Bearer tokens, or CSRF tokens — and understand how permissions are enforced.

Every PropOps API endpoint requires authentication. The platform supports two authentication methods depending on your client type.

<Tabs>
  <Tab title="Browser session">
    Browser clients authenticate automatically via session cookies set at login. No additional configuration is needed — the browser sends the session cookie on every request.

    Session lifetimes:

    | Session type         | Duration   |
    | -------------------- | ---------- |
    | Web browser (active) | 2 hours    |
    | Web browser (idle)   | 30 minutes |
    | Remember Me          | 30 days    |

    Cookies are set with `HttpOnly`, `Secure` (HTTPS only), and `SameSite=Lax` flags to prevent JavaScript access and cross-site request forgery.
  </Tab>

  <Tab title="Bearer token">
    Mobile apps and direct API clients use a 64-character SHA-256 Bearer token. Request a token from `POST /api/security/mobile-auth`, then include it on every request in the `Authorization` header.

    Bearer tokens are long-lived (up to one year). You can revoke a token at any time via `DELETE /api/security/mobile-auth`.

    <Steps>
      <Step title="Request a Bearer token">
        Send your credentials to the mobile auth endpoint. You receive a token in the response.

        <CodeGroup>
          ```bash cURL theme={null}
          curl -X POST https://propops.yourcompany.com/api/security/mobile-auth \
            -H "Content-Type: application/json" \
            -d '{
              "email": "you@example.com",
              "password": "your-password"
            }'
          ```

          ```javascript JavaScript theme={null}
          const res = await fetch('https://propops.yourcompany.com/api/security/mobile-auth', {
            method: 'POST',
            headers: { 'Content-Type': 'application/json' },
            body: JSON.stringify({ email: 'you@example.com', password: 'your-password' }),
          });

          const { success, data } = await res.json();
          const token = data.token; // 64-character SHA-256 hex string
          ```

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

          res = requests.post(
              'https://propops.yourcompany.com/api/security/mobile-auth',
              json={'email': 'you@example.com', 'password': 'your-password'},
          )

          token = res.json()['data']['token']  # 64-character SHA-256 hex string
          ```
        </CodeGroup>

        Response:

        ```json theme={null}
        {
          "success": true,
          "data": {
            "token": "a1b2c3d4e5f6a1b2c3d4e5f6a1b2c3d4e5f6a1b2c3d4e5f6a1b2c3d4e5f6a1b2"
          },
          "message": "Mobile authentication token issued"
        }
        ```
      </Step>

      <Step title="Include the token on every request">
        Pass the token in the `Authorization` header using the `Bearer` scheme.

        <CodeGroup>
          ```bash cURL theme={null}
          curl https://propops.yourcompany.com/api/jobs/manage \
            -H "Authorization: Bearer a1b2c3d4e5f6a1b2c3d4e5f6a1b2c3d4e5f6a1b2c3d4e5f6a1b2c3d4e5f6a1b2"
          ```

          ```javascript JavaScript theme={null}
          const res = await fetch('https://propops.yourcompany.com/api/jobs/manage', {
            headers: {
              'Authorization': 'Bearer a1b2c3d4e5f6a1b2c3d4e5f6a1b2c3d4e5f6a1b2c3d4e5f6a1b2c3d4e5f6a1b2',
            },
          });

          const { success, data, count } = await res.json();
          ```

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

          res = requests.get(
              'https://propops.yourcompany.com/api/jobs/manage',
              headers={'Authorization': 'Bearer a1b2c3d4e5f6a1b2c3d4e5...'},
          )

          data = res.json()
          ```
        </CodeGroup>
      </Step>
    </Steps>
  </Tab>
</Tabs>

***

## CSRF tokens

State-changing requests — **POST**, **PUT**, and **DELETE** — require a valid CSRF token in addition to your session or Bearer token. This protects against cross-site request forgery attacks.

### Get a token

```bash theme={null}
GET /api/security/csrf-token
```

```json Response theme={null}
{
  "success": true,
  "data": {
    "token": "abc123def456..."
  },
  "message": "CSRF token issued"
}
```

### Send the token

Pass the CSRF token in any one of these locations:

| Location                     | Key            |
| ---------------------------- | -------------- |
| JSON request body            | `csrf_token`   |
| Form field or POST parameter | `csrf_token`   |
| HTTP request header          | `X-CSRF-Token` |

<CodeGroup>
  ```bash cURL — header theme={null}
  curl -X POST https://propops.yourcompany.com/api/jobs/manage \
    -H "Authorization: Bearer <your-token>" \
    -H "X-CSRF-Token: abc123def456..." \
    -H "Content-Type: application/json" \
    -d '{ "title": "Boiler repair", "priority": "high" }'
  ```

  ```javascript JavaScript — JSON body theme={null}
  // 1. Get a fresh CSRF token
  const csrfRes = await fetch('/api/security/csrf-token', {
    headers: { 'Authorization': 'Bearer <your-token>' },
  });
  const { data: { token: csrfToken } } = await csrfRes.json();

  // 2. Include it in the POST body
  const res = await fetch('/api/jobs/manage', {
    method: 'POST',
    headers: {
      'Authorization': 'Bearer <your-token>',
      'Content-Type': 'application/json',
    },
    body: JSON.stringify({
      csrf_token: csrfToken,
      title: 'Boiler repair',
      priority: 'high',
    }),
  });
  ```

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

  # 1. Get a fresh CSRF token
  csrf_res = requests.get(
      '/api/security/csrf-token',
      headers={'Authorization': 'Bearer <your-token>'},
  )
  csrf_token = csrf_res.json()['data']['token']

  # 2. Pass it as a header
  res = requests.post(
      '/api/jobs/manage',
      headers={
          'Authorization': 'Bearer <your-token>',
          'X-CSRF-Token': csrf_token,
      },
      json={'title': 'Boiler repair', 'priority': 'high'},
  )
  ```
</CodeGroup>

<Note>
  CSRF tokens are single-use and session-scoped. Fetch a fresh token for each state-changing operation or at the start of each user session.
</Note>

***

## Permissions

Access to each endpoint is controlled by a granular permission key in dot-notation, for example `api.jobs.manage.manage`. Your account inherits permissions from the role your administrator assigns to you.

If you call an endpoint your role does not have permission for, you receive `403 Forbidden`:

```json theme={null}
{
  "success": false,
  "error": "You do not have permission to perform this action"
}
```

To see your resolved permissions, call:

```bash theme={null}
GET /api/users/permissions
```

Contact your PropOps administrator to request additional permissions for your role.
