Sanctum tutorial - Token Authentication in Laravel 11
Last update: 06-15-2024
In this blog post, we'll explore how to create a RESTful API in Laravel 11 for a mobile app frontend that will consume this API using token authentication with Sanctum.
Mobile App Authentication vs. SPA Authentication
As a rule of thumb, using an authentication token is generally acceptable for mobile applications. Authentication tokens can also be used in SPAs (single-page applications) but only if additional security measures are applied.
When developing a mobile app, you generally have more control on what your users can and cannot do as compared to your SPA users (such as React or Angular). This allows for a somewhat more straightforward process when it comes to authentication. Browsers give full control of the client side to the user, requiring meticulous attention to prevent easy access to sensitive data.
One significant difference is secure storage mechanisms available in mobile operating systems like Keychain on iOS and Keystore on Android. Sensitive information can be stored there and cannot be accessed or viewed by users. Browsers expose all data sent to the client side to the end user, and while mitigations exist, such as HTTP-only cookies that prevent JavaScript access, they do not eliminate all risks. The potential attack surface is larger in web applications due to how data is handled and stored on the client side.
Given the more limited nature of mobile applications, the authentication process is more straightforward, and essentialy involves receiving a token from the server then using it in subsequent requests.
Token based authentication flow
[Step 1] 📱 → 🖥️
Mobile App sends authentication data to Server
[Step 2] 🖥️ ↔ 🖥️
Server validates authentication data
[Step 3] 🖥️ ← 📱
Server sends back an authentication token to Mobile App
[Step 4] 📱 → 🖥️
Mobile App sends a request with the token as a bearer
[Step 5] 🖥️ ↔ 🖥️
Server validates the token and processes the request
Steps 1-3 only happen once, then the same token can used for many requests (steps 4-5).
Sanctum - Mobile Authentication, the Laravel Way
Laravel Sanctum provides a simple and secure way to implement token-based authentication for mobile apps. While Sanctum also supports SPA authentication, this post focuses solely on token authentication. One of the benefits of Sanctum is its seamless integration with Laravel's broader authentication system, allowing easy access to user tokens via the User model.
Getting Started with Sanctum for Mobile App Token Authentication
First, create a new Laravel project:
composer create-project laravel/laravel token-auth-example
Then, install Sanctum in your Laravel project:
php artisan install:api
After installing Sanctum, the file "routes/api.php" will have been created.
api.php
You're probably familiar with routes/web.php
, which is the default file for defining routes. api.php
is created to handle requests from third-party apps and pages that do not originate directly from our server. There are two important differences between the routes in web.php
and api.php
:
-
Route Prefix: Routes in
api.php
are automatically prefixed with/api/
. For example, if we define the route/login
in this file, it needs to be accessed as/api/login
. -
CSRF Protection: In
api.php
, CSRF protection is not enforced for POST routes. When pages originate from the server, it's straightforward to inject the CSRF token into them, making it practical to expect CSRF tokens in the request's body. However, when working with a mobile app (where the pages do not originate from the server), injecting CSRF tokens is not feasible. This is not necessarily a problem, as CSRF attacks are primarily effective when cookies are involved and rely on the browser's automatic inclusion of cookies in requests. Bearer tokens, on the other hand, are not sent automatically and must be manually included in the request by the client, rendering CSRF attacks next to impossible.
Updating the User Model
To allow the User model access its tokens, we add the 'HasApiTokens' trait to it:
use Laravel\Sanctum\HasApiTokens;
class User extends Authenticatable
{
use HasApiTokens, HasFactory, Notifiable;
}
Run the migrations
We now need to run the migrations to create the updated tables:
php artisan migrate
Example Code
Below is a minimal example demonstrating user registration, login, a protected route, and token revocation. These routes should be added to your routes/api.php
file. You're invited to create a controller, of course, but to keep things simple, I gathered everything in one file:
<?php
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Route;
use Laravel\Sanctum\PersonalAccessToken;
use Illuminate\Support\Facades\Validator;
use Illuminate\Support\Facades\Hash;
use App\Models\User;
Route::post('/register', function (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',
]);
if ($validator->fails()) {
return response()->json(['errors' => $validator->errors()], 422);
}
User::create([
'name' => $request->name,
'email' => $request->email,
'password' => Hash::make($request->password),
]);
return response()->json(['message' => 'User registered successfully'], 201);
});
Route::post('/login', function (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->createToken('mobile_app_token')->plainTextToken;
return response()->json(['token' => $token], 200);
});
Route::get('/protected-data', function (Request $request) {
return response()->json(['message' => 'This text is only available to logged in users']);
})->middleware('auth:sanctum');
Route::get('revoke-token', function (Request $request) {
$token = $request->bearerToken();
if (!$token) {
return response()->json(['message' => 'No token provided'], 400);
}
$tokenInstance = PersonalAccessToken::findToken($token);
if ($tokenInstance) {
$tokenInstance->delete();
return response()->json(['message' => 'Token revoked'], 200);
}
return response()->json(['message' => 'Token not found'], 404);
});
Example HTTP Requests
To test this API using a tool like Postman, you can use the following HTTP requests:
Register a New User
POST /api/register
{
"name": "John Doe",
"email": "john@example.com",
"password": "password"
}
Login a user
POST /api/login
{
"email": "john@example.com",
"password": "password"
}
The login request returns a user, we're going to use it in our following request, so copy and paste it.
Access Protected Data
GET /api/protected-data
Authorization: Bearer your_generated_token
Revoke Token
GET /api/revoke-token
Authorization: Bearer your_generated_token