Navigation

Laravel

Laravel Sanctum: API Token Authentication Made Simple

Learn how to implement secure API authentication with Laravel Sanctum. This comprehensive guide covers token-based auth, SPA authentication, and mobile app integration with practical examples and best practices.

Summary: Explore Laravel Sanctum, the lightweight authentication system for SPAs, mobile apps, and API tokens. Learn how to implement secure, token-based authentication with minimal configuration while protecting your Laravel APIs from unauthorized access.

Table Of Contents

Introduction to Laravel Sanctum

Authentication is one of the foundational aspects of modern web applications. Whether you're building a Single Page Application (SPA), a mobile app that communicates with your backend, or a simple API that needs token-based authentication, handling authentication securely and efficiently is crucial.

Laravel Sanctum was introduced to provide a lightweight solution for API token authentication, SPA authentication, and mobile application authentication. Unlike the more complex Laravel Passport (which implements a full OAuth2 server), Sanctum offers a simpler approach that's perfect for many applications.

In this guide, we'll explore how Sanctum works and how to implement it in various scenarios to secure your Laravel applications.

Understanding Sanctum's Features

Sanctum provides three primary authentication features:

  1. API Token Authentication: Issue API tokens that can be used to authenticate API requests
  2. SPA Authentication: Cookie-based session authentication for single-page applications
  3. Mobile Application Authentication: Token-based authentication for mobile apps

Each of these approaches solves a specific authentication problem, making Sanctum a versatile tool in your Laravel development toolkit.

Getting Started with Sanctum

Installation

Let's begin by installing Sanctum in your Laravel project:

composer require laravel/sanctum

After installation, publish the configuration and migration files:

php artisan vendor:publish --provider="Laravel\Sanctum\SanctumServiceProvider"

Run the migrations to create the necessary database tables:

php artisan migrate

Configuration

For SPA authentication, you'll need to update your CORS configuration to allow requests from your frontend application. In your config/cors.php file:

'paths' => ['api/*', 'sanctum/csrf-cookie'],
'supports_credentials' => true,

And in your app/Http/Kernel.php file, add Sanctum's middleware to the API middleware group:

'api' => [
    \Laravel\Sanctum\Http\Middleware\EnsureFrontendRequestsAreStateful::class,
    'throttle:api',
    \Illuminate\Routing\Middleware\SubstituteBindings::class,
],

API Token Authentication

Let's start with the simplest use case: API token authentication. This approach is perfect for server-to-server communication or third-party API integration.

Creating API Tokens

First, ensure your User model uses the HasApiTokens trait:

use Laravel\Sanctum\HasApiTokens;

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

Now you can create tokens for your users:

// Creating a token with specific abilities
$token = $user->createToken('token-name', ['server:update']);

// Creating a token with all abilities
$token = $user->createToken('token-name');

// Get the plain text token (save this, as you won't be able to see it again)
$tokenString = $token->plainTextToken;

Each token consists of two parts:

  1. A token ID stored in your database
  2. A hashed token that is only shown once when created

Authenticating Requests with Tokens

When making requests to your API, clients need to include the token in the Authorization header:

Authorization: Bearer {your-token-string}

Here's an example using Axios:

axios.get('/api/user', {
    headers: {
        'Authorization': `Bearer ${token}`
    }
});

Protecting Routes with Sanctum

To restrict access to authenticated users, use the auth:sanctum middleware:

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

Token Abilities (Permissions)

One of Sanctum's powerful features is the ability to assign specific permissions (or "abilities") to tokens:

// Check if the token has a specific ability
if ($request->user()->tokenCan('server:update')) {
    // User can update the server
}

In your routes, you can check for these abilities:

Route::middleware(['auth:sanctum', 'ability:server:update'])->post('/servers', function () {
    // Update server code
});

Revoking Tokens

To revoke tokens, you have several options:

// Revoke all tokens
$user->tokens()->delete();

// Revoke a specific token
$user->tokens()->where('id', $tokenId)->delete();

// Revoke all tokens except the current one
$user->tokens()->where('id', '!=', $tokenId)->delete();

SPA Authentication

For Single Page Applications (SPAs), Sanctum provides a seamless authentication experience using cookies rather than tokens.

How SPA Authentication Works

  1. Your SPA makes a request to the /sanctum/csrf-cookie endpoint to get a CSRF token
  2. Your SPA then makes a login request with credentials and the CSRF token
  3. If the credentials are correct, Laravel creates a session
  4. Subsequent requests are authenticated using the session cookie

Setting Up SPA Authentication

First, configure your .env file with the correct session domain:

SESSION_DOMAIN=your-domain.com
SANCTUM_STATEFUL_DOMAINS=your-spa-domain.com

In your SPA, here's how to implement the login flow using Axios:

// 1. Get CSRF cookie
await axios.get('/sanctum/csrf-cookie');

// 2. Login with credentials
await axios.post('/login', {
    email: 'user@example.com',
    password: 'password'
});

// 3. Now you're authenticated and can make requests to authenticated endpoints
const response = await axios.get('/api/user');

Remember to configure Axios to include credentials:

axios.defaults.withCredentials = true;

