PHP Logolaravel/sanctum

Laravel Sanctum is a lightweight API authentication system provided by Laravel. It offers a simple and robust way to authenticate single-page applications (SPAs), mobile applications, and simple, token-based APIs.

Key Features and Use Cases:

1. SPA Authentication: For SPAs (like Vue.js, React, or Angular frontends) that interact with a Laravel backend, Sanctum leverages Laravel's existing session-based authentication. When a SPA makes a request, Sanctum attempts to authenticate via a session cookie. This requires proper Cross-Origin Resource Sharing (CORS) configuration, sending credentials (`withCredentials`), and handling CSRF tokens on the frontend.

2. Mobile Application & Token-Based API Authentication: For mobile applications or third-party APIs, Sanctum provides a system for issuing API tokens (also known as "personal access tokens"). These tokens are issued to users and stored in the database. When a mobile app or another API client makes a request, it sends this token in the `Authorization` header as a Bearer token. Sanctum then authenticates the request using this token.

How it Works (API Tokens):

* Token Issuance: Users can generate API tokens through an endpoint in your application. These tokens are stored in the `personal_access_tokens` table in your database.
* Abilities (Scopes): Each token can be assigned a specific set of "abilities" or scopes, defining what actions the token is authorized to perform. For example, a token might have `['read', 'write']` abilities for a specific resource, allowing fine-grained access control.
* Authentication: When an incoming request includes an `Authorization: Bearer {token}` header, Sanctum retrieves the associated token from the database, authenticates the user, and makes the authenticated user available via `Auth::user()`.
* Token Revocation: Tokens can be revoked by the user or an administrator, instantly invalidating them and preventing further unauthorized access.

Installation and Setup:

1. Install Sanctum:
```bash
composer require laravel/sanctum
```
2. Publish Configuration and Run Migrations:
```bash
php artisan vendor:publish --provider="Laravel\Sanctum\SanctumServiceProvider"
php artisan migrate
```
This command creates the `personal_access_tokens` table and publishes the `sanctum.php` configuration file.
3. Middleware: For SPA authentication, ensure the `EnsureFrontendRequestsAreStateful` middleware is uncommented in your `app/Http/Kernel.php` within the `api` middleware group. For API token authentication, ensure your API routes use the `auth:sanctum` guard.
4. User Model Trait: Add the `HasApiTokens` trait to your `App\Models\User` model:
```php
use Laravel\Sanctum\HasApiTokens;

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

SPA Statefulness Configuration:
For SPAs, you must configure the `SANCTUM_STATEFUL_DOMAINS` environment variable in your `.env` file to include the domain of your frontend application (e.g., `http://localhost:3000`). This tells Sanctum to treat requests from these domains as stateful, using session cookies.

Example Code

<?php

namespace App\Http\Controllers;

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

class AuthController extends Controller
{
    /
     * Register a new user.
     *
     * @param  \Illuminate\Http\Request  $request
     * @return \Illuminate\Http\JsonResponse
     */
    public function register(Request $request)
    {
        $request->validate([
            'name' => 'required|string|max:255',
            'email' => 'required|string|email|max:255|unique:users',
            'password' => 'required|string|min:8|confirmed',
        ]);

        $user = User::create([
            'name' => $request->name,
            'email' => $request->email,
            'password' => Hash::make($request->password),
        ]);

        return response()->json([
            'message' => 'User registered successfully!'
        ], 201);
    }

    /
     * Authenticate user and create a new personal access token.
     *
     * @param  \Illuminate\Http\Request  $request
     * @return \Illuminate\Http\JsonResponse
     * @throws \Illuminate\Validation\ValidationException
     */
    public function login(Request $request)
    {
        $request->validate([
            'email' => 'required|email',
            'password' => 'required',
            'device_name' => 'required', // e.g., 'iPhone_12_Pro', 'My_Web_Browser'
        ]);

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

        if (! $user || ! Hash::check($request->password, $user->password)) {
            throw ValidationException::withMessages([
                'email' => ['The provided credentials are incorrect.'],
            ]);
        }

        // Create a token with specific abilities (scopes)
        // For example, ['post:create', 'post:read'], or ['*'] for all abilities
        $token = $user->createToken($request->device_name, ['post:create', 'post:read'])->plainTextToken;

        return response()->json([
            'token' => $token
        ]);
    }

    /
     * Get the authenticated user's details.
     *
     * @param  \Illuminate\Http\Request  $request
     * @return \Illuminate\Http\JsonResponse
     */
    public function user(Request $request)
    {
        return response()->json($request->user());
    }

    /
     * Logout the authenticated user (revoke current token).
     *
     * @param  \Illuminate\Http\Request  $request
     * @return \Illuminate\Http\JsonResponse
     */
    public function logout(Request $request)
    {
        // Revoke the current token that was used for the request
        $request->user()->currentAccessToken()->delete();

        // Optionally, to revoke ALL tokens for the user:
        // $request->user()->tokens()->delete();

        return response()->json([
            'message' => 'Logged out successfully.'
        ]);
    }
}

/*
|--------------------------------------------------------------------------
| Example API Routes (in routes/api.php)
|--------------------------------------------------------------------------
*/

// Public routes (accessible without authentication)
// Route::post('/register', [AuthController::class, 'register']);
// Route::post('/login', [AuthController::class, 'login']);

// Protected routes (require a valid Sanctum token)
// Route::middleware('auth:sanctum')->group(function () {
//     Route::get('/user', [AuthController::class, 'user']);
//     Route::post('/logout', [AuthController::class, 'logout']);

//     // Example of route protection by token ability (scope)
//     Route::post('/posts', function () {
//         // This route can only be accessed by tokens with 'post:create' ability
//         return response()->json(['message' => 'Post created successfully!']);
//     })->middleware('abilities:post:create'); // Or ->middleware('can:create,Post') if using gates/policies

//     Route::get('/posts', function () {
//         // This route can only be accessed by tokens with 'post:read' ability
//         return response()->json(['message' => 'List of posts.']);
//     })->middleware('abilities:post:read');
// });

/*
|--------------------------------------------------------------------------
| How to make requests using the token
|--------------------------------------------------------------------------
*/

// After a successful login, your API will return a token.
// Send this token in the Authorization header for subsequent protected requests:
//
// HTTP Request Example (using a tool like Postman, Insomnia, or Axios in JS):
//
// GET /api/user
// Headers:
//   Authorization: Bearer <YOUR_GENERATED_TOKEN>
//   Accept: application/json
//
// POST /api/posts
// Headers:
//   Authorization: Bearer <YOUR_GENERATED_TOKEN>
//   Accept: application/json
// Body: {"title": "New Post", "content": "This is a new post."}