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

# Laravel Sanctum (authentification par token API)

> Implémentez l'authentification par token API et SPA avec Laravel Sanctum : package léger, de l'installation à la mise en pratique.

## Qu'est-ce que Sanctum

Laravel Sanctum est un package d'authentification léger destiné aux SPA, applications mobiles et APIs simples. Sans connaître OAuth, vous pouvez émettre et gérer plusieurs tokens API par utilisateur.

Deux problèmes résolus :

| Mode           | Mécanisme                               | Usage principal               |
| -------------- | --------------------------------------- | ----------------------------- |
| **Tokens API** | En-tête `Authorization: Bearer <token>` | Mobile, intégrations tierces  |
| **SPA**        | Cookie de session + CSRF                | Frontend maison (Vue, React…) |

<Info>
  Utilisez le mode SPA pour vos SPA maison, et les tokens API pour mobile ou clients tiers. Vous pouvez utiliser un seul mode.
</Info>

### Sanctum vs Passport

|                   | Sanctum               | Passport                                  |
| ----------------- | --------------------- | ----------------------------------------- |
| **Complexité**    | Simple                | OAuth2 complet                            |
| **Usage**         | SPA/mobile maison     | Serveur OAuth2 pour applications externes |
| **Type de token** | Personal access token | OAuth2 access token                       |

Choisissez Passport si vous devez être un fournisseur OAuth2. Sinon, Sanctum suffit à la plupart des applications.

***

## Installation et configuration

### Installation

`install:api` configure Sanctum.

```shell theme={null}
php artisan install:api
```

Cette commande :

* installe `laravel/sanctum`
* publie la migration `personal_access_tokens`
* exécute la migration

### Ajouter le trait `HasApiTokens`

Ajoutez le trait au modèle `User`.

```php theme={null}
// app/Models/User.php

use Laravel\Sanctum\HasApiTokens;

class User extends Authenticatable
{
    use HasApiTokens, HasFactory, Notifiable;
}
```

Vous disposez alors de `$user->createToken()`, `$user->tokens`, etc.

***

## Authentification par token API

### Flux

```mermaid theme={null}
sequenceDiagram
    participant Client
    participant API as API Laravel
    participant DB as Base

    Client->>API: POST /login (email, password)
    API->>DB: Auth utilisateur
    DB-->>API: Infos utilisateur
    API->>DB: Génération et enregistrement (haché)
    API-->>Client: plainTextToken

    Note over Client: Stocke le token

    Client->>API: GET /api/user<br>Authorization: Bearer <token>
    API->>DB: Vérification (SHA-256)
    DB-->>API: Utilisateur authentifié
    API-->>Client: Réponse
```

### Émission d'un token

`createToken()` génère un token. `plainTextToken` contient la version en clair. **Elle n'est pas persistée** : renvoyez-la immédiatement à l'utilisateur.

```php theme={null}
use Illuminate\Http\Request;

Route::post('/tokens/create', function (Request $request) {
    $token = $request->user()->createToken($request->token_name);

    return ['token' => $token->plainTextToken];
})->middleware('auth');
```

Le hash SHA-256 seul est stocké en base.

### Portées (abilities)

Restreignez les actions autorisées par le token via des abilities.

```php theme={null}
$token = $user->createToken('mobile-app', ['server:update', 'server:read']);

return $token->plainTextToken;
```

Vérifiez à l'exécution :

```php theme={null}
if ($request->user()->tokenCan('server:update')) {
    // Mise à jour
}

if ($request->user()->tokenCant('server:update')) {
    abort(403);
}
```

#### Vérifier via middleware

Alias dans `bootstrap/app.php` :

```php theme={null}
use Laravel\Sanctum\Http\Middleware\CheckAbilities;
use Laravel\Sanctum\Http\Middleware\CheckForAnyAbility;

->withMiddleware(function (Middleware $middleware): void {
    $middleware->alias([
        'abilities' => CheckAbilities::class,  // Toutes
        'ability'   => CheckForAnyAbility::class, // Au moins une
    ]);
})
```

Application aux routes :

```php theme={null}
// Doit avoir check-status ET place-orders
Route::get('/orders', function () {
    // ...
})->middleware(['auth:sanctum', 'abilities:check-status,place-orders']);

// Doit avoir au moins une des deux
Route::get('/orders', function () {
    // ...
})->middleware(['auth:sanctum', 'ability:check-status,place-orders']);
```

