Building RESTful APIs with Laravel Sanctum

Introduction

Building Secure RESTful APIs with Laravel Sanctum

Complete Guide to API Authentication and Security in Laravel Applications

In today’s interconnected digital landscape, building secure and scalable RESTful APIs is crucial for modern web applications. Laravel Sanctum (formerly known as Laravel Airlock) provides a featherweight authentication system for SPAs (Single Page Applications), mobile applications, and simple token-based APIs. Unlike Laravel Passport, which provides a full OAuth2 implementation, Sanctum offers a simpler, more focused approach to API authentication that’s perfect for most projects. In this comprehensive guide, we’ll explore how to build, secure, and optimize RESTful APIs using Laravel Sanctum.


Getting Started: Installation & Configuration

Setting up Laravel Sanctum for your API development journey

Installation Process

First, install Laravel Sanctum via Composer and run the necessary migrations to set up your database tables.

Terminal Commands

# Install Laravel Sanctum via Composer
composer require laravel/sanctum

# Publish the Sanctum configuration and migration files
php artisan vendor:publish --provider="Laravel\Sanctum\SanctumServiceProvider"

# Run database migrations
php artisan migrate

# Add Sanctum middleware to API routes in app/Http/Kernel.php
'api' => [
    \Laravel\Sanctum\Http\Middleware\EnsureFrontendRequestsAreStateful::class,
    'throttle:api',
    \Illuminate\Routing\Middleware\SubstituteBindings::class,
],

User Model Configuration

Update your User model to use the HasApiTokens trait provided by Sanctum.

User.php Model

<?php

namespace App\Models;

use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Foundation\Auth\User as Authenticatable;
use Illuminate\Notifications\Notifiable;
use Laravel\Sanctum\HasApiTokens;

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

    // Custom token creation with abilities
    public function createApiToken(string $name, array $abilities = ['*'])
    {
        return $this->createToken($name, $abilities)->plainTextToken;
    }

    // Get all active API tokens
    public function getActiveTokens()
    {
        return $this->tokens()->where('last_used_at', '>', now()->subDays(30))->get();
    }
}

Building API Authentication Endpoints

Registration & Login Controllers

Create authentication endpoints for user registration, login, and token management.

AuthController.php

<?php

namespace App\Http\Controllers\Api;

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

class AuthController extends Controller
{
    // User Registration
    public function register(Request $request)
    {
        $validator = Validator::make($request->all(), [
            'name' => 'required|string|max:255',
            'email' => 'required|string|email|max:255|unique:users',
            'password' => 'required|string|min:8|confirmed',
        ]);

        if ($validator->fails()) {
            return response()->json([
                'message' => 'Validation failed',
                'errors' => $validator->errors()
            ], 422);
        }

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

        $token = $user->createApiToken('api-token');

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

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

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

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

        $token = $user->createApiToken('login-token');

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

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

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

    // Get Current User
    public function user(Request $request)
    {
        return response()->json($request->user());
    }
}

API Routes Configuration

Define your API routes with proper authentication middleware.

api.php (Routes)

<?php

use Illuminate\Support\Facades\Route;
use App\Http\Controllers\Api\AuthController;
use App\Http\Controllers\Api\PostController;

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

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

    // Token management
    Route::get('/tokens', function (Request $request) {
        return $request->user()->tokens;
    });

    // CRUD Resources (RESTful API endpoints)
    Route::apiResource('posts', PostController::class);
    Route::apiResource('categories', CategoryController::class);

    // Custom routes with additional middleware
    Route::middleware(['can:admin'])->group(function () {
        Route::get('/admin/stats', AdminController::class);
        Route::apiResource('users', UserController::class)->except(['store']);
    });
});

// Public API endpoints (read-only)
Route::get('/public/posts', [PostController::class, 'index']);
Route::get('/public/posts/{id}', [PostController::class, 'show']);

Advanced Sanctum Features

