> ## 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 (autenticazione con API token)

> Come implementare autenticazione API token e SPA con Laravel Sanctum. Installazione e uso pratico di un pacchetto di autenticazione leggero.

## Cos'è Sanctum

Laravel Sanctum è un pacchetto di autenticazione leggero per SPA, applicazioni mobili e API semplici. Senza conoscenze approfondite di OAuth puoi emettere e gestire più API token per utente.

Sanctum risolve due problemi.

| Modalità                     | Meccanismo                             | Uso                                     |
| ---------------------------- | -------------------------------------- | --------------------------------------- |
| **Autenticazione API token** | Header `Authorization: Bearer <token>` | App mobili, integrazioni di terze parti |
| **Autenticazione SPA**       | Cookie di sessione + protezione CSRF   | Frontend proprie (Vue/React, ecc.)      |

<Info>
  Per API chiamate dalla tua SPA usa l'autenticazione SPA. Per app mobili o integrazioni terze usa l'autenticazione con API token. Puoi usarne solo una.
</Info>

### Quando usare Passport

|               | Sanctum                | Passport                       |
| ------------- | ---------------------- | ------------------------------ |
| Complessità   | Semplice               | OAuth2 completo                |
| Uso           | SPA/app mobili proprie | Provider OAuth per app esterne |
| Tipo di token | Personal access token  | OAuth2 access token            |

Se devi essere un provider OAuth2 per servizi esterni, scegli Passport; per il resto Sanctum è sufficiente.

***

## Installazione e configurazione

### Installazione

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

Installa `laravel/sanctum`, pubblica la migration di `personal_access_tokens` ed esegue le migration.

### Trait HasApiTokens

Aggiungi il trait a `User`.

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

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

Ora hai `$user->createToken()` e `$user->tokens`.

***

## Autenticazione con API token

### Flusso

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

    Client->>API: POST /login (email, password)
    API->>DB: Verifica utente
    DB-->>API: Info utente
    API->>DB: Crea e salva token (hash)
    API-->>Client: plainTextToken

    Note over Client: Salva il token

    Client->>API: GET /api/user<br>Authorization: Bearer <token>
    API->>DB: Verifica token (SHA-256)
    DB-->>API: Utente autenticato
    API-->>Client: Response
```

### Emettere un token

`createToken()` restituisce `plainTextToken`. **Il token in chiaro non viene salvato**: restituiscilo subito al client.

```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');
```

Nel DB è salvato con hash SHA-256.

### Scope (abilities)

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

return $token->plainTextToken;
```

Verifica gli scope:

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

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

#### Middleware per scope

In `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,
        'ability'   => CheckForAnyAbility::class,
    ]);
})
```

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

Route::get('/orders', function () {
    // ...
})->middleware(['auth:sanctum', 'ability:check-status,place-orders']);
```

### Scadenza dei token

Di default nessuna scadenza. In `config/sanctum.php` `expiration` in minuti.

```php theme={null}
'expiration' => 525600, // 365 giorni
```

Per token:

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

Prune periodico:

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

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

### Revocare token

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

$request->user()->currentAccessToken()->delete();

$user->tokens()->where('id', $tokenId)->delete();
```

***

## Autenticazione SPA

Con cookie di sessione, senza gestione token. Ideale per frontend proprie.

<Warning>
  SPA e API devono condividere lo stesso dominio (i sottodomini possono differire). Le richieste devono includere `Accept: application/json` e `Referer` o `Origin`.
</Warning>

### Attiva il middleware Sanctum

In `bootstrap/app.php`:

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

### Domini first-party

```php theme={null}
'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

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

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

Frontend axios:

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

Sessione cookie:

```php theme={null}
'domain' => '.example.com',
```

### Flusso di login SPA

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

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

    SPA->>API: POST /login<br>Header X-XSRF-TOKEN
    API->>Session: Crea sessione
    API-->>SPA: Set-Cookie: laravel_session

    SPA->>API: GET /api/user<br>Cookie: laravel_session
    API->>Session: Verifica sessione
    Session-->>API: Utente autenticato
    API-->>SPA: Info utente
```

<Steps>
  <Step title="Ottieni il cookie CSRF">
    ```js theme={null}
    await axios.get('/sanctum/csrf-cookie');
    ```
  </Step>

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

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

***

## Protezione delle route

Applica `auth:sanctum`.

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

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

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

***

## Esempio: API di login e restituzione del token

<Steps>
  <Step title="Endpoint di login">
    ```php theme={null}
    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' => ['Credenziali non valide.'],
            ]);
        }

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

  <Step title="Route autenticate">
    ```php theme={null}
    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' => 'Logout effettuato.']);
        });

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

            return response()->json(['message' => 'Logout da tutti i dispositivi.']);
        });
    });
    ```
  </Step>

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

    const token = data.token;

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

***

## Test

Con `Sanctum::actingAs()` autentichi e definisci gli scope.

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

    test('è possibile ottenere la lista dei task', function () {
        Sanctum::actingAs(
            User::factory()->create(),
            ['view-tasks']
        );

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

        $response->assertOk();
    });

    test('token con tutte le 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>

***

## Riepilogo

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

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

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

  <Accordion title="API più usate">
    ```php theme={null}
    $token = $user->createToken('token-name')->plainTextToken;
    $token = $user->createToken('token-name', ['read', 'write'])->plainTextToken;

    $user->tokenCan('read');
    $user->tokenCant('write');

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

  <Accordion title="API token vs SPA">
    * **API token**: client senza sessione (mobile, terze parti, CLI).
    * **SPA**: frontend Vue/React/Next.js sullo stesso dominio. Più sicura, no token da gestire.
  </Accordion>
</AccordionGroup>


## Related topics

- [Creare un server MCP con Laravel](/it/advanced/mcp-server.md)
- [Laravel Passport (implementazione di server OAuth2)](/it/passport.md)
- [Laravel Socialite (autenticazione social)](/it/socialite.md)
- [Autenticazione OAuth 2.0 - Google Sheets API for Laravel](/it/packages/laravel-google-sheets/oauth.md)
- [Routing](/it/routing.md)