### Expiration

Par défaut, pas d'expiration. Réglez `expiration` (minutes) dans `config/sanctum.php`.

```php theme={null}
// config/sanctum.php
'expiration' => 525600, // 365 jours (en minutes)
```

Expiration par token :

```php theme={null}
$token = $user->createToken(
    'token-name',
    ['*'],
    now()->addWeeks(1)
)->plainTextToken;
```

Programme la purge :

```php theme={null}
use Illuminate\Support\Facades\Schedule;

Schedule::command('sanctum:prune-expired --hours=24')->daily();
```

### Révocation

```php theme={null}
// Tous les tokens
$user->tokens()->delete();

// Token courant
$request->user()->currentAccessToken()->delete();

// Token précis
$user->tokens()->where('id', $tokenId)->delete();
```

***

## Authentification SPA

Basée sur les cookies de session — pas de gestion de token. Idéal pour votre frontend Vue/React/Next.js maison.

<Warning>
  SPA et API doivent partager le même TLD (sous-domaines différents autorisés). Envoyez `Accept: application/json` et un `Referer` ou `Origin`.
</Warning>

### Activer le middleware

Dans `bootstrap/app.php` :

```php theme={null}
->withMiddleware(function (Middleware $middleware): void {
    $middleware->statefulApi();
})
```

### Domaines first-party

Dans `config/sanctum.php`, réglez `stateful`.

```php theme={null}
// config/sanctum.php
'stateful' => explode(',', env('SANCTUM_STATEFUL_DOMAINS', sprintf(
    '%s%s',
    'localhost,localhost:3000,127.0.0.1,127.0.0.1:8000,::1',
    Sanctum::currentApplicationUrlWithPort()
))),
```

### CORS

Pour un autre sous-domaine :

```shell theme={null}
php artisan config:publish cors
```

Dans `config/cors.php` :

```php theme={null}
'supports_credentials' => true,
```

Côté frontend (axios) :

```js theme={null}
// resources/js/bootstrap.js
axios.defaults.withCredentials = true;
axios.defaults.withXSRFToken = true;
```

N'oubliez pas le domaine de session :

```php theme={null}
// config/session.php
'domain' => '.example.com', // avec le point
```

### Flux d'authentification

```mermaid theme={null}
sequenceDiagram
    participant SPA as Frontend SPA
    participant API as API Laravel
    participant Session

    SPA->>API: GET /sanctum/csrf-cookie
    API-->>SPA: Cookie XSRF-TOKEN

    SPA->>API: POST /login<br>en-tête X-XSRF-TOKEN
    API->>Session: Création de session
    API-->>SPA: Set-Cookie: laravel_session

    SPA->>API: GET /api/user<br>Cookie: laravel_session
    API->>Session: Vérification
    Session-->>API: Utilisateur authentifié
    API-->>SPA: Infos utilisateur
```

<Steps>
  <Step title="Récupérer le cookie CSRF">
    Avant de se connecter, initialisez CSRF.

    ```js theme={null}
    await axios.get('/sanctum/csrf-cookie');
    ```
  </Step>

  <Step title="Envoyer la requête de login">
    ```js theme={null}
    await axios.post('/login', {
        email: 'user@example.com',
        password: 'password',
    });
    ```
  </Step>

  <Step title="Requêtes authentifiées">
    Les requêtes suivantes utilisent le cookie de session.

    ```js theme={null}
    const response = await axios.get('/api/user');
    console.log(response.data);
    ```
  </Step>
</Steps>

***

## Protéger les routes

`auth:sanctum` retourne `401 Unauthorized` pour les requêtes non authentifiées. Ce middleware unique gère à la fois tokens API et SPA.

```php theme={null}
use Illuminate\Http\Request;

// Route seule
Route::get('/user', function (Request $request) {
    return $request->user();
})->middleware('auth:sanctum');

// Groupe de routes
Route::middleware('auth:sanctum')->group(function () {
    Route::get('/profile', [ProfileController::class, 'show']);
    Route::put('/profile', [ProfileController::class, 'update']);
    Route::get('/posts', [PostController::class, 'index']);
});
```

***

## Exemple : login API et renvoi d'un token

Cas mobile.

