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
- Understanding Sanctum's Features
- Getting Started with Sanctum
- API Token Authentication
- SPA Authentication
- Mobile App Authentication
- Advanced Sanctum Configuration
- Best Practices for Sanctum Implementation
- Testing Authentication with Sanctum
- Real-world Sanctum Implementation Example
- Conclusion
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:
- API Token Authentication: Issue API tokens that can be used to authenticate API requests
- SPA Authentication: Cookie-based session authentication for single-page applications
- 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:
- A token ID stored in your database
- 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
- Your SPA makes a request to the
/sanctum/csrf-cookie
endpoint to get a CSRF token - Your SPA then makes a login request with credentials and the CSRF token
- If the credentials are correct, Laravel creates a session
- 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:
- Make a request to this login endpoint with credentials and a device name
- Store the returned token securely in the mobile app
- 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
- Use HTTPS: Always use HTTPS in production to prevent token interception
- Token Storage: Store tokens securely, especially on the client side
- Token Abilities: Limit token abilities to only what's needed
- Regular Cleanup: Implement a routine to clean up expired tokens
- Rate Limiting: Apply rate limiting to authentication endpoints
Performance Optimizations
- Database Indexing: Ensure proper indexing on token tables for large applications
- Token Pruning: Regularly delete old, unused tokens
- 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.
Add Comment
No comments yet. Be the first to comment!