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

# Authentication

> Pair the API guard with the middleware group. Session cookies, Sanctum SPAs, and bearer tokens.

The guard and the middleware group have to match. The `web` guard reads the user out of the session, and Laravel's `api` group never starts one. The installer pairs them; if you edit config by hand, keep them in sync.

| Your app                                 | `api.guard` | `api.middleware` |
| ---------------------------------------- | ----------- | ---------------- |
| Blade / Livewire / Inertia, same origin  | `'web'`     | `['web']`        |
| SPA on another origin, or mobile / token | `'sanctum'` | `['api']`        |

Getting this wrong is quiet rather than loud: **reads keep working** (they are public) and **every write comes back 401**.

Pro resolves the acting user through `ApiAuth` and `ResolveCommentifyGuard` middleware, which calls `Auth::shouldUse()` for the configured guard on every API request.

## Same-origin (`web`)

Nothing else to do. The browser sends the session cookie. The SDK reads Laravel's `XSRF-TOKEN` cookie and echoes it as `X-XSRF-TOKEN`, so CSRF passes without setup.

```php theme={null}
'api' => [
    'guard' => 'web',
    'middleware' => ['web'],
],
```

```ts theme={null}
new CommentifyClient(); // credentials: include, XSRF from cookie
```

## Cross-origin SPA (`sanctum`)

1. Add the SPA origin to `config/sanctum.php` `stateful`.
2. Set `supports_credentials => true` in `config/cors.php`.
3. Call `/sanctum/csrf-cookie` once before the first write.

```php theme={null}
'api' => [
    'guard' => 'sanctum',
    'middleware' => ['api'],
],
```

## Tokens (mobile, server-to-server)

Pass the token to the client and skip cookies. Fetch uses `credentials: 'same-origin'` when a token is set, and sends `Authorization: Bearer …`.

```ts theme={null}
new CommentifyClient({ token: await getAccessToken() });
```

## Who can write

| Action                                              | Auth                                                                   |
| --------------------------------------------------- | ---------------------------------------------------------------------- |
| List comments, replies, UI config, markdown preview | None                                                                   |
| `@mention` user search (`GET users?q=`)             | **Required** (enumeration protection; max 5; `LIKE` wildcards escaped) |
| Create comment                                      | User, or guest when `core.allow_guests` **and** `api.guest_comments`   |
| Edit / delete                                       | Owner (guests cannot)                                                  |
| Like                                                | User, or guest when `api.guest_likes` (IP + user agent)                |
| Report                                              | User, or guest when `api.guest_reports`                                |
| Pin / unpin                                         | Moderator (`isCommentifyModerator()` or `badges.moderator_ids`)        |
| Upload media                                        | User, or guest per `media.guests`                                      |

Read-only mode (`core.read_only`) rejects all writes with `423` and code `read_only`. Comment bans reject with `403` and code `comment_banned`. See [API errors](/api#error-codes).