<Steps>
  <Step title="Endpoint de login">
    ```php theme={null}
    // routes/api.php

    use App\Models\User;
    use Illuminate\Http\Request;
    use Illuminate\Support\Facades\Hash;
    use Illuminate\Validation\ValidationException;

    Route::post('/sanctum/token', function (Request $request) {
        $request->validate([
            'email'       => ['required', 'email'],
            'password'    => ['required'],
            'device_name' => ['required', 'string'],
        ]);

        $user = User::where('email', $request->email)->first();

        if (! $user || ! Hash::check($request->password, $user->password)) {
            throw ValidationException::withMessages([
                'email' => ['Les identifiants fournis sont incorrects.'],
            ]);
        }

        return response()->json([
            'token' => $user->createToken($request->device_name)->plainTextToken,
        ]);
    });
    ```
  </Step>

  <Step title="Routes authentifiées">
    ```php theme={null}
    // routes/api.php

    Route::middleware('auth:sanctum')->group(function () {
        Route::get('/user', function (Request $request) {
            return $request->user();
        });

        Route::post('/logout', function (Request $request) {
            $request->user()->currentAccessToken()->delete();

            return response()->json(['message' => 'Déconnecté.']);
        });

        Route::post('/logout/all', function (Request $request) {
            $request->user()->tokens()->delete();

            return response()->json(['message' => 'Déconnexion de tous les appareils.']);
        });
    });
    ```
  </Step>

  <Step title="Requêtes client">
    ```js theme={null}
    // Login
    const { data } = await axios.post('/api/sanctum/token', {
        email: 'user@example.com',
        password: 'password',
        device_name: 'My iPhone',
    });

    const token = data.token;

    // Requêtes authentifiées
    const response = await axios.get('/api/user', {
        headers: {
            Authorization: `Bearer ${token}`,
        },
    });
    ```
  </Step>
</Steps>

***

## Tests

Utilisez `Sanctum::actingAs()` pour authentifier avec des abilities.

<Tabs>
  <Tab title="Pest">
    ```php theme={null}
    use App\Models\User;
    use Laravel\Sanctum\Sanctum;

    test('récupère la liste des tâches', function () {
        Sanctum::actingAs(
            User::factory()->create(),
            ['view-tasks']
        );

        $response = $this->get('/api/tasks');

        $response->assertOk();
    });

    test('accède avec toutes les abilities', function () {
        Sanctum::actingAs(
            User::factory()->create(),
            ['*']
        );

        $response = $this->get('/api/tasks');

        $response->assertOk();
    });
    ```
  </Tab>

  <Tab title="PHPUnit">
    ```php theme={null}
    use App\Models\User;
    use Laravel\Sanctum\Sanctum;

    public function test_task_list_can_be_retrieved(): void
    {
        Sanctum::actingAs(
            User::factory()->create(),
            ['view-tasks']
        );

        $response = $this->get('/api/tasks');

        $response->assertOk();
    }
    ```
  </Tab>
</Tabs>

***

## Récapitulatif

<AccordionGroup>
  <Accordion title="Installation">
    ```shell theme={null}
    php artisan install:api
    ```

    Ajout du trait `HasApiTokens` :

    ```php theme={null}
    use Laravel\Sanctum\HasApiTokens;

    class User extends Authenticatable
    {
        use HasApiTokens, HasFactory, Notifiable;
    }
    ```
  </Accordion>

  <Accordion title="API fréquente">
    ```php theme={null}
    // Émission
    $token = $user->createToken('token-name')->plainTextToken;

    // Avec abilities
    $token = $user->createToken('token-name', ['read', 'write'])->plainTextToken;

    // Vérification
    $user->tokenCan('read');
    $user->tokenCant('write');

    // Révocation
    $user->tokens()->delete();
    $request->user()->currentAccessToken()->delete();
    $user->tokens()->where('id', $id)->delete();
    ```
  </Accordion>

  <Accordion title="Choix API vs SPA">
    * **Tokens API** : mobile, intégrations tierces, CLI — clients sans session.
    * **SPA** : Vue/React/Next.js maison sur le même domaine — plus sûr, sans gestion de token.
  </Accordion>
</AccordionGroup>


## Related topics

- [Implémenter un guard d'authentification personnalisé](/fr/advanced/custom-auth-guard.md)
- [Créer un serveur MCP avec Laravel](/fr/advanced/mcp-server.md)
- [Authentification par compte de service - Google Sheets API for Laravel](/fr/packages/laravel-google-sheets/service-account.md)
- [Laravel Passport (implémentation d'un serveur OAuth2)](/fr/passport.md)
- [Routage](/fr/routing.md)