Token Abilities & Scopes

Sanctum allows you to assign abilities (scopes) to tokens for fine-grained access control.

TokenAbilitiesController.php

<?php

namespace App\Http\Controllers\Api;

use Illuminate\Http\Request;
use App\Models\User;

class TokenAbilitiesController extends Controller
{
    // Create token with specific abilities
    public function createTokenWithAbilities(Request $request)
    {
        $request->validate([
            'name' => 'required|string',
            'abilities' => 'required|array'
        ]);

        $user = $request->user();
        $token = $user->createToken($request->name, $request->abilities)->plainTextToken;

        return response()->json([
            'message' => 'Token created with abilities',
            'token' => $token,
            'abilities' => $request->abilities
        ]);
    }

    // Route protected by token ability middleware
    public function adminOnly(Request $request)
    {
        return response()->json([
            'message' => 'Admin access granted',
            'user' => $request->user()
        ]);
    }
}

Middleware for Token Abilities

Create custom middleware to protect routes based on token abilities.


Testing & Security Best Practices

API Testing with Sanctum

Learn how to properly test your Sanctum-protected APIs.

AuthTest.php

<?php

namespace Tests\Feature\Api;

use Tests\TestCase;
use App\Models\User;
use Laravel\Sanctum\Sanctum;

class AuthTest extends TestCase
{
    // Test user registration
    public function test_user_can_register()
    {
        $response = $this->postJson('/api/register', [
            'name' => 'Test User',
            'email' => 'test@example.com',
            'password' => 'password123',
            'password_confirmation' => 'password123'
        ]);

        $response->assertStatus(201)
            ->assertJsonStructure([
                'message',
                'user' => ['id', 'name', 'email'],
                'token'
            ]);
    }

    // Test protected route access
    public function test_protected_route_requires_authentication()
    {
        // Attempt to access protected route without token
        $response = $this->getJson('/api/user');
        $response->assertStatus(401);
    }

    // Test authenticated route access
    public function test_authenticated_user_can_access_protected_route()
    {
        // Create and authenticate a user
        $user = User::factory()->create();
        Sanctum::actingAs($user);

        $response = $this->getJson('/api/user');
        $response->assertStatus(200)
            ->assertJson([
                'id' => $user->id,
                'name' => $user->name,
                'email' => $user->email
            ]);
    }

    // Test token abilities
    public function test_token_with_abilities()
    {
        $user = User::factory()->create();
        $token = $user->createToken('test-token', ['read', 'write']);

        $response = $this->withToken($token->plainTextToken)
            ->getJson('/api/user');

        $response->assertStatus(200);
    }
}

Conclusion

Laravel Sanctum provides an elegant solution for API authentication in modern Laravel applications.

Whether you’re building a simple token-based API, a SPA with session authentication, or a mobile app backend, Sanctum offers the flexibility and security you need. Its lightweight nature compared to Passport makes it ideal for most projects while maintaining robust security features.

Key Takeaways:

  • Sanctum is perfect for simple API token authentication and SPA authentication
  • Use token abilities for fine-grained access control in your APIs
Home » Building RESTful APIs with Laravel Sanctum
What is Laravel Sanctum and when should I use it?

Laravel Sanctum is a lightweight authentication system for SPAs, mobile applications, and simple token-based APIs. Use it when you need a simpler alternative to Passport for API authentication without the complexity of OAuth2.

How does Sanctum differ from Laravel Passport?

Sanctum is lightweight and designed for simple token-based APIs, while Passport provides full OAuth2 implementation. Sanctum is easier to set up and maintain for most projects, while Passport is better for complex OAuth2 requirements.

Can I use Sanctum for both API tokens and SPA authentication?
Yes! Sanctum uniquely supports both token-based API authentication and traditional session-based authentication for SPAs, making it versatile for different application types.

Leave a Reply

Your email address will not be published. Required fields are marked *