
REST API Authentication dengan Laravel Sanctum
Rest API Laravel Sanctum - Dalam dunia pengembangan web modern, REST API telah menjadi pendekatan yang populer dalam membangun aplikasi web yang terpisah antara frontend dan backend. Dengan REST API, kita dapat menghubungkan frontend yang berbeda, seperti aplikasi web dan aplikasi seluler, dengan backend yang sama tanpa harus mengulang logika bisnis. Salah satu hal penting yang harus dipertimbangkan dalam membangun REST API adalah keamanan, terutama dalam mengotentikasi user yang ingin mengakses resource tertentu.
Laravel adalah salah satu framework PHP yang populer dan powerful untuk membangun aplikasi web. Laravel Sanctum adalah sebuah package yang ditawarkan oleh Laravel untuk menyediakan autentikasi API yang sederhana namun aman. Dengan Laravel Sanctum, kita dapat dengan mudah mengintegrasikan mekanisme autentikasi API ke dalam aplikasi Laravel kita.
Table of Contents
Apa itu Laravel Sanctum?
Laravel Sanctum adalah package autentikasi API yang menyediakan beberapa metode autentikasi yang berbeda untuk aplikasi Laravel. Package ini secara khusus dirancang untuk mendukung autentikasi berbasis token untuk RESTful API.
Sanctum menggunakan konsep stateless tokens, yang berarti tidak ada session yang diatur di sisi server. Setiap request ke API harus menyertakan token autentikasi yang valid untuk mendapatkan akses ke resource tertentu. Sanctum package membuat autentikasi menjadi lebih sederhana dan cocok untuk aplikasi berbasis SPA (Single Page Application) atau aplikasi seluler.
Step 1: Install Laravel
Sebelum menggunakan Laravel Sanctum, pastikan Anda telah menginstal Laravel terlebih dahulu. Jika belum, Anda dapat menginstalnya menggunakan Composer dengan perintah berikut:
composer create-project --prefer-dist laravel/laravel-10
atau menggunakan laravel installer dengan perintah berikut:
laravel new laravel-10
Step 2: Konfigurasi Database
Oke, setelah menyelesaikan step pertama, lanjutkan dengan membuat database baru dan jangan lupa untuk menyesuaikan pada file .env.
DB_CONNECTION=mysql
DB_HOST=127.0.0.1
DB_PORT=3306
DB_DATABASE=laravel_10
DB_USERNAME=root
DB_PASSWORD=
Step 3: Setup Mailer
Dan karena pada artikel ini kita akan membuat skenario, ketika user berhasil register maka akan mengirimkan welcome email notification dan untuk OTP sendernya kita akan menggunakan Gmail SMTP, maka dari itu kita perlu menyesuaikan value untuk key mail pada env.
MAIL_MAILER=smtp
MAIL_HOST=smtp.googlemail.com
MAIL_PORT=587
[email protected]
MAIL_PASSWORD=qyrqmgbxxxxxxxx
MAIL_ENCRYPTION=tls
[email protected]
MAIL_FROM_NAME="${APP_NAME}"
Step 4: Setup Model & Migration
Oke, selanjutnya kita perlu melakukan set up pada file users migration. Kita tambahkan dua column yaitu token dan token_expires_at. Kenapa kita perlu menambahkan kedua column tersebut? Karena seperti yang telah saya jelaskan sebelumnya, pada artikel ini kita akan menerapkan login menggunakan kode OTP.
public function up(): void
{
Schema::create('users', function (Blueprint $table) {
$table->id();
$table->string('name');
$table->string('email')->unique();
$table->timestamp('email_verified_at')->nullable();
$table->string('password');
$table->string('token')->nullable();
$table->timestamp('token_expires_at')->nullable();
$table->rememberToken();
$table->timestamps();
});
}
Jika sudah menambahkan dua column tersebut, sekarang kita bisa menjalankan perintah artisan migrate untuk memigrasi file-file migration menjadi table-table di database yang telah kita buat sebelumnya. Jalankan perintah artisan seperti di bawah ini untuk menjalankan migration.
php artisan migrate
Karena kita mengubah file default users migration dari laravel, maka kita juga perlu menyesuaikannya pada file User model. Silahkan buka file models/User.php
, tambahkan token
dan token_expires_at
pada method $fillable
.
protected $fillable = [
'name',
'email',
'password',
'token',
'token_expires_at',
];
Step 5: Setup Welcome Email Notification
Kita lanjutkan untuk membuat notification class di laravel project kita. Silahkan jalankan perintah artisan seperti di bawah ini.
php artisan make:notification WelcomeEmailNotification
Perintah tersebut akan menempatkan notification class baru di direktori app/Notifications
kita. Setiap notification class berisi method via
dan sejumlah variabel method pembuatan pesan, seperti toMail
atau toArray
, yang mengubah notification menjadi pesan yang disesuaikan untuk chanel tertentu.
Selanjutnya, buka file app/Notifications/WelcomeEmailNotification.php
dan sesuaikan kode yang ada menjadi seperti di bawah ini. Disini kita menambahkan construct
dan mendefinisikan teks pesan email pada method toMail
.
public function __construct(User $user)
{
$this->user = $user;
}
public function via(object $notifiable): array
{
return ['mail'];
}
public function toMail(object $notifiable): MailMessage
{
return (new MailMessage)
->line('Welcome to our application, '.$this->user->name)
->line('Thank you for using our application!');
}
Step 6: Setup OtpNotification
Selanjutnya kita buat notification class untuk mengirimkan notifikasi OTP ketika ada request kode OTP. Silahkan jalankan perintah artisan seperti di bawah ini untuk membuat class OtpNotification
.
php artisan make:notification OtpNotification
Selanjutnya, buka file app/Notifications/OtpNotification.php
dan sesuaikan kode yang ada menjadi seperti di bawah ini. Disini kita menambahkan construct
dan mendefinisikan teks pesan email pada method toMail
.
public function __construct(User $user)
{
$this->user = $user;
}
public function via(object $notifiable): array
{
return ['mail'];
}
public function toMail(object $notifiable): MailMessage
{ return (new MailMessage)
->greeting('Hello, '.$this->user->name)
->line('Your OTP is '.$this->user->token)
->line('This OTP will expire in 5 minutes')
->line('If you did not request an OTP, no further action is required.')
->line('Thank you for using our application!');
}
Step 7: Setup Controller & Resource
Oke, pada step kelima kita akan membuat file Controller baru yang nantinya akan kita gunakan untuk membuat logic-logic. Silahkan jalankan perintah artisan seperti di bawah ini. Perintah tersebut akan membuatkan kita file AuthController.php yang terletak di direktori app/Http/Controllers/API.
php artisan make:controller Api/AuthController
Kemudian, buka file AuthController.php
yang baru saja kita generate tersebut dan sesuaikan codenya menjadi seperti di bawah.
<?php
namespace App\Http\Controllers\Api;
use App\Models\User;
use Illuminate\Http\Request;
use App\Http\Controllers\Controller;
use App\Http\Resources\UserResource;
use Illuminate\Support\Facades\Hash;
use App\Notifications\OtpNotification;
use Illuminate\Support\Facades\Validator;
use App\Notifications\WelcomeEmailNotification;
class AuthController extends Controller
{
public function register(Request $request)
{
try {
$validator = Validator::make($request->all(), [
'name' => 'required|string|max:255',
'email' => 'required|string|email|max:255|unique:users',
'password' => 'required|string|min:8|confirmed',
]);
if($validator->fails()){
return response()->json($validator->errors());
}
$user = User::create([
'name' => $request->name,
'email' => $request->email,
'password' => Hash::make($request->password)
]);
$user->notify(new WelcomeEmailNotification($user));
$token = $user->createToken('auth_token')->plainTextToken;
return response()->json([
'access_token' => $token,
'token_type' => 'Bearer',
]);
} catch (\Throwable $th) {
return response()->json([
'message' => 'Something went wrong',
'error' => $th->getMessage()
], 500);
}
}
public function otp(Request $request)
{
try {
$validator = Validator::make($request->all(), [
'email' => 'required|string|email|max:255|exists:users,email',
]);
if($validator->fails()){
return response()->json($validator->errors());
}
$user = User::where('email', $request->email)->first();
$otp = rand(100000, 999999);
$user->update([
'token' => $otp,
'token_expires_at' => now()->addMinutes(5)
]);
$user->notify(new OtpNotification($user));
return response()->json([
'message' => 'OTP sent to your email'
]);
} catch (\Throwable $th) {
return response()->json([
'message' => 'Something went wrong',
'error' => $th->getMessage()
], 500);
}
}
public function login(Request $request)
{
try {
$validator = Validator::make($request->all(), [
'email' => 'required|string|email|max:255|exists:users,email',
'password' => 'required|string|min:8',
]);
if($validator->fails()){
return response()->json($validator->errors());
}
$user = User::where('email', $request->email)->first();
if (!Hash::check($request->password, $user->password)) {
return response()->json([
'message' => 'Password mismatch'
], 401);
}
if ($user->token != $request->otp) {
return response()->json([
'message' => 'OTP mismatch'
], 401);
}
if ($user->token_expires_at < now()) {
return response()->json([
'message' => 'OTP expired'
], 401);
}
$user->tokens()->delete();
$token = $user->createToken('auth_token')->plainTextToken;
return response()->json([
'access_token' => $token,
'token_type' => 'Bearer',
]);
} catch (\Throwable $th) {
return response()->json([
'message' => 'Something went wrong',
'error' => $th->getMessage()
], 500);
}
}
public function profile(Request $request)
{
try {
return response()->json(new UserResource($request->user()));
} catch (\Throwable $th) {
return response()->json([
'message' => 'Something went wrong',
'error' => $th->getMessage()
], 500);
}
}
public function updateProfile(Request $request)
{
try {
$validator = Validator::make($request->all(), [
'name' => 'required|string|max:255',
'email' => 'required|string|email|max:255|unique:users,email,'.$request->user()->id,
]);
if($validator->fails()){
return response()->json($validator->errors());
}
$request->user()->update($request->only('name', 'email'));
return response()->json(new UserResource($request->user()));
} catch (\Throwable $th) {
return response()->json([
'message' => 'Something went wrong',
'error' => $th->getMessage()
], 500);
}
}
public function logout(Request $request)
{
try {
$request->user()->tokens()->delete();
return response()->json([
'message' => 'Logged out'
]);
} catch (\Throwable $th) {
return response()->json([
'message' => 'Something went wrong',
'error' => $th->getMessage()
], 500);
}
}
}
Pada AuthController
ini kita mempunyai beberapa method diantaranya; register
, otp
, profile
, updateProfile
, login
dan logout
. Method-method tersebut akan dijelaskan di bawah ini.
Register
public function register(Request $request)
{
try {
$validator = Validator::make($request->all(), [
'name' => 'required|string|max:255',
'email' => 'required|string|email|max:255|unique:users',
'password' => 'required|string|min:8|confirmed',
]);
if($validator->fails()){
return response()->json($validator->errors());
}
$user = User::create([
'name' => $request->name,
'email' => $request->email,
'password' => Hash::make($request->password)
]);
$user->notify(new WelcomeEmailNotification($user));
$token = $user->createToken('auth_token')->plainTextToken;
return response()->json([
'access_token' => $token,
'token_type' => 'Bearer',
]);
} catch (\Throwable $th) {
return response()->json([
'message' => 'Something went wrong',
'error' => $th->getMessage()
], 500);
}
}
Method register hanya berisikan fungsi validasi dan insert to database dari apa yang telah diinputkan pada endpoint register. Setelah data disimpan di tabel users, kita jalankan fungsi $user->notify(new WelcomeEmailNotification($user));
untuk mengirimkan welcome email notification ke email yang digunakan saat register. Setelah itu kita generate bearer token dengan fungsi $token = $user->createToken('auth_token')->plainTextToken;
. Method createToken
me-return instance Laravel\Sanctum\NewAccessToken
. API token di-hash menggunakan hashing SHA-256 sebelum disimpan di database, tetapi kita dapat mengakses plain text token value menggunakan property plainTextToken
dari instance NewAccessToken
.
Get OTP Code
public function otp(Request $request)
{
try {
$validator = Validator::make($request->all(), [
'email' => 'required|string|email|max:255|exists:users,email',
]);
if($validator->fails()){
return response()->json($validator->errors());
}
$user = User::where('email', $request->email)->first();
$otp = rand(100000, 999999);
$user->update([
'token' => $otp,
'token_expires_at' => now()->addMinutes(5)
]);
$user->notify(new OtpNotification($user));
return response()->json([
'message' => 'OTP sent to your email'
]);
} catch (\Throwable $th) {
return response()->json([
'message' => 'Something went wrong',
'error' => $th->getMessage()
], 500);
}
}
Method otp
berfungsi untuk generate otp code (random) kemudian memperbarui value property otp
dan token_expires_at
dari akun user yang digunakan. Kemudian akan mengirimkan OTP notification via email menggunakan fungsi $user->notify(new OtpNotification($user));
Login
public function login(Request $request)
{
try {
$validator = Validator::make($request->all(), [
'email' => 'required|string|email|max:255|exists:users,email',
'password' => 'required|string|min:8',
]);
if($validator->fails()){
return response()->json($validator->errors());
}
$user = User::where('email', $request->email)->first();
if (!Hash::check($request->password, $user->password)) {
return response()->json([
'message' => 'Password mismatch'
], 401);
}
if ($user->token != $request->otp) {
return response()->json([
'message' => 'OTP mismatch'
], 401);
}
if ($user->token_expires_at < now()) {
return response()->json([
'message' => 'OTP expired'
], 401);
}
$user->tokens()->delete();
$token = $user->createToken('auth_token')->plainTextToken;
return response()->json([
'access_token' => $token,
'token_type' => 'Bearer',
]);
} catch (\Throwable $th) {
return response()->json([
'message' => 'Something went wrong',
'error' => $th->getMessage()
], 500);
}
}
Method login me-validasi email, password dan token atau otp code. Jika valid, maka akan menjalankan fungsi hapus token-token yang dimiliki oleh user tersebut dan generate token baru.
Get Profile
public function profile(Request $request)
{
try {
return response()->json(new UserResource($request->user()));
} catch (\Throwable $th) {
return response()->json([
'message' => 'Something went wrong',
'error' => $th->getMessage()
], 500);
}
}
Untuk method profile hanya berisikan fungsi untuk menampilkan data user menggunakan class UserResource
.
Update Profile
public function updateProfile(Request $request)
{
try {
$validator = Validator::make($request->all(), [
'name' => 'required|string|max:255',
'email' => 'required|string|email|max:255|unique:users,email,'.$request->user()->id,
]);
if($validator->fails()){
return response()->json($validator->errors());
}
$request->user()->update($request->only('name', 'email'));
return response()->json(new UserResource($request->user()));
} catch (\Throwable $th) {
return response()->json([
'message' => 'Something went wrong',
'error' => $th->getMessage()
], 500);
}
}
Method updateProfile hanya berisikan fungsi update profile biasa. Sebelum menjalankan fungsi update data, terdapat validasi name dan email. Dan setelah data name dan email terdapat, maka akan me-return data user menggunakan class UserResource
.
Logout
public function logout(Request $request)
{
try {
$request->user()->tokens()->delete();
return response()->json([
'message' => 'Logged out'
]);
} catch (\Throwable $th) {
return response()->json([
'message' => 'Something went wrong',
'error' => $th->getMessage()
], 500);
}
}
Saat method ini dipanggil, maka akan menghapus token milik user tersebut dari database. Sehingga, jika user tersebut ingin mengakses halaman yang telah diproteksi dengan middleware auth:sanctum, dia harus login terlebih dahulu untuk mendapatkan access token lagi.
Seperti yang telah saya sebutkan di awal, pada artikel ini saya juga akan mengimplementasikan eloquent api resources. Untuk membuat file api resources, kita bisa menjalankan perintah artisan seperti di bawah ini.
php artisan make:resource UserResource
Perintah tersebut akan membuatkan kita file UserResource dan terletak didirektori app/Http/Resources
.
Buka file yang baru saja digenerate tersebut, pada function toArray
silahkan ubah menjadi seperti contoh kode di bawah ini.
public function toArray(Request $request): array
{
return [
'id' => $this->id,
'name' => $this->name,
'email' => $this->email,
];
}
Pada Class UserResource
, kita hanya ingin mengambil data id, name dan email user.
Step 8: Setup Route
Setelah selesai melakukan set up Controller, langkah selanjutnya atau pada langkah ke enam ini, kita akan melakukan set up route. Silahkan buka file routes/api.php
dan sesuaikan code yang ada menjadi seperti di bawah ini.
use App\Http\Controllers\Api\AuthController;
....
....
....
Route::controller(AuthController::class)->group(function () {
Route::post('register', 'register');
Route::post('login', 'login');
Route::middleware('auth:sanctum')->group(function () {
Route::prefix('profile')->group(function () {
Route::get('/', 'profile');
Route::post('/', 'update');
});
Route::post('logout', 'logout');
});
});
Kita bisa mengelompokkan route dalam group middleware auth:sanctum
untuk route-route yang perlu diproteksi seperti get profile, update profile dan logout.
Step 9: Testing
Oke, setelah menyelesaikan langkah-langkah di atas, sekarang waktunya untuk pengujian. Untuk menguji Rest API authentication yang telah kita buat dengan Sanctum di laravel 10 ini, kita bisa menggunakan Postman.
Register
Pengujian yang pertama adalah menguji fitur register. Silahkan buka Postman, buat request baru dengan method Post, isi URL dengan 127.0.0.1:{port}/api/register
atau laravel-sanctum.test/api/register
. Kemudian pada tab Body, pilih type raw JSON dan tambahkan data json seperti gambar di bawah ini, lalu klik Send. Jika request yang kamu kirimkan berhasil divalidasi, maka response yang akan diberikan akan seperti di bawah ini.
Nah, seperti yang telah saya sebutkan pada step kelima. Jika user berhasil melakukan register, maka akan langsung digenerate access token. Kita akan membutuhkan token tersebut untuk mengakses route yang telah diproteksi dengan middleware auth:sanctum.
Selain itu, jika kita berhasil register, maka sistem akan mengirimkan welcome email notification seperti di bawah ini.
Get Profile
Pengujian yang selanjutnya adalah melihat data profile user. Seperti yang telah saya jelaskan sebelumya, untuk route get profile ini telah kita proteksi menggunakan middleware auth:sanctum
, sehingga untuk mengaksesnya, kita perlu menambahkan token yang telah kita dapatkan saat melakukan register atau login.
Silahkan buat request baru dengan method GET, isi URL dengan 127.0.0.1:{port}/api/profile
atau laravel-sanctum.test/api/profile
. Pada tab Authorization
, pilih type Bearer Token
dan isi tokennya dengan token yang telah didapatkan saat melakukan register atau login. Jika token sudah benar, klik Send, maka akan menampilkan response berupa data dari user tersebut seperti pada gambar di bawah ini.
Update Profile
OK, Next. Kita akan melakukan pengujian pada endpoint update profile. Sama seperti pada endpoint get profile, pada update profile ini kita juga memerlukan token
untuk mengaksesnya. Silahkan buat request baru dengan method POST
, isi URL dengan 127.0.0.1:{port}/api/profile
atau laravel-sanctum.test/api/profile
. Pada tab Authorization
, pilih type Bearer Token
dan isi tokennya dengan token yang telah didapatkan saat melakukan register atau login.
Kemudian, buka tab Body dan pilih type raw JSON
. Isi key dan value sesuai dengan apa yang ingin kamu ubah dari data profile tersebut, lalu klik Send. Jika request berhasil divalidasi maka data dari profile kamu akan berubah.
Logout
Selanjutnya Kita akan melakukan pengujian pada endpoint logout. Pada endpoint logout ini kita memerlukan token untuk mengaksesnya. Silahkan buat request baru dengan method POST, isi URL dengan 127.0.0.1:{port}/api/logout
atau laravel-sanctum.test/api/logout
. Pada tab Authorization
, pilih type Bearer Token
dan isi tokennya dengan token yang telah didapatkan saat melakukan register atau login. Jika kita klik send, maka semua token yang dimiliki user tersebut pada table personal_access_tokens
akan terhapus. Dan jika kita ingin mengakses get profile, update profile dan logout, kita perlu login kembali untuk mendapatkan token.
Get OTP
Seperti yang telah saya jelaskan di awal, pada artikel ini kita akan menerapkan login menggunakan kode OTP. Jadi, sebelum login, kita perlu request kode OTP terlebih dahulu untuk dapat kita gunakan saat login ke aplikasi kita. Silahkan buat request baru dengan method POST, isi URL dengan 127.0.0.1:{port}/api/otp
atau laravel-sanctum.test/api/otp
. Kemudian, klik tab body pilih raw JSON dan isi key email lalu klik Send. Jika request yang kamu kirimkan berhasil divalidasi, maka akan menampilkan response berisikan message seperti pada gambar di bawah ini.
Setelah endpoint get OTP dijalankan, maka sistem akan mengirimkan kode OTP via email dengan isi pesan email seperti gambar di bawah ini.
Kode OTP inilah yang akan kita gunakan untuk login ke aplikasi atau untuk mengisi key otp pada endpoint login.
Login
Dan yang terakhir, kita akan melakukan pengujian pada endpoint login. Silahkan buat request baru dengan method POST, isi URL dengan 127.0.0.1:{port}/api/login
atau laravel-sanctum.test/api/login
. Kemudian, klik tab body dan isi key email, password dan otp lalu klik Send. Jika request yang kamu kirimkan berhasil divalidasi, maka akan menampilkan response berisikan message dan bearer token seperti pada gambar di bawah ini.
Kesimpulan
Dalam artikel ini, kami telah membahas tentang Laravel Sanctum, sebuah package yang menyediakan autentikasi API yang sederhana namun aman untuk aplikasi Laravel. Dengan menggunakan Sanctum, kita dapat dengan mudah mengintegrasikan autentikasi berbasis token ke dalam aplikasi Laravel, menjadikannya lebih aman dan sesuai dengan standar autentikasi REST API.
Penting untuk selalu menjaga keamanan aplikasi web dan API, dan Laravel Sanctum adalah salah satu cara yang bagus untuk melakukannya. Dengan langkah-langkah yang telah dijelaskan di atas, Anda dapat dengan cepat mulai menggunakan autentikasi API di proyek Laravel Anda dan melindungi resource dari akses yang tidak sah.
- REST API Authentication dengan Laravel Sanctum
- REST API CRUD dengan Laravel Sanctum
- Generate Unique Slug di Laravel dengan Eloquent Sluggable Package
- Implementasi Request Validation di Laravel REST API
- Implementasi API Versioning di Laravel
- Membuat Admin Panel dengan Filament - Tutorial Lengkap untuk Pemula
- Tutorial : Menggunakan DD ( ) - Di Laravel
- Tutorial : Membuat API Resources Di Laravel 10
- Tutorial : Membuat-Model Dan Migration-Di Laravel 10
- Laravel 10
- Laravel Restful Api
- Tutorial: Membuat Eloquent Accessor Di Laravel 10
- Tutorial : Menampilkan Data dari Database Di Laravel 10
- Tutorial: Cara Memasukan Data Ke Dalam Database Di Laravel 10
- Tutorial: Menampilkan Detail Data Di Laravel 10
- Tutorial: Update Data Ke Dalam Database Di Laravel 10
- Tutorial: Membuat WhatsApp Menggunakan Laravel
- Laravel Midtrans Tutorial
- Sweet Alert Laravel 10
Last updated on April 21, 2025