Mobile App Authentication

For mobile apps, you'll want to use the token-based approach, but with a login endpoint instead of creating tokens manually.

Creating a Login Endpoint

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

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

    if (! $user || ! Hash::check($request->password, $user->password)) {
        return response()->json([
            'message' => 'The provided credentials are incorrect.'
        ], 401);
    }

    // Create token for the device
    $token = $user->createToken($request->device_name);

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

In your mobile app, you would then:

  1. Make a request to this login endpoint with credentials and a device name
  2. Store the returned token securely in the mobile app
  3. Include the token in subsequent API requests

Advanced Sanctum Configuration

Customizing Token Expiration

By default, Sanctum tokens never expire. To add expiration, you can configure it in your config/sanctum.php file:

'expiration' => 60 * 24, // 24 hours in minutes

To manually check for expired tokens in your code:

if ($token->created_at->lte(now()->subMinutes(config('sanctum.expiration')))) {
    // Token has expired
}

Custom Token Models

If you need to extend the functionality of tokens, you can create a custom token model:

namespace App\Models;

use Laravel\Sanctum\PersonalAccessToken as SanctumPersonalAccessToken;

class PersonalAccessToken extends SanctumPersonalAccessToken
{
    // Add your custom functionality
}

Then register it in your config/sanctum.php file:

'personal_access_token_model' => App\Models\PersonalAccessToken::class,

Best Practices for Sanctum Implementation

Security Considerations

  1. Use HTTPS: Always use HTTPS in production to prevent token interception
  2. Token Storage: Store tokens securely, especially on the client side
  3. Token Abilities: Limit token abilities to only what's needed
  4. Regular Cleanup: Implement a routine to clean up expired tokens
  5. Rate Limiting: Apply rate limiting to authentication endpoints

Performance Optimizations

  1. Database Indexing: Ensure proper indexing on token tables for large applications
  2. Token Pruning: Regularly delete old, unused tokens
  3. Consider Redis: For large-scale applications, consider using Redis for token storage

Testing Authentication with Sanctum

Laravel makes it easy to test APIs protected by Sanctum. Here's how to authenticate in your tests:

use Laravel\Sanctum\Sanctum;

public function test_user_can_view_protected_resource()
{
    Sanctum::actingAs(
        User::factory()->create(),
        ['view-resources'] // abilities if needed
    );

    $response = $this->getJson('/api/resources');

    $response->assertStatus(200);
}

Real-world Sanctum Implementation Example

Let's walk through a complete example of implementing Sanctum in a real-world scenario for a blog API.

Setting Up User Authentication

// routes/api.php
Route::post('/register', [AuthController::class, 'register']);
Route::post('/login', [AuthController::class, 'login']);
Route::post('/logout', [AuthController::class, 'logout'])->middleware('auth:sanctum');

// AuthController.php
public function register(Request $request)
{
    $validated = $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' => $validated['name'],
        'email' => $validated['email'],
        'password' => Hash::make($validated['password']),
    ]);

    $token = $user->createToken('auth_token')->plainTextToken;

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

public function login(Request $request)
{
    $validated = $request->validate([
        'email' => 'required|string|email',
        'password' => 'required|string',
    ]);

    if (!Auth::attempt($validated)) {
        return response()->json([
            'message' => 'Invalid login credentials',
        ], 401);
    }

    $user = User::where('email', $validated['email'])->firstOrFail();
    
    // Revoke old tokens
    $user->tokens()->delete();
    
    $token = $user->createToken('auth_token')->plainTextToken;

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

public function logout(Request $request)
{
    $request->user()->currentAccessToken()->delete();

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

Protecting API Routes

// routes/api.php
Route::middleware('auth:sanctum')->group(function () {
    Route::get('/user', function (Request $request) {
        return $request->user();
    });
    
    Route::apiResource('posts', PostController::class);
    
    Route::post('/posts/{post}/comments', [CommentController::class, 'store']);
});

Role-Based Access with Token Abilities

// Create admin token with specific abilities
$adminToken = $user->createToken('admin-token', ['post:create', 'post:update', 'post:delete']);

// Create reader token with limited abilities
$readerToken = $user->createToken('reader-token', ['post:read']);

// In your controller
public function update(Request $request, Post $post)
{
    if (!$request->user()->tokenCan('post:update')) {
        return response()->json([
            'message' => 'Not authorized to update posts',
        ], 403);
    }
    
    // Update post logic
}

Conclusion

Laravel Sanctum offers a flexible, lightweight solution for API authentication that strikes the perfect balance between simplicity and functionality. Whether you're building a SPA, mobile app, or providing API tokens for third-party integration, Sanctum provides a straightforward approach to authentication.

By following the patterns and practices outlined in this guide, you can implement secure, efficient authentication in your Laravel applications with minimal configuration and overhead. The beauty of Sanctum lies in its simplicity—it does exactly what you need without unnecessary complexity.

Next time you're building a Laravel application that requires authentication, consider whether the lightweight approach of Sanctum might be a better fit than more complex solutions like Passport, especially if you don't need full OAuth2 functionality.

Share this article

Add Comment

No comments yet. Be the first to comment!

More from Laravel