Laravel sanctum giúp quản lý và tạo các api token phục vụ auth cho single page applications(SPA). Sanctum cho phép mỗi người dùng tạo nhiều api token cho tài khoản của họ. Ứng dụng của bạn có thể phân quyền theo các token này.
Để cài đặt Laravel Sanctum bạn cần sử dụng composer
composer require laravel/sanctum
public laravel sanctum cho dự án của bạn
php artisan vendor:publish --provider="Laravel\Sanctum\SanctumServiceProvider"
Tạo table để quản lý các token đăng nhập
php artisan migrate
// Các token được quản lý trong bảng personal_access_tokens
mysql> desc personal_access_tokens;
+----------------+---------------------+------+-----+---------+----------------+
| Field | Type | Null | Key | Default | Extra |
+----------------+---------------------+------+-----+---------+----------------+
| id | bigint(20) unsigned | NO | PRI | NULL | auto_increment |
| tokenable_type | varchar(255) | NO | MUL | NULL | |
| tokenable_id | bigint(20) unsigned | NO | | NULL | |
| name | varchar(255) | NO | | NULL | |
| token | varchar(64) | NO | UNI | NULL | |
| abilities | text | YES | | NULL | |
| last_used_at | timestamp | YES | | NULL | |
| expires_at | timestamp | YES | | NULL | |
| created_at | timestamp | YES | | NULL | |
| updated_at | timestamp | YES | | NULL | |
+----------------+---------------------+------+-----+---------+----------------+
10 rows in set (0.01 sec)
Thêm auth config
'guards' => [
'web' => [
'driver' => 'session',
'provider' => 'users',
],
'api' => [
'driver' => 'sanctum',
'provider' => 'users',
'hash' => false,
],
],
Thêm middleware của laravel sanctum vào nhóm api
middleware.
'api' => [
\Laravel\Sanctum\Http\Middleware\EnsureFrontendRequestsAreStateful::class,
'throttle:api',
\Illuminate\Routing\Middleware\SubstituteBindings::class,
],
Thêm middleware add accept application/json to header khi sửa dụng api, giúp laravel sanctum có thể check được token invalid khi sử dụng api.
<?php
namespace App\Http\Middleware;
use Closure;
use Illuminate\Http\RedirectResponse;
use Illuminate\Http\Request;
use Illuminate\Http\Response;
/**
* Add accept to header
* the sanctum middleware
*/
class AddAcceptToHeader
{
/**
* Handle an incoming request.
*
* @param Request $request
* @param Closure(\Illuminate\Http\Request): (\Illuminate\Http\Response|\Illuminate\Http\RedirectResponse) $next
* @return Response|RedirectResponse
*/
public function handle(Request $request, Closure $next)
{
if ($request->is('*api*')) {
$request->headers->set('Accept', 'application/json');
}
return $next($request);
}
}
Thêm middleware vào nhóm api middleware
'api' => [
\App\Http\Middleware\AddAcceptToHeader::class,
\Laravel\Sanctum\Http\Middleware\EnsureFrontendRequestsAreStateful::class,
'throttle:api',
\Illuminate\Routing\Middleware\SubstituteBindings::class,
],
Như vậy là chúng ta đã cài đặt và thiết lập thành công laravel sanctum, giờ đến sử dụng nó.
Tạo route cho chức năng đăng kí, đăng nhập, user profile, logout.
<?php
use Illuminate\Support\Facades\Route;
use App\Http\Controllers\Api\V1\AuthController;
Route::group(['prefix' => 'v1'], function () {
Route::post('register', [AuthController::class, 'createUser']);
Route::post('login', [AuthController::class, 'login']);
Route::middleware(['auth:sanctum'])->group(function () {
Route::post('logout', [AuthController::class, 'logout']);
Route::get('profile', [AuthController::class, 'profile']);
});
});
Tạo AuthController đăng kí, đăng nhập, lấy thông tin user và logout.
<?php
namespace App\Http\Controllers\Api\V1;
use App\Exceptions\ApiException;
use App\Http\Controllers\Controller;
use App\Http\Requests\Auth\CreateUserRequest;
use App\Http\Requests\Auth\AuthRequest;
use App\Http\Resources\AuthResource;
use App\Http\Resources\Users\UserResource;
use App\Http\Resources\SuccessResource;
use Illuminate\Http\Request;
use App\Models\User;
use Illuminate\Support\Facades\Hash;
use Illuminate\Support\Facades\Auth;
class AuthController extends Controller
{
/**
* Register user
*
* @OA\Post(
* path="/api/v1/register",
* tags={"Auth"},
* @OA\RequestBody(
* required=true,
* @OA\JsonContent(
* ref="#/components/schemas/CreateUserRequest"
* )
* ),
* @OA\Response(
* response=200,
* description="Successful operation",
* @OA\JsonContent(ref="#/components/schemas/AuthResource")
* ),
* @OA\Response(
* response=401,
* description="Unauthenticated",
* ),
* @OA\Response(
* response=403,
* description="Forbidden"
* )
* )
* @param CreateUserRequest $request
* @return AuthResource
*/
public function createUser(CreateUserRequest $request): AuthResource
{
$user = User::create([
'name' => $request->name,
'email' => $request->email,
'password' => Hash::make($request->password)
]);
return new AuthResource($user);
}
/**
* User login
*
* @OA\Post(
* path="/api/v1/login",
* tags={"Auth"},
* @OA\RequestBody(
* required=true,
* @OA\JsonContent(
* ref="#/components/schemas/AuthRequest"
* )
* ),
* @OA\Response(
* response=200,
* description="Successful operation",
* @OA\JsonContent(ref="#/components/schemas/AuthResource")
* ),
* @OA\Response(
* response=401,
* description="Unauthenticated",
* ),
* @OA\Response(
* response=403,
* description="Forbidden"
* )
* )
*
* @param AuthRequest $request
* @return AuthResource
*/
public function login(AuthRequest $request): AuthResource
{
if (!Auth::attempt($request->only('email', 'password'))) {
throw ApiException::badRequest('username or password wrong');
}
$user = User::where('email', $request['email'])->firstOrFail();
return new AuthResource($user);
}
/**
* User profile
*
* @OA\Get(
* path="/api/v1/profile",
* tags={"Auth"},
* security={{ "sanctum": {} }},
* @OA\Response(
* response=200,
* description="Successful operation",
* @OA\JsonContent(ref="#/components/schemas/UserResource")
* ),
* @OA\Response(
* response=401,
* description="Unauthenticated",
* ),
* @OA\Response(
* response=403,
* description="Forbidden"
* )
* )
*
* @param Request $request
* @return UserResource
*/
public function profile(Request $request): UserResource
{
return new UserResource($request->user());
}
/**
* User logout
*
* @OA\Post(
* path="/api/v1/logout",
* tags={"Auth"},
* security={{ "sanctum": {} }},
* @OA\Response(
* response=200,
* description="Successful operation",
* @OA\JsonContent(ref="#/components/schemas/SuccessResource")
* ),
* @OA\Response(
* response=401,
* description="Unauthenticated",
* ),
* @OA\Response(
* response=403,
* description="Forbidden"
* )
* )
*
* @return SuccessResource
*/
public function logout(): SuccessResource
{
$user = auth()->guard('api')->user();
if (method_exists($user->currentAccessToken(), 'delete')) {
$user->currentAccessToken()->delete();
return new SuccessResource('Logout success');
}
auth()->guard('web')->logout();
return new SuccessResource('Logout success');
}
}
Tạo CreateUserRequest
<?php
namespace App\Http\Requests\Auth;
use App\Http\Requests\BaseRequest;
/**
* @OA\Schema(
* properties={
* @OA\Property(property="name", type="string", example="huunv"),
* @OA\Property(property="email", type="string", example="huunv@gmail.com"),
* @OA\Property(property="password", type="string", example="123456"),
* }
* )
*/
class CreateUserRequest extends BaseRequest
{
/**
* Get the validation rules that apply to the request.
*
* @return array<string, mixed>
*/
public function rules()
{
return [
'name' => 'required',
'email' => 'required|email|unique:users,email',
'password' => 'required'
];
}
}
Tạo AuthRequest
<?php
namespace App\Http\Requests\Auth;
use App\Exceptions\ApiException;
use App\Http\Requests\BaseRequest;
use Illuminate\Contracts\Validation\Validator;
/**
* @OA\Schema(
* properties={
* @OA\Property(property="email", type="string", example="huunv@gmail.com"),
* @OA\Property(property="password", type="string", example="123456"),
* }
* )
*/
class AuthRequest extends BaseRequest
{
/**
* Get the validation rules that apply to the request.
*
* @return array<string, mixed>
*/
public function rules()
{
return [
'email' => 'required|email',
'password' => 'required'
];
}
}
Tạo AuthResource thiết lập response trả về cho api. Tìm hiểu thêm hướng dẫn sử dụng laravel resource
<?php
namespace App\Http\Resources;
/**
* @OA\Schema(
* properties={
* @OA\Property(
* property="success",
* type="boolean",
* example="true"
* ),
* @OA\Property(
* property="data",
* type="object",
* @OA\Property(property="token", type="string", example="3|PXX3pewsSBbtJhJQXXuRZ1NiLHzTvD6Bv2TBUJjm"),
* @OA\Property(property="token_type", type="string", example="Bearer"),
* ),
* @OA\Property(
* property="message",
* type="string",
* example="success"
* )
* }
* )
*/
class AuthResource extends BaseResource
{
/**
* Transform the resource into an array.
*
* @param \Illuminate\Http\Request $request
* @return array|\Illuminate\Contracts\Support\Arrayable|\JsonSerializable
*/
public function toArray($request)
{
return [
'token' => $this->createToken("api")->plainTextToken,
'token_type' => 'Bearer',
];
}
}
Kiểm tra api đăng kí user
Api đăng nhập, mỗi lần đăng nhập laravel sanctum sẽ tạo ra 1 token mới
// Create token without permission
$user->createToken('token-name')->plainTextToken;
// Create Abilities token
return $user->createToken('token-name', ['server:update'])->plainTextToken;
// Check ability using
if ($user->tokenCan('server:update')) {
//
}
// Revoke all tokens...
$user->tokens()->delete();
// Revoke the token that was used to authenticate the current request...
$request->user()->currentAccessToken()->delete();
// Revoke a specific token...
$user->tokens()->where('id', $tokenId)->delete();
config/sanctum.php
/*
|--------------------------------------------------------------------------
| Expiration Minutes
|--------------------------------------------------------------------------
|
| This value controls the number of minutes until an issued token will be
| considered expired. If this value is null, personal access tokens do
| not expire. This won't tweak the lifetime of first-party sessions.
|
*/
'expiration' => null,