Laravel Sanctum cung cấp cơ chế xác thực nhẹ cho API token, SPA và các ứng dụng mobile.
Ở Bài 41, chúng ta đã xây dựng REST API.
Ở Bài 42, chúng ta dùng API Resource để kiểm soát dữ liệu JSON.
Nhưng API hiện tại có một vấn đề:
GET /api/posts
POST /api/posts
PUT /api/posts/1
DELETE /api/posts/1
Ai cũng có thể gửi request.
Ví dụ:
Anonymous User
↓
POST /api/posts
↓
Tạo bài viết?
Đây rõ ràng không phải điều chúng ta muốn.
Chúng ta cần biết:
Ai đang gọi API?
Và:
Người đó có được phép gọi API này không?
Đó là lúc Laravel Sanctum xuất hiện.
1. Laravel Sanctum là gì?
Sanctum là cơ chế xác thực nhẹ được Laravel cung cấp cho:
API token
SPA
Mobile application
Các API đơn giản cần token authentication
Sanctum cho phép user tạo nhiều personal access token và dùng token đó để xác thực các request API.
Có thể hình dung:
Client
│
│ Authorization: Bearer TOKEN
↓
Laravel API
│
↓
Sanctum
│
├── Token hợp lệ?
│
└── User nào?
↓
Controller
Nếu token hợp lệ:
Request
↓
Authenticated User
↓
Controller
Nếu không hợp lệ:
Request
↓
401 Unauthorized
2. Sanctum không phải là Authorization
Đây là điểm rất quan trọng.
Authentication:
Bạn là ai?
Authorization:
Bạn có quyền làm gì?
Ví dụ:
Authentication
↓
User ID = 10
Sau đó:
Authorization
↓
User 10 có được xóa Post 5 không?
Sanctum chủ yếu giải quyết vấn đề:
Authentication
Còn:
Authorization
Policies
Gates
Roles
Permissions
là những phần khác của Laravel.
3. Sanctum hoạt động như thế nào?
Ví dụ user đăng nhập và nhận token:
User
↓
Login
↓
Sanctum
↓
API Token
Ví dụ token:
1|abc123xyz...
Client lưu token.
Sau đó gửi:
Authorization: Bearer 1|abc123xyz...
Laravel nhận request:
Request
↓
Sanctum
↓
Kiểm tra token
↓
User
Nếu hợp lệ:
$request->user()
sẽ trả về user đang đăng nhập.
4. Cài Sanctum trong Laravel 13
Với Laravel hiện đại, cách đơn giản để cài API routing và Sanctum là:
php artisan install:api
Lệnh này được Laravel sử dụng để thiết lập API routing và Sanctum.
Chạy:
php artisan install:api
Sau đó chạy migration:
php artisan migrate
5. Kiểm tra project
Sau khi cài đặt, kiểm tra:
routes/
├── web.php
├── api.php
└── console.php
API route:
routes/api.php
Sanctum sử dụng database để quản lý personal access tokens.
Trong database sẽ có bảng:
personal_access_tokens
Bảng này được Sanctum dùng để lưu thông tin token đã được cấp.
6. User Model cần HasApiTokens
Mở:
app/Models/User.php
Thêm:
use Laravel\Sanctum\HasApiTokens;
Sau đó:
class User extends Authenticatable
{
use HasApiTokens, HasFactory, Notifiable;
// ...
}
Trong project thực tế, User Model của chúng ta có thể đang sử dụng nhiều trait khác nhau.
Chỉ cần bổ sung:
HasApiTokens
vào danh sách trait.
Sanctum cung cấp trait này để User có thể tạo và quản lý API tokens.
7. Tạo API Token
Sanctum cung cấp:
createToken()
Ví dụ:
$token = $user->createToken('blog-api');
Token được tạo ra có thể lấy bằng:
$token->plainTextToken
Ví dụ:
return [
'token' => $token->plainTextToken
];
Sanctum chỉ cung cấp giá trị plaintext của token ngay lúc token được tạo; token được lưu dưới dạng hash trong database. Vì vậy giá trị plaintext phải được bảo vệ và gửi cho client tại thời điểm cấp token.
8. Tạo API Token bằng Route
Để học Sanctum, chúng ta có thể tạo một route demo.
routes/api.php:
<?php
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Route;
Route::post('/tokens/create', function (Request $request) {
$token = $request->user()->createToken(
$request->token_name
);
return [
'token' => $token->plainTextToken
];
});
Nhưng có một vấn đề:
$request->user()
cần có authenticated user.
Do đó trong project thật, chúng ta cần thiết kế endpoint cấp token dựa trên thông tin đăng nhập của user.
Phần login API hoàn chỉnh sẽ được xử lý ở Bài 44 — API Authentication.
Ở bài này, chúng ta tập trung hiểu cơ chế Sanctum.
9. Token là gì?
Token có thể hiểu đơn giản là:
Một chuỗi bí mật đại diện cho quyền truy cập API của một user.
Ví dụ:
1|K7x9fL2mQ...
Client không cần gửi:
email
password
ở mọi request.
Thay vào đó:
Authorization: Bearer 1|K7x9fL2mQ...
Laravel dùng token để xác định user.
10. Bearer Token
Đây là format phổ biến:
Authorization: Bearer TOKEN
Ví dụ:
Authorization: Bearer 1|K7x9fL2mQ...
Có thể hình dung:
Authorization
│
├── Bearer
│
└── Token
Bearer cho biết request đang sử dụng một bearer token để xác thực.
11. Bảo vệ API bằng auth:sanctum
Đây là phần quan trọng nhất.
Ví dụ API:
Route::get('/user', function (Request $request) {
return $request->user();
})->middleware('auth:sanctum');
Middleware:
auth:sanctum
sẽ yêu cầu request phải được Sanctum xác thực. Laravel tài liệu hướng dẫn sử dụng middleware này để bảo vệ API routes.
12. API không có token
Client gọi:
GET /api/user
nhưng không gửi:
Authorization: Bearer ...
Laravel sẽ không xác thực được user.
Kết quả thường là:
401 Unauthorized
Có thể hình dung:
GET /api/user
↓
auth:sanctum
↓
Token?
NO ↓
401
13. API có token
Client gửi:
GET /api/user
Authorization: Bearer TOKEN
Sanctum kiểm tra:
Token
↓
personal_access_tokens
↓
Hợp lệ?
↓
User
Sau đó:
$request->user()
sẽ trả về user đã được xác thực.
14. Tạo endpoint /api/user
Trong:
routes/api.php
có thể viết:
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Route;
Route::get('/user', function (Request $request) {
return $request->user();
})->middleware('auth:sanctum');
Request:
GET /api/user
Nếu token hợp lệ:
{
"id": 1,
"name": "Admin",
"email": "admin@example.com"
}
15. Không nên trả User Model trực tiếp trong project thực tế
Ở Bài 42 chúng ta đã học API Resource.
Do đó thay vì:
return $request->user();
chúng ta có thể sử dụng:
return new UserResource(
$request->user()
);
Ví dụ:
use App\Http\Resources\UserResource;
use Illuminate\Http\Request;
Route::get('/user', function (Request $request) {
return new UserResource(
$request->user()
);
})->middleware('auth:sanctum');
Luồng:
Sanctum
↓
Authenticated User
↓
UserResource
↓
JSON
Đây chính là sự kết hợp giữa:
Bài 42
API Resource
+
Bài 43
Sanctum
16. Bảo vệ Posts API
Ở Bài 41 chúng ta có:
Route::apiResource('posts', PostController::class);
Nhưng nếu toàn bộ route đều public thì:
POST
PUT
DELETE
cũng có thể bị gọi bởi người chưa đăng nhập.
Chúng ta có thể tách route.
Ví dụ:
Route::get('/posts', [PostController::class, 'index']);
Route::get('/posts/{post}', [PostController::class, 'show']);
Route::middleware('auth:sanctum')->group(function () {
Route::post('/posts', [PostController::class, 'store']);
Route::put('/posts/{post}', [PostController::class, 'update']);
Route::patch('/posts/{post}', [PostController::class, 'update']);
Route::delete('/posts/{post}', [PostController::class, 'destroy']);
});
Khi đó:
GET /api/posts
có thể public.
Nhưng:
POST /api/posts
PUT /api/posts/1
DELETE /api/posts/1
cần authentication.
17. Có thể viết bằng apiResource
Một cách khác:
Route::apiResource('posts', PostController::class)
->only(['index', 'show']);
Route::apiResource('posts', PostController::class)
->except(['index', 'show'])
->middleware('auth:sanctum');
Tuy nhiên với người mới học, cách chia route thành hai nhóm thường dễ nhìn hơn:
Route::get(...);
Route::get(...);
Route::middleware('auth:sanctum')->group(function () {
// private API
});
18. Public API và Private API
Chúng ta có thể phân chia:
PUBLIC API
GET /api/posts
GET /api/posts/1
GET /api/categories
và:
PRIVATE API
POST /api/posts
PUT /api/posts/1
DELETE /api/posts/1
Private API yêu cầu:
Authentication
Có thể hình dung:
API
│
┌─────────┴─────────┐
│ │
PUBLIC PRIVATE
│ │
GET POST
GET PUT
DELETE
│
auth:sanctum
19. Sanctum Token không phải Password
Một lỗi phổ biến là nhầm:
password
với:
API token
Password dùng để xác thực user trong quá trình đăng nhập.
Token được cấp sau đó có thể được client dùng cho các request API.
Ví dụ:
Email + Password
↓
Authentication
↓
Sanctum Token
↓
API Requests
Không nên đưa password vào mỗi API request.
20. Token có thể có tên
Khi tạo token:
$user->createToken('blog-api');
Tên:
blog-api
chỉ là tên giúp nhận biết token.
Ví dụ user có:
blog-web
iphone
android
desktop
Các token này có thể thuộc cùng một user.
Sanctum hỗ trợ nhiều token trên một user và cho phép quản lý chúng qua relationship tokens.
21. Xem các token của User
User Model sử dụng:
HasApiTokens
thì có thể truy cập:
$user->tokens
Ví dụ:
foreach ($user->tokens as $token) {
// ...
}
Có thể hình dung:
User
│
├── Token 1
├── Token 2
└── Token 3
Điều này rất hữu ích khi ứng dụng cho phép user đăng nhập từ nhiều thiết bị.
22. Thu hồi một Token
Token có thể bị revoke bằng cách xóa token khỏi database.
Ví dụ token hiện tại:
$request->user()
->currentAccessToken()
->delete();
Điều này có thể dùng cho:
POST /api/logout
Ví dụ:
Route::post('/logout', function (Request $request) {
$request->user()
->currentAccessToken()
->delete();
return response()->json([
'message' => 'Logged out successfully'
]);
})->middleware('auth:sanctum');
Sau khi token bị xóa:
Token cũ
↓
Không còn hợp lệ
Sanctum hỗ trợ revoke token thông qua relationship tokens hoặc currentAccessToken().
23. Thu hồi tất cả Token
Ví dụ user chọn:
Đăng xuất khỏi tất cả thiết bị.
Có thể:
$request->user()
->tokens()
->delete();
Khi đó:
Token Web
Token Android
Token iPhone
Token Desktop
đều bị revoke.
24. Token Abilities
Sanctum còn hỗ trợ abilities cho token.
Ví dụ tạo token:
$token = $user->createToken(
'mobile',
['posts:read']
);
Token này có ability:
posts:read
Có thể tạo token khác:
$user->createToken(
'admin',
[
'posts:read',
'posts:create',
'posts:update',
'posts:delete'
]
);
Khi đó có thể phân biệt:
Read-only token
và:
Full-access token
25. Kiểm tra Ability
Trong request:
if ($request->user()->tokenCan('posts:read')) {
// ...
}
Ví dụ:
if (! $request->user()->tokenCan('posts:delete')) {
abort(403);
}
Luồng:
User
↓
Token
↓
Abilities
↓
posts:delete?
↓
YES / NO
Đây là một bước tiến từ:
Authentication
sang:
Authorization
Tuy nhiên project Blog CMS của chúng ta đã có:
role
Policy
nên trong dự án thực tế cần thiết kế hai tầng này một cách rõ ràng, thay vì nhồi mọi permission vào token.
26. Token Expiration
Theo mặc định, Sanctum token không tự hết hạn và có thể bị vô hiệu hóa bằng cách revoke token. Sanctum có tùy chọn cấu hình thời gian hết hạn nếu ứng dụng cần.
Ví dụ ý tưởng:
Token
↓
Expiration
↓
Hết hạn
↓
Không sử dụng được
Đây là một điểm cần quan tâm khi xây dựng API production.
27. Sanctum và SPA
Sanctum không chỉ có API token.
Sanctum còn hỗ trợ authentication cho first-party SPA bằng cookie/session.
Đây là điểm rất quan trọng:
API Token
và:
SPA Authentication
là hai cơ chế khác nhau trong Sanctum.
Với SPA của chính bạn, Sanctum có thể sử dụng session cookie thay vì personal access token. Tài liệu Laravel cũng lưu ý không nên dùng API token như cơ chế xác thực cho first-party SPA nếu SPA authentication của Sanctum phù hợp hơn.
28. API Token và SPA Cookie khác nhau
API Token
Mobile App
↓
Bearer Token
↓
Laravel
↓
Sanctum
Phù hợp với:
Mobile
Third-party API
Personal API token
SPA Authentication
React / Vue SPA
↓
Session Cookie
↓
Laravel
↓
Sanctum
Phù hợp với:
First-party SPA
Không nên học hai cơ chế này như thể chúng là cùng một thứ.
29. statefulApi()
Nếu sử dụng Sanctum cho first-party SPA, Laravel có middleware hỗ trợ stateful API.
Trong bootstrap/app.php có thể cấu hình:
->withMiddleware(function (Middleware $middleware): void {
$middleware->statefulApi();
})
Cơ chế này cho phép các request từ SPA của bạn được xác thực bằng session cookie, trong khi Sanctum vẫn có thể xác thực API token cho các request thích hợp.
Phần này chúng ta chỉ cần biết ở Bài 43.
Không cần triển khai SPA authentication đầy đủ ở đây.
30. Sanctum trong Blog CMS
Project của chúng ta hiện có:
users
categories
posts
và:
role
is_active
API có thể thiết kế:
PUBLIC
GET /api/posts
GET /api/posts/1
GET /api/categories
PRIVATE:
POST /api/posts
PUT /api/posts/1
DELETE /api/posts/1
Sau Sanctum:
Client
↓
Bearer Token
↓
Sanctum
↓
User
↓
Controller
↓
Policy
↓
Post
Đây mới là kiến trúc hợp lý.
31. Sanctum không thay thế Policy
Ví dụ:
User #10
đã đăng nhập thành công.
Sanctum xác nhận:
User #10 là người đang gọi API.
Nhưng User #10 có được sửa Post #100 hay không?
Đó là câu hỏi khác.
Ta cần:
Policy
Ví dụ:
Sanctum
↓
Authentication
↓
User #10
↓
PostPolicy
↓
Được sửa?
Vì vậy:
Sanctum ≠ Permission
32. auth:sanctum + Policy
Ví dụ:
Route::put('/posts/{post}', [
PostController::class,
'update'
])
->middleware('auth:sanctum');
Controller:
public function update(Request $request, Post $post)
{
$this->authorize('update', $post);
// ...
}
Luồng:
Request
↓
auth:sanctum
↓
User authenticated?
↓
YES
↓
Policy
↓
Có quyền update?
↓
YES
↓
Update Post
Đây là kiến trúc authentication + authorization rất quan trọng.
33. Test Sanctum bằng Postman
Sau khi có token:
TOKEN = 1|abc123...
Trong Postman:
Authorization
chọn:
Bearer Token
nhập:
1|abc123...
Postman sẽ gửi:
Authorization: Bearer 1|abc123...
Sau đó gọi:
GET /api/user
Nếu token hợp lệ:
200 OK
Nếu token không hợp lệ:
401 Unauthorized
34. Không gửi Token trong URL
Không nên:
/api/posts?token=abc123
Không nên:
/api/user/abc123
Thay vào đó:
Authorization: Bearer TOKEN
Token là thông tin nhạy cảm.
Không nên đưa token vào:
URL
HTML
log
screenshot
Git repository
35. Không commit token vào Git
Ví dụ tuyệt đối không viết:
$token = '1|abc123...';
rồi commit lên Git.
Không nên lưu token thật trong:
source code
hoặc:
README
hoặc:
.env.example
Token thật phải được bảo vệ.
Nếu token bị lộ:
Revoke token
↓
Tạo token mới
36. Một ví dụ hoàn chỉnh
routes/api.php:
<?php
use App\Http\Controllers\Api\PostController;
use App\Http\Resources\UserResource;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Route;
Route::get('/user', function (Request $request) {
return new UserResource($request->user());
})->middleware('auth:sanctum');
Route::get('/posts', [
PostController::class,
'index'
]);
Route::get('/posts/{post}', [
PostController::class,
'show'
]);
Route::middleware('auth:sanctum')->group(function () {
Route::post('/posts', [
PostController::class,
'store'
]);
Route::put('/posts/{post}', [
PostController::class,
'update'
]);
Route::patch('/posts/{post}', [
PostController::class,
'update'
]);
Route::delete('/posts/{post}', [
PostController::class,
'destroy'
]);
Route::post('/logout', function (Request $request) {
$request->user()
->currentAccessToken()
->delete();
return response()->json([
'message' => 'Logged out successfully'
]);
});
});
Kiến trúc:
PUBLIC
│
├── GET /api/posts
└── GET /api/posts/{post}
PRIVATE
│
├── GET /api/user
├── POST /api/posts
├── PUT /api/posts/{post}
├── PATCH /api/posts/{post}
├── DELETE /api/posts/{post}
└── POST /api/logout
37. Kiểm tra route
Chạy:
php artisan route:list --path=api
Có thể thấy:
GET|HEAD api/posts
POST api/posts
GET|HEAD api/posts/{post}
PUT api/posts/{post}
PATCH api/posts/{post}
DELETE api/posts/{post}
GET|HEAD api/user
POST api/logout
Các route private sẽ có middleware:
auth:sanctum
38. Luồng Authentication hoàn chỉnh
Hiện tại chúng ta có thể hình dung:
USER
│
▼
Login API
│
▼
Sanctum Token
│
▼
┌─────────────────┐
│ API Client │
└─────────────────┘
│
│ Bearer Token
▼
Laravel API
│
▼
auth:sanctum
│
┌────────┴────────┐
│ │
Invalid Valid
│ │
▼ ▼
401 User
│
▼
Policy
│
▼
Controller
│
▼
Model
│
▼
Resource
│
▼
JSON
Đây là kiến trúc mà chúng ta sẽ hoàn thiện ở Bài 44.
39. Bài tập thực hành
Bài tập 1 — Cài Sanctum
Chạy:
php artisan install:api
Sau đó:
php artisan migrate
Bài tập 2 — User Model
Thêm:
use Laravel\Sanctum\HasApiTokens;
và:
use HasApiTokens, HasFactory, Notifiable;
Bài tập 3 — Protected API
Tạo:
GET /api/user
với:
->middleware('auth:sanctum')
Bài tập 4 — Test không có Token
Gọi:
GET /api/user
Không gửi Authorization.
Quan sát:
401 Unauthorized
Bài tập 5 — Test bằng Bearer Token
Gửi:
Authorization: Bearer YOUR_TOKEN
Sau đó gọi:
GET /api/user
Quan sát user trả về.
Bài tập 6 — Logout
Tạo:
POST /api/logout
Sau khi logout:
Token cũ
↓
Không còn hợp lệ
Bài tập 7 — Bảo vệ Post API
Public:
GET /api/posts
GET /api/posts/1
Private:
POST /api/posts
PUT /api/posts/1
DELETE /api/posts/1
40. Những điều cần nhớ
Sanctum dùng để làm gì?
API Authentication
SPA Authentication
Mobile Authentication
API Tokens
User Model:
use Laravel\Sanctum\HasApiTokens;
Tạo token:
$user->createToken('blog-api');
Lấy token plaintext:
$token->plainTextToken
Gửi token:
Authorization: Bearer TOKEN
Bảo vệ route:
->middleware('auth:sanctum')
User hiện tại:
$request->user()
Token hiện tại:
$request->user()->currentAccessToken()
Revoke token:
$request->user()
->currentAccessToken()
->delete();
Revoke tất cả token:
$request->user()
->tokens()
->delete();
41. Authentication ≠ Authorization
Hãy nhớ thật kỹ:
SANCTUM
↓
Authentication
↓
"Bạn là ai?"
Còn:
POLICY / GATE / PERMISSION
↓
Authorization
↓
"Bạn được phép làm gì?"
Ví dụ:
User đăng nhập
↓
Sanctum
↓
User #5
↓
Policy
↓
User #5 có được xóa Post #10?
Hai vấn đề này phải được xử lý riêng.
🎯 Tổng kết Bài 43
Sau Bài 41:
REST API
Sau Bài 42:
API Resource
Bây giờ Bài 43:
Sanctum
Chúng ta đã xây dựng được:
Laravel 13 API
│
┌───────┴───────┐
│ │
Public Private
│ │
GET API auth:sanctum
│
Token
│
User
API bây giờ không còn là:
Ai cũng gọi được
mà đã có:
Client
↓
Bearer Token
↓
Sanctum
↓
Authenticated User
Tuy nhiên chúng ta vẫn còn một mảnh ghép rất quan trọng:
User lấy token bằng cách nào?
Chúng ta cần một API:
POST /api/login
Client gửi:
{
"email": "admin@example.com",
"password": "password"
}
Laravel kiểm tra:
Email
+
Password
Nếu chính xác:
↓
Sanctum
↓
API Token
↓
Client
Sau đó client dùng token để gọi:
GET /api/user
GET /api/posts
POST /api/posts
PUT /api/posts/1
DELETE /api/posts/1
Đó chính là nội dung tiếp theo:
🔥 BÀI 44 — API AUTHENTICATION
Chúng ta sẽ hoàn thiện:
Register API
Login API
Logout API
Bearer Token
auth:sanctum
User API
Validation
401 Unauthorized
403 Forbidden
Token revoke
API Resource
và ghép tất cả thành một API Authentication hoàn chỉnh cho Blog CMS Laravel 13.




Không có nhận xét nào:
Đăng nhận xét