Laravel 8 - #16 - User Login dan Middleware
Pada pembahasan kali ini kita akan melanjutkan pembahasan mengenai aplikasi blog kita, setelah sebelumnya kita berhasil membuat feature registrasi untuk user kita. Nah, sekarang saatnya kita akan membuat dan meng-aplikasikan feature login nya, sehingga nantinya user yang berhasil registrasi itu bisa masuk kedalam aplikasi kita. Selain kita membuat feature login, kita juga akan sambil mempelajari featrue `middleware` yang ada didalam Laravel.
Arman Dwi Pangestu
1 Januari 2024•1 menit baca
Pendahuluan
Pada pembahasan kali ini kita akan melanjutkan pembahasan mengenai aplikasi blog kita, setelah sebelumnya kita berhasil membuat feature registrasi untuk user kita. Nah, sekarang saatnya kita akan membuat dan meng-aplikasikan feature login nya, sehingga nantinya user yang berhasil registrasi itu bisa masuk kedalam aplikasi kita.
Selain kita membuat feature login, kita juga akan sambil mempelajari featrue middleware
yang ada didalam Laravel.
Penjelasan Authentication di Laravel
Sebelum kita mulai coding, kita cari tau terlbeih dahulu di dokumentasi Laravel nya mengenai Authentication (bagaimana Laravel ini menangani feature login).
Starter Kits
Sebetulnya Laravel ini menyediakan feature Starter Kits
, jadi terdapat semacam plugin atau aplikasi yang khusus menangani masalah khusus authentication yaitu namanya Laravel Breeze, Laravel Jetstrem dan Laravel Fortify. Nah sebetulnya dengan aplikasi tersebut kita bisa dengan mudah menangani masalah autentikasi ini, mulai dari registrasi, login bahkan mempunyai verifikasi lewat email, remember me, forgot password dan semua yang berhubungan dengan authentication dan authorization itu sudah ditangani oleh Starter Kits ini. Semua plugin tersebut bisa kalian download atau install menggunakan composer namun UI nya dibuat menggunakan tailwind.
Manual Authentication
Pada pembahasan kali ini kita tidak akan menggunakan starter kits tersebut melainkan kita akan menggunakan versi manual. Sehingga jika kita tidak akan menggunakan aplikasi starter kit yang mempunyai scaffolding authentication nya tidak perlu khawatir karena Laravel mempunyai sebuah authentication service melalui Auth
facade.
Facade ini seperti library didalam Laravel yang namanya Auth, sehingga nantinya kita akan dipermudah dalam pengelolaan session untuk loginnya. Cara menggunakannya cukup mudah yaitu kita cukup panggil facade nya didalam controller kita kemudian kita pakai apa yang dibutuhkan.
<?php
namespace App\Http\Controllers;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Auth;
class LoginController extends Controller
{
/**
* Handle an authentication attempt.
*
* @param \Illuminate\Http\Request $request
* @return \Illuminate\Http\Response
*/
public function authenticate(Request $request)
{
$credentials = $request->validate([
'email' => ['required', 'email'],
'password' => ['required'],
]);
if (Auth::attempt($credentials)) {
$request->session()->regenerate();
return redirect()->intended('dashboard');
}
return back()->withErrors([
'email' => 'The provided credentials do not match our records.',
]);
}
}
Memperbaiki Bagian View Login
Sebelum kita melakukan authentication, kita benarkan terlebih dahulu view login.blade.php
nya seperti csrf_token
dan juga attribute action
dan method
nya,
<form action="/login" method="POST">
@csrf
<div class="form-floating">
<input type="email" name="email" class="form-control @error('email')
is-invalid
@enderror" id="email" placeholder="name@example.com" autofocus required value="{{ old('email') }}">
<label for="email">Email address</label>
@error('email')
<div class="invalid-feedback">
{{ $message }}
</div>
@enderror
</div>
<div class="form-floating">
<input type="password" name="password" class="form-control" id="password" placeholder="Password" required>
<label for="password">Password</label>
</div>
<button class="btn btn-primary w-100 py-2" type="submit">Login</button>
</form>
Menyiapkan Route Login
Setelah kita memberikan action dan method pada form view login nya, sekarang kita siapkan route nya untuk menangani request tersebut di file web.php
Route::post('/login', [AuthController::class, 'authenticate']);
Membuat Method authenticate
Sekarang kita siapkan method dengan nama authenticate
di file AuthController.php
untuk menangani route yang sudah kita bikin sebelumnya
use Illuminate\Support\Facades\Auth;
public function authenticate(Request $request)
{
}
Validasi Credentials
Sekarang kita buatkan validasi credentials request login nya didalam method authenticate
nya
public function authenticate(Request $request)
{
$credentials = $request->validate([
'email' => ['required', 'email:dns'],
'password' => ['required']
]);
}
Pengecekan Login
Setelah kita definisikan validasi credentials yang bisa login, sekarang kita cek apakah percobaan login nya berhasil atau tidak data nya sesuai dengan yang di database menggunakan method attempt
dan jika gagal maka kita redirect ke halaman login dengan mengirimkan pesan error nya
Catatan: Tips Security
Pastikan pesan error yang dikirim bukan mengenai data yang senstivie seperti:
- Email belum terdaftar
- Password salah
Walaupun pesan error tersebut sangat bagus atau sangat membantu user nya, tetapi hal tersebut juga bisa menjadi celah keamanan, karena user yang berniat jahat menjadi tau jika email tersebut sudah atau belum terdaftar di sebuah sistem (sehingga orang tersebut minimal mengetahui alamat email nya). Sebaiknya kita tidak sedikitpun memberi sebuah clue walaupun password nya tidak ketauan.
Alternative lain kita bisa kirimkan saja informasi seperti
Login failed
atauLogin gagal
Dan satu hal lagi, mengapa kita melakukan regenerate session seperti kode dibawah ini ketika login berhasil, itu dilakukan untuk menghindari sebuah teknik yang namanya
Session Fixation
. Teknik tersebut bekerja dengan bagaimana seseorang itu masuk kedalam celah keamanan sistem menggunakan session, sehingga seperti berpura-pura masuk dengan session yang sama sebelumnya sehingga tidak perlu login lagi karena sudah menggunakan session yang sama.
public function authenticate(Request $request)
{
$credentials = $request->validate([
'email' => ['required', 'email:dns'],
'password' => ['required']
]);
if (Auth::attempt($credentials)) {
$request->session()->regenerate();
return redirect()->intended('/dashboard');
}
// Error disini akan masuk kedalam variabel @error
// return back()->withErrors('');
// Menggunakan flash message sebagai informasi error
return back()->with('loginError', 'Login failed!');
}
Method intended
pada bagian return setelah login berhasil adalah sebuah method yang disediakan oleh Laravel yang akan me-redirect user nya ke sebuah tempat atau URL sebelum melewati sebuah authentication middleware.
Setelah method authenticate dibuat, sekarang kita tambahkan informasi flash message yang dikirim dari method tersebut apabila terdapat login error di file view login.blade.php
@if (session()->has('loginError'))
<div class="alert alert-danger alert-dismissible fade show" role="alert">
{{ session('loginError') }}
<button type="button" class="btn-close" data-bs-dismiss="alert" aria-label="Close"></button>
</div>
@endif
Maka sekarang jika kalian mencoba memasukkan data email benar dan password salah, akan muncul error flash message nya
Nah, bagaimana sekarang jika kalian mencoba dengan data login yang benar? sedangkan kita belum mempunyai route dan view /dashboard
nya, maka sekarang akan muncul error 404 | NOT FOUND
Namun didalam session nya sekarang kita sudah terdaftar login, Laravel nya tau sekarang kita sudah ter-autentikasi.
Membuat Controller, View dan Route Dashboard
Sekarang kita buat controller untuk dashboard nya menggunakan perintah artisan
php artisan make:controller DashboardController
Setelah itu kita siapkan method index
untuk menangani view default nya
class DashboardController extends Controller
{
public function index()
{
return view('dashboard.index');
}
}
Selanjutnya kita buat folder dan file dashboard/index.blade.php
baru di resources/views
dengan isian
<h1>Welcome, devnull</h1>
Terakhir kita siapkan route untuk mengarah ke controller dashboard nya di file web.php
use App\Http\Controllers\DashboardController;
Route::get('/dashboard', [DashboardController::class, 'index']);
Maka sekarang tampilan dashboard nya akan muncul tulisan Welcome, devnull
Sampai sini kelihatannya sudah sangat oke, namun masih banyak yang harus kita perbaiki contohnya adalah jika kita paksa kembali ke route /login
maka akan tetap tampil, seharusnya jika kita sudah login kita tidak bisa masuk ke route login lagi dan juga kita belum mempunyai cara untuk logout.
Nah untuk melakukan hal tersebut kita harus mengenal dulu yang namanya authentication middleware.
Apa itu Middleware?
Jika dikutip dari dokumentasi resmi Laravel nya, middelware itu menyediakan sebuah mekanisme yang memudahkan kita untuk melakukan sebuah insfeksi dan filtering HTTP Request, sehingga request kita di filter terlebih dahulu oleh middleware ini. Contohnya, laravel sudah menyertakan sebuah middleware didalamnya yang melakukan verifikasi apakah seorang user didalam aplikasi kita itu sudah ter-autentikasi atau belum, jika belum maka middleware tadi akan melakukan redirect user tadi ke halaman /login
atau ke halaman mana sesuai dengan yang kita inginkan. Tapi jika sudah ter-autentikasi maka middleware akan memperbolehkan kita untuk lanjut masuk kedalam aplikasi kita.
Jadi sebetulnya middleware itu seperti ini, middleware itu bisa kita pasang misalkan di route kita seperti ini
Route::get('/login', [AuthController::class, 'login']);
Route tersebut dibaca seperti ini
- Jika terdapat request dengan method
get
- Ke route atau alamat
/login
- Maka jalankan Class Controller
Auth
dengan method nya adalahlogin
Nah, nantinya kita bisa pasangkan middleware kedalam route tersebut, middleware akan berjalan sebelum [AuthController::class, 'login']
. Sehingga jika terdapat request dengan method get
ke route /login
maka jalanin dulu middleware dan jika sudah oke maka lanjut ke controller nya.
Secara default didalam Laravel itu sudah ada banyak middleware yang jalan yaitu Global Middleware
Global Middleware
Didalam aplikasi Laravel ada middleware default yang otomatis jalan setiap HTTP Request kita dijalankan di app/Http/Kernel.php
<?php
namespace App\Http;
use Illuminate\Foundation\Http\Kernel as HttpKernel;
class Kernel extends HttpKernel
{
/**
* The application's global HTTP middleware stack.
*
* These middleware are run during every request to your application.
*
* @var array<int, class-string|string>
*/
protected $middleware = [
// \App\Http\Middleware\TrustHosts::class,
\App\Http\Middleware\TrustProxies::class,
\Fruitcake\Cors\HandleCors::class,
\App\Http\Middleware\PreventRequestsDuringMaintenance::class,
\Illuminate\Foundation\Http\Middleware\ValidatePostSize::class,
\App\Http\Middleware\TrimStrings::class,
\Illuminate\Foundation\Http\Middleware\ConvertEmptyStringsToNull::class,
];
/**
* The application's route middleware groups.
*
* @var array<string, array<int, class-string|string>>
*/
protected $middlewareGroups = [
'web' => [
\App\Http\Middleware\EncryptCookies::class,
\Illuminate\Cookie\Middleware\AddQueuedCookiesToResponse::class,
\Illuminate\Session\Middleware\StartSession::class,
// \Illuminate\Session\Middleware\AuthenticateSession::class,
\Illuminate\View\Middleware\ShareErrorsFromSession::class,
\App\Http\Middleware\VerifyCsrfToken::class,
\Illuminate\Routing\Middleware\SubstituteBindings::class,
],
'api' => [
// \Laravel\Sanctum\Http\Middleware\EnsureFrontendRequestsAreStateful::class,
'throttle:api',
\Illuminate\Routing\Middleware\SubstituteBindings::class,
],
];
/**
* The application's route middleware.
*
* These middleware may be assigned to groups or used individually.
*
* @var array<string, class-string|string>
*/
protected $routeMiddleware = [
'auth' => \App\Http\Middleware\Authenticate::class,
'auth.basic' => \Illuminate\Auth\Middleware\AuthenticateWithBasicAuth::class,
'cache.headers' => \Illuminate\Http\Middleware\SetCacheHeaders::class,
'can' => \Illuminate\Auth\Middleware\Authorize::class,
'guest' => \App\Http\Middleware\RedirectIfAuthenticated::class,
'password.confirm' => \Illuminate\Auth\Middleware\RequirePassword::class,
'signed' => \Illuminate\Routing\Middleware\ValidateSignature::class,
'throttle' => \Illuminate\Routing\Middleware\ThrottleRequests::class,
'verified' => \Illuminate\Auth\Middleware\EnsureEmailIsVerified::class,
];
}
Bisa kalian lihat terdapat middleware yang namanya auth
didalam property $routeMiddleware
. Nah, middleware tersebut akan kita gunakan ketika kita ingin route atau halaman tertentu hanya bisa diakses oleh user yang sudah ter-autentikasi.
Dan middleware guest
didalam property $routeMiddleware
juga akan kita gunakan ketika kita ingin menangani route atau halaman tertentu jika user nya belum ter-autentikasi.
Jadi, bisa kalian anggap middleware auth
ini kalian sudah login dan middleware guest
ini tamu belum login.
Implementasi Middleware
Contohnya misalkan kita ingin halaman login yang ini
Route::get('/login', [AuthController::class, 'login']);
hanya bisa diakses oleh user yang belum ter-autentikasi, berarti menggunakan middleware guest
.
Sedangkan halaman dashboard yang ini
Route::get('/dashboard', [DashboardController::class, 'index']);
hanya bisa diakses oleh user yang sudah ter-autentikasi, berarti menggunakan middleware auth
.
Sehingga sekarang kita bisa terapkan middlware tersebut pada route yang kita inginkan seperti berikut ini
Route::get('/login', [AuthController::class, 'login'])->middleware('guest');
Route::get('/register', [AuthController::class, 'register'])->middleware('guest');
Route::get('/dashboard', [DashboardController::class, 'index'])->middleware('auth');
Maka sekarang jika kita mencoba masuk ke route /login
dengan posisi sudah ter-autentikasi, maka kita akan di redirect ke route /home
Hal tersebut terjadi karena default middleware dari guest
, jika kalian ingin mencoba mengubah default redirect nya kalian bisa ubah di file service provider nya app/Providers/RouteServiceProvider.php
public const HOME = '/';
Maka sekarang default redirect nya jika kita paksa ke route /login
dengan posisi sudah ter-autentikai, maka kita akan di redirect ke route /
Fix Komponen
Nah, bahkan kerennya sekarang kita bisa buat agar button login nya berubah jika kita sudah ter-autentikasi, cara nya kita ubah file navbar.blade.php
<ul class="navbar-nav ms-auto">
@auth
<li class="nav-item dropdown">
<a class="nav-link dropdown-toggle" href="#" role="button" data-bs-toggle="dropdown" aria-expanded="false">
Welcome, {{ auth()->user()->name }}
</a>
<ul class="dropdown-menu">
<li>
<a class="dropdown-item" href="/dashboard">
<i class="bi bi-layout-text-sidebar-reverse"></i> My Dashboard
</a>
</li>
<li><hr class="dropdown-divider"></li>
<li>
<form action="">
<button type="submit" class="dropdown-item">
<i class="bi bi-box-arrow-right"></i> Logout
</button>
</form>
</li>
</ul>
</li>
@else
<li class="nav-item">
<a href="/login" class="nav-link {{ ($active === 'login' ? 'active' : '') }}">
<i class="bi bi-box-arrow-right"></i> Login
</a>
</li>
@endauth
</ul>
Maka sekarang tampilan navlink item sebelah kanan nya akan seperti ini dan memiliki dropdown untuk pergi ke dashboard dan juga logout
Feature Logout
Setelah kita menyiapkan form dengan button untuk melakukan logout di navigation, sekarang kita arahkan action nya ke /logout
dengan method nya POST
<li>
<form action="/logout" method="POST">
@csrf
<button type="submit" class="dropdown-item">
<i class="bi bi-box-arrow-right"></i> Logout
</button>
</form>
</li>
Selanjutnya kita siapkan route /logout
nya di file web.php
Route::post('/logout', [AuthController::class, 'logout']);
Setelah route nya kita siapkan, sekarang kita buat method logout
di controller nya
public function logout(Request $request)
{
Auth::logout();
// Invalidate session agar tidak bisa digunakan
$request->session()->invalidate();
// Regenerate Token atau bikin baru agar tidak dibajak
$request->session()->regenerateToken();
return redirect('/');
}
Maka sekarang jika kita mencoba logout, komponen navbar dropdown nya sekarang berubah menjadi komponen button login lagi
Named Routes
Sekarang jika kita paksa masuk kedalam route /dashboard
dengan posisi kita belum ter-autentikasi maka akan muncul error Route [login] not defined
Hal tersebut terjadi karena kita tidak mempunyai sebuah route yang namanya login
, kenapa harus login? karena jika kalian lihat di file app/Http/Middleware/Authenticate.php
protected function redirectTo($request)
{
if (! $request->expectsJson()) {
return route('login');
}
}
Terdapat default route pada method redirectTo
yang mengarah ke route login
jika kita menggunakan middleware auth
. Nah, route di Laravel itu bisa kita berikan sebuah nama atau istilah nya itu adalah Named Routes
agar route nya tidak berpatokan pada URL nya. Sehingga jika URL nya apapun asalakan nama route nya apa kita bisa akses.
Route::get('/login', [AuthController::class, 'login'])->name('login')->middleware('guest');
Maka sekarang jika kalian mencoba memaksa masuk ke route /dashboard
dengan posisi belum ter-autentikasi, maka akan dikembalikan lagi ke route /login
. Namun jika sudah ter-autentikasi maka diperbolehkan.