Have you ever wondered whether the API we have created has sufficient security to protect it from cyber attacks? In fact, many novice developers overlook an important aspect after successfully creating an API, namely how to protect APIs that store sensitive data from leaks. The way to address this issue is to implement token-based authentication for APIs in the applications we create, one of which is using Laravel Sanctum.
Laravel Sanctum provides a lightweight authentication system for SPAs (Single Page Applications), mobile applications, and simple APIs. This article will discuss the definition and features of Laravel Sanctum, as well as a guide to implementing secure API authentication in Laravel.
What is Laravel Sanctum
Laravel Sanctum is an official package or library that offers token-based authentication for Laravel applications. Sanctum is designed for Single Page Applications, Mobile Apps, and Simple APIs without using more complex systems such as Laravel Passport (OAuth2). Sanctum operates using two approaches:
- API Tokens - for applications requiring classic token-based authentication.
- Cookie-based SPA Authentication - for SPAs running on the same domain, using secure session cookies. However, in this discussion, we will use API Authentication Tokens.
Main Features of Laravel Sanctum
Laravel Sanctum has several outstanding features, namely:
- API Authentication Tokens
- SPA Authentication
- Mobile App Authentication Support
- Token Abilities / Scope Tokens
- Token Expiration
- Easy to Use and Lightweight
Preparation
Before implementing Laravel Sanctum in your Laravel application, make sure you have:
- Laravel 12.x already installed.
- PHP 8.2 or higher.
- Composer.
- A basic understanding of Laravel.
- A database (MySQL, PostgreSQL, etc.).
Install Laravel Sanctum
Open the terminal and copy the script below.
composer require laravel/sanctum
Publish the Sanctum configuration.
php artisan vendor:publish --provider=“Laravel\Sanctum\SanctumServiceProvider”
Run the migration
php artisan migrate
Laravel Sanctum Configuration
- Config Middleware
Open the bootstrap/app.php
file and add the code below.
$middleware->statefulApi();
and
api: __DIR__.‘/../routes/api.php’,
so that the full code for the app.php
file becomes like this.
return Application::configure(basePath: dirname(__DIR__))
->withProviders()
->withRouting(
web: __DIR__.‘/../routes/web.php’,
api: __DIR__.‘/../routes/api.php’,
commands: __DIR__.‘/../routes/console.php’,
health: ‘/up’,
)
->withMiddleware(function (Middleware $middleware) {
$middleware->statefulApi();
})
->withExceptions(function (Exceptions $exceptions) {
})->create();
- CORS
Paste the following command into the terminal to publish the cors.php
configuration.
php artisan config:publish cors
Update the config/cors.php
file to allow credentials.
‘supports_credentials’ => true,
- Set Expired Token
Open the config/sanctum.php
file to set the expired token in minutes.
‘expiration’ => 60 //1 hour
Implementation
- User Model Configuration
Open the file app/Models/User.php
and add the trait HasApiTokens
as shown below.
<?php
namespace App\Models;
use Laravel\Sanctum\HasApiTokens;
class User extends Authenticatable
{
use HasFactory, Notifiable, HasApiTokens;
}
- Controller Configuration
Create a controller named AuthController
to handle the authentication process.
php artisan make:controller Api/AuthController
Open the AuthController
file and paste the code below.
<?php
namespace App\Http\Controllers\Api;
use App\Http\Controllers\Controller;
use App\Models\User;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Hash;
use Illuminate\Validation\ValidationException;
class AuthController extends Controller
{
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),
]);
$token = $user->createToken('auth-token')->plainTextToken;
return response()->json([
'user' => $user,
'message' => 'registration success'
], 201);
}
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)) {
throw ValidationException::withMessages([
'email' => ['invalid credentials.'],
]);
}
$token = $user->createToken('auth-token')->plainTextToken;
return response()->json([
'access_token' => $token,
'token_type' => 'Bearer',
'user' => $user,
'message' => 'login success'
]);
}
public function logout(Request $request)
{
$request->user()->currentAccessToken()->delete();
return response()->json([
'message' => 'logout success'
]);
}
public function user(Request $request)
{
return response()->json([
'user' => $request->user(),
'message' => 'user data retrieved successfully'
]);
}
}
Let's discuss some parts of the code above.
$token = $user->createToken(‘auth-token’)->plainTextToken;
The createToken()
function is a method from the HasApiTokens
trait to generate a token in the form of a plainTextToken
.
$request->user()->currentAccessToken()->delete();
Deletes the token that has been generated and used by the user.
- Update Route API
Create a file named api.php
inside the routes/
folder, then paste the code below.
<?php
use App\Http\Controllers\Api\AuthController;
use Illuminate\Support\Facades\Route;
Route::post('/register', [AuthController::class, 'register']);
Route::post('/login', [AuthController::class, 'login']);
Route::middleware('auth:sanctum')->group(function () {
Route::post('/logout', [AuthController::class, 'logout']);
Route::get('/get-user', [AuthController::class, 'user']);
});
We define middleware(‘auth:sanctum’)
within a route group, which means that all routes within that group will be protected by auth:sanctum
.
Testing the API Using Postman
We use the Postman tool to test the API we have created.
- Register User
- POST
/api/register
- Body
{“name”: “NganggurDev”, “email”: “bussines.nganggurdev@gmail.com”, “password”: “admin123”, ‘password_confirmation’: “admin123”}
- Login
- POST
/api/login
- Body
{"email": "bussines.nganggurdev@gmail.com", "password": "admin123"}
- Access Protected Route
- GET
/api/get-user
- Header:
Authorization: Bearer <access token>
User Has a Bearer Token (Authenticated)
Unauthenticated
4. Logout
- POST
/api/logout
- Header:
Authorization: Bearer <access token>
Common Problems
- CORS Problem
Solution: Make sure the CORS
configuration is correct.
- 401 Unauthorized
Solution: Check if the Authorization
header is formatted correctly: Bearer {token}.
- Session with SPA
Solution: Ensure to call api /sanctum/csrf-cookie
before making an authenticated request in SPA.
- Token Not Expiring as Expected
Solution: Configure the expiration time in config/sanctum.php
and implement custom expiration for specific tokens if needed.
- Duplicate Tokens for the Same User
Solution: Implement a strategy to revoke old tokens upon new login or limit the maximum number of active tokens per user.
Conclusion
Laravel Sanctum provides a solution for securing sensitive data in modern web applications. By following this guide, we have learned the basics of implementing secure API authentication in Laravel 12.
Thank you.