Custom Login dengan Memodifikasi Auth Laravel


Ada banyak kelebihan yang didapatkan ketika membuat sendiri fitur login dari awal. Seperti alur kerja aplikasi yang bebas diatur sedemikian rupa. Namun, ketika menggunakan auth yang sudah disediakan, khususnya fitur login, bukan berarti kita terpaku pada alur yang sudah dibuat Laravel. Dengan menggunakan auth bawaan Laravel, kita juga dimungkinkan membuat fitur login dengan spesifikasi khusus.

Contoh kasus pada tulisan ini dapat diimplementasikan pada Laravel versi 5.3 ke atas.

Jika kita buka controller login yang terletak dalam berkas app/Http/Controllers/Auth/LoginController.php, akan terlihat hanya ada satu method di dalam class tersebut, itupun method constructor. Dalam class controller ini, tidak didefinisikan secara eksplisit fungsi untuk menangani proses login.

<?php

namespace App\Http\Controllers\Auth;

use App\Http\Controllers\Controller;
use Illuminate\Foundation\Auth\AuthenticatesUsers;

class LoginController extends Controller
{
    /*
    |--------------------------------------------------------------------------
    | Login Controller
    |--------------------------------------------------------------------------
    |
    | This controller handles authenticating users for the application and
    | redirecting them to your home screen. The controller uses a trait
    | to conveniently provide its functionality to your applications.
    |
    */

    use AuthenticatesUsers;

    /**
     * Where to redirect users after login.
     *
     * @var string
     */
    protected $redirectTo = '/home';

    /**
     * Create a new controller instance.
     *
     * @return void
     */
    public function __construct()
    {
        $this->middleware('guest')->except('logout');
    }
}

Jika diperhatikan, class controller di atas mengimpor dan menggunakan trait AuthenticatesUsers yang terletak dalam berkas berikut:

vendor/laravel/framework/src/Illuminate/Foundation/Auth/AuthenticatesUsers.php

Nah, di dalam trait tersebutlah method yang berhubungan dengan fungsi login didefinisikan. Method yang tersedia lengkap mulai dari menampilkan formulir login, validasi, pengecekan user, sampai dengan respons. Setiap proses dipisah ke dalam method tersendiri. Hal ini memungkinkan pemrogram (programmer) untuk mengubah hal yang dirasa perlu saja.

<?php

namespace Illuminate\Foundation\Auth;

use Illuminate\Http\Request;
use Illuminate\Support\Facades\Auth;

trait AuthenticatesUsers
{
    use RedirectsUsers, ThrottlesLogins;

    /**
     * Show the application's login form.
     *
     * @return \Illuminate\Http\Response
     */
    public function showLoginForm()
    {
        return view('auth.login');
    }

    /**
     * Handle a login request to the application.
     *
     * @param  \Illuminate\Http\Request  $request
     * @return \Illuminate\Http\RedirectResponse|\Illuminate\Http\Response
     */
    public function login(Request $request)
    {
        $this->validateLogin($request);

        // If the class is using the ThrottlesLogins trait, we can automatically throttle
        // the login attempts for this application. We'll key this by the username and
        // the IP address of the client making these requests into this application.
        if ($this->hasTooManyLoginAttempts($request)) {
            $this->fireLockoutEvent($request);

            return $this->sendLockoutResponse($request);
        }

        if ($this->attemptLogin($request)) {
            return $this->sendLoginResponse($request);
        }

        // If the login attempt was unsuccessful we will increment the number of attempts
        // to login and redirect the user back to the login form. Of course, when this
        // user surpasses their maximum number of attempts they will get locked out.
        $this->incrementLoginAttempts($request);

        return $this->sendFailedLoginResponse($request);
    }

    /**
     * Validate the user login request.
     *
     * @param  \Illuminate\Http\Request  $request
     * @return void
     */
    protected function validateLogin(Request $request)
    {
        $this->validate($request, [
            $this->username() => 'required|string',
            'password' => 'required|string',
        ]);
    }

    /**
     * Attempt to log the user into the application.
     *
     * @param  \Illuminate\Http\Request  $request
     * @return bool
     */
    protected function attemptLogin(Request $request)
    {
        return $this->guard()->attempt(
            $this->credentials($request), $request->has('remember')
        );
    }

    /**
     * Get the needed authorization credentials from the request.
     *
     * @param  \Illuminate\Http\Request  $request
     * @return array
     */
    protected function credentials(Request $request)
    {
        return $request->only($this->username(), 'password');
    }

    /**
     * Send the response after the user was authenticated.
     *
     * @param  \Illuminate\Http\Request  $request
     * @return \Illuminate\Http\Response
     */
    protected function sendLoginResponse(Request $request)
    {
        $request->session()->regenerate();

        $this->clearLoginAttempts($request);

        return $this->authenticated($request, $this->guard()->user())
                ?: redirect()->intended($this->redirectPath());
    }

    /**
     * The user has been authenticated.
     *
     * @param  \Illuminate\Http\Request  $request
     * @param  mixed  $user
     * @return mixed
     */
    protected function authenticated(Request $request, $user)
    {
        //
    }

    /**
     * Get the failed login response instance.
     *
     * @param  \Illuminate\Http\Request  $request
     * @return \Illuminate\Http\RedirectResponse
     */
    protected function sendFailedLoginResponse(Request $request)
    {
        $errors = [$this->username() => trans('auth.failed')];

        if ($request->expectsJson()) {
            return response()->json($errors, 422);
        }

        return redirect()->back()
            ->withInput($request->only($this->username(), 'remember'))
            ->withErrors($errors);
    }

    /**
     * Get the login username to be used by the controller.
     *
     * @return string
     */
    public function username()
    {
        return 'email';
    }

    /**
     * Log the user out of the application.
     *
     * @param  \Illuminate\Http\Request  $request
     * @return \Illuminate\Http\Response
     */
    public function logout(Request $request)
    {
        $this->guard()->logout();

        $request->session()->invalidate();

        return redirect('/');
    }

    /**
     * Get the guard to be used during authentication.
     *
     * @return \Illuminate\Contracts\Auth\StatefulGuard
     */
    protected function guard()
    {
        return Auth::guard();
    }
}

***

Sebagai contoh permulaan, kita punya kasus untuk mengubah path lokasi view login yang secara bawaan merujuk ke “auth.login”, kemudian diubah menjadi “user.login”.

Untuk memodifikasi path tersebut, tentunya tidak dianjurkan mengubah langsung pada trait. Alih-alih melakukan perubahan pada trait AuthenticatesUsers, kita dapat menimpanya method tersebut sesuai dengan konsep trait di PHP.

Dalam trait, method untuk menampilkan form login bernama showLoginForm(). Untuk menimpa atau mengabaikan method ini dalam trait, kita hanya perlu menambahkan nama method yang sama ke dalam class LoginController.

<?php

namespace App\Http\Controllers\Auth;

use App\Http\Controllers\Controller;
use Illuminate\Foundation\Auth\AuthenticatesUsers;

class LoginController extends Controller
{
    /*
    |--------------------------------------------------------------------------
    | Login Controller
    |--------------------------------------------------------------------------
    |
    | This controller handles authenticating users for the application and
    | redirecting them to your home screen. The controller uses a trait
    | to conveniently provide its functionality to your applications.
    |
    */

    use AuthenticatesUsers;

    /**
     * Where to redirect users after login.
     *
     * @var string
     */
    protected $redirectTo = '/home';

    /**
     * Create a new controller instance.
     *
     * @return void
     */
    public function __construct()
    {
        $this->middleware('guest')->except('logout');
    }

    /**
     * Show the application's login form.
     *
     * @return \Illuminate\Http\Response
     */
    public function showLoginForm()
    {
        return view('user.login');
    }
}

Voila! Path formulir login sudah diubah sesuai dengan contoh kasus pertama. Tentunya, kita juga dapat menambahkan variabel pada view layaknya controller yang sering kita buat sebelumnya.

***

Contoh kedua misalnya, kita akan menambahkan validasi di formulir login yang sebelumnya hanya mengecek email dan password, maka di custom login yang kita buat juga akan melakukan pengecekan kode unik.

Dalam trait, method untuk memvalidasi input dari pengguna bernama validateLogin(). Salin method ini ke dalam LoginController, dan tambahkan validasi kode unique sesuai dengan spesifikasi contoh kasus.

<?php

namespace App\Http\Controllers\Auth;

use App\Http\Controllers\Controller;
use Illuminate\Foundation\Auth\AuthenticatesUsers;

class LoginController extends Controller
{
    /*
    |--------------------------------------------------------------------------
    | Login Controller
    |--------------------------------------------------------------------------
    |
    | This controller handles authenticating users for the application and
    | redirecting them to your home screen. The controller uses a trait
    | to conveniently provide its functionality to your applications.
    |
     */

    use AuthenticatesUsers;

    /**
     * Where to redirect users after login.
     *
     * @var string
     */
    protected $redirectTo = '/home';

    /**
     * Create a new controller instance.
     *
     * @return void
     */
    public function __construct()
    {
        $this->middleware('guest')->except('logout');
    }

    /**
     * Show the application's login form.
     *
     * @return \Illuminate\Http\Response
     */
    public function showLoginForm()
    {
        return view('user.login');
    }

    /**
     * Validate the user login request.
     *
     * @param  \Illuminate\Http\Request  $request
     * @return void
     */
    protected function validateLogin(Request $request)
    {
        $this->validate($request, [
            $this->username() => 'required|string',
            'password' => 'required|string',
            'code' => 'required|exists|users,code',
        ]);
    }
}

***

Nah, sampai di sini, kita sudah berhasil memodifikasi dua hal, yaitu path formulir login dan validasi input tambahan.

Sekarang, masuk ke contoh lainnya, yang mana selain input email dan password, sistem juga harus mengecek status user apakah sudah aktif atau belum. Di dalam pangkalan data, kalian menyimpan data “A” sebagai penanda status user sudah aktif.

Facade Auth di Laravel sudah menyediakan data kombinasi selain email dan password. Sebagai contoh, untuk mengecek credential user, kalian bisa menuliskan skrip seperti di bawah.

$loggedIn = \Auth::attempt([
    'email' => $request->email,
    'password' => $request->password,
    'status' => 'A',
], $remember);

Setiap kali ingin menambahkan kombinasi pengecekan, kalian cukup mendefinisikannya pada argumen pertama method attempt() dalam facade Auth.

Di trait AuthenticateUsers, penulisan skripnya sedikit berbeda walau pada dasarnya menggunakan package yang sama. Sebelum memodifikasinya, terlebih dahulu kita lihat isi method attemptLogin() yang sudah ada.

/**
 * Attempt to log the user into the application.
 *
 * @param  \Illuminate\Http\Request  $request
 * @return bool
 */
protected function attemptLogin(Request $request)
{
    return $this->guard()->attempt(
        $this->credentials($request), $request->filled('remember')
    );
}

Method attemptLogin() juga terikat dependensi dengan method lain yang bernama credentials(). Method ini berfungsi untuk mengambil request apa saja yang dimasukkan oleh pengguna. Secara bawaan, hanya email dan password yang diizinkan untuk digunakan.

/**
 * Get the needed authorization credentials from the request.
 *
 * @param  \Illuminate\Http\Request  $request
 * @return array
 */
protected function credentials(Request $request)
{
    return $request->only($this->username(), 'password');
}

Untuk memodifikasinya, hal pertama yang dilakukan adalah menambahkan izin untuk request yang bernama “status” (dengan asumsi nama kolom di pangkalan data juga “status”).

/**
 * Get the needed authorization credentials from the request.
 *
 * @param  \Illuminate\Http\Request  $request
 * @return array
 */
protected function credentials(Request $request)
{
    return $request->only($this->username(), 'password', 'status');
}

Langkah selanjutnya, definisikan status harus bernilai “A”. Karena pengecekan status ini bukan berasal dari masukan data user, maka kita dapat menambahkan nilai pada object $request dengan method merge().

/**
 * Attempt to log the user into the application.
 *
 * @param  \Illuminate\Http\Request  $request
 * @return bool
 */
protected function attemptLogin(Request $request)
{
    $request->merge(['status' => 'A']);

    return $this->guard()->attempt(
        $this->credentials($request), $request->filled('remember')
    );
}

Sebagai alternatif penggunaan method merge(), kalian juga dapat menggunakan method add() seperti yang dicontohkan oleh Laravel Daily.

$request->request->add(['status' => 'A']);

Terakhir, jangan lupa untuk menyalin kedua method tersebut beserta isinya ke dalam class LoginController. Dan, seharusnya aplikasi yang kita bangun sudah berfungsi sesuai dengan spesifikasi contoh-contoh kasus di atas.

***

Tentunya, tidak dapat semua contoh kasus saya jabarkan di tulisan sini. Dalam dunia pemrograman sesungguhnya, ada ratusan atau bahkan ribuan kombinasi contoh kasus yang terjadi, tergantung dengan spesifikasi dari pemilik aplikasi.

Pengenalan custom login di tulisan ini memberikan gambaran bagi kalian yang ingin memanfaatkan fitur auth di Laravel, namun tanpa harus membuatnya dari awal.

Terakhir, sebagai informasi, tak hanya fitur login yang menggunakan trait seperti di atas. Fitur seperti register dan password juga mempunyai konsep yang sama. Yang perlu kalian perhatikan adalah nama trait yang digunakan, serta memahami sedikit demi sedikit fungsi dalam trait tersebut.

Jangan takut untuk mengeksplorasi fitur lainnya. Jika ada hal yang dirasa kurang paham, bisa ditanyakan langsung pada formulir komentar di bawah. 😉

Yugo Purwanto

Pemrogram PHP dan JavaScript yang sedang sibuk mengembangkan aplikasi Glosarium Bahasa Indonesia.

1 comment

Tinggalkan Balasan