Laravel custom authentication system
Tags: laravel, beginners
Published: 2019-11-26 19:15:17

![Alt Text](https://media.giphy.com/media/gXGeWOVtwntaU/giphy.gif)
Hey everyone! So, this is my first post and I'm super excited to share some thoughts about this topic and wasting no time, let's get into it :fire:

####Synopsis:
* We will create custom Login and Registration system.
* We will see some code refactoring, which avoids too much logic inside a single method.

####Authentication
Basically, authentication means a lock :lock: to prevent anyone from accessing  protected content unless they are logged in. Laravel provides that in a super elegant way, but we are not limited to it.

---

Let's discuss some ways to perform Authentication.

###1. In built Basic Authentication (we won't use it here)
**Note - Laravel below v6** : Laravel has built an auth scaffolding which we can roll out with a single command. Just run `php artisan make:auth` and it does all the heavy lifting for us, but this has been changed in Laravel v6

**Laravel v6 (*Latest* as of writing this post)** : The same command above will not work, rather we have to use two commands, which provides the routes and layouts(views) for authentication endpoints.
```php
composer require laravel/ui

php artisan ui vue --auth
```

###2. Custom Basic Authentication:
> Create a fresh laravel app using **`laravel new application-name`** command.

Run the following commands,
```php
composer require laravel/ui  // ui scaffolding package

php artisan ui bootstrap  // *optional - adding bootstrap 

npm install && npm run dev
```
> Run **`php artisan serve`** to start development server

#####Steps
[ 1 ] We can use the **User** schema created by default inside 
      *database/create_users_table.php*

[ 2 ] Run **`php artisan migrate`** to migrate db

[ 3 ] Create routes for Login and Registration

```php
# File: routes/web.php

// Show Register Page & Login Page
Route::get('/login', 'LoginController@show')->name('login')->middleware('guest');
Route::get('/register', 'RegistrationController@show')
    ->name('register')
    ->middleware('guest');


// Register & Login User
Route::post('/login', 'LoginController@authenticate');
Route::post('/register', 'RegistrationController@register');


// Protected Routes - allows only logged in users
Route::middleware('auth')->group(function () {
    Route::get('/', 'DashboardController@index');

    Route::post('/logout', 'LoginController@logout');
});
```
Let's break some pieces, 
 * You may notice the **name** at the end, it creates a route named "register". This provides an option of routing to that specific page using the name given., Eg: In our controller we can call `redirect()->route("register")` which will hit the `GET /register` endpoint. It also helps to access the route inside blade file `{{ route('login') }}`
 * We have grouped some of our endpoints with a middleware called "auth". It basically prevents access to those endpoints from unauthenticated users. The middleware "guest" is vice versa of "auth", it allows only unauthenticated users. But if a logged in user visits the route, he will be redirected to the url mentioned inside *RedirectIfAuthenticated.php* middleware. And we can modify the url too.
 * Middleware can be used on route endpoints or assign it to a group of endpoints. It can also be used inside the controller's constructor method.

[ 4 ] Add this method to **User** model. It will hash the password before storing it to DB. It is called as **Mutator**

```php

use Illuminate\Support\Facades\Hash;  // Import Hash facade

public function setPasswordAttribute($password)
{
    $this->attributes['password'] = Hash::make($password);
}
```

[ 5 ] Run these commands to create controllers for Login, Registration and Dashboard,
```php
php artisan make:controller LoginController
php artisan make:controller RegistrationController
php artisan make:controller DashboardController
```
[ 6 ] Lets add the logic to display registration page and registering users inside **RegistrationController.php**
```php
<!-- File: RegistrationController.php -->

<?php

namespace App\Http\Controllers;

use Illuminate\Http\Request;
use Illuminate\Support\Facades\Hash;
use Illuminate\Support\Facades\Validator;

class RegistrationController extends Controller
{

    public function show()
    {
        return view('register');
    }

    public function register(Request $request)
    {
        $validator = $request->validate([
          'name'      => 'required|min:1',
          'email'     => 'required',
          'password'  => 'required|min:6'
        ]);

        \App\User::create($validator);

        return redirect('/login');
     }

}

```
 * *Show()* method returns the **register.blade.php** file using the global **view** helper. If register.blade is inside a folder, Eg: "auth/register.blade.php", then we can call it as **view('auth.register')**
 * Register users: 
   * There are number of ways to access the request fields inside a method. 
   * Here, we use method injection. It injects the incoming request to the method as an parameter. Request contains some variety of helper methods, here we use validate to check our request fields. 
   * Keep in mind that, if the fields (name, email, password) mentioned are not added into $fillable property inside "User" model, then a **Mass Assignment Exception** will be thrown.

[ 7 ] Adding logic to display login page, logging and logout users inside **LoginController.php**
```php
<!-- File: LoginController.php -->

<?php

namespace App\Http\Controllers;

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

class LoginController extends Controller
{
    public function show()
    {
        return view('login');
    }

    public function authenticate(Request $request)
    {
        $validator = $request->validate([
            'email'     => 'required',
            'password'  => 'required|min:6'
        ]);

        if (Auth::attempt($validator)) {
            return redirect()->route('dashboard');
        }
    }

    public function logout()
    {
        Session::flush()
        Auth::logout();
        return back();
    }

}

```
 * *show()* returns login page
 * *authenticate()* :
    * Validates request fields.
    * **Auth** facade is a helper laravel provides. *Auth::attempt* returns `true` if authentication was successful or `false` if it had failed. You can also add an else statement and redirect the user back with an error message.
    * Redirect to dashboard ("/") on successful authentication.
 * *logout()* : We logout using Auth facade and to make sure the user session is completely removed we flush the session.

[ 8 ] Add the necessary blade files to the views directory,
 * *register.blade.php, login.blade.php, dashboard.blade.php*

---

It's time to **Refactor**

We will make sure our controller method only does exactly what it is intended. This is a simple piece of code to refactor, but the practice will really help out on a large scale.

Code to refactor,
1. Separate validation logic from both register and authenticate methods to a *Request* file for each.
2. Inject the request classes created in step 1 as parameters to authenticate and register methods.
3. Move the registration logic to a *Trait* and use it inside controller.


[ 1 ] 
 * Separate out the validation logic from *register* method in *RegistrationController.php*.
 * Run **`php artisan make:request RegistrationRequest`**, this will create a file under *App/Http/Requests*.

Add the code below to *RegistrationRequest.php*
 
```php
// RegistrationRequest.php

public function authorize()
{
    return true;  // Set this to "true" else Unauthorized error will be thrown
}

public function rules()
{
    return [
        'name'      => ['required', 'min:3'],
        'email'     => ['required', 'string', 'email'],
        'password'  => ['required', 'string', 'min:6'],
    ];
}
```
---

 * Separate out the validation logic from *authenticate* method in *LoginController.php*.
 * Run **`php artisan make:request LoginRequest`**, this will create a file under *App/Http/Requests*.

Add the code below to *LoginRequest.php*
 
```php
// LoginRequest.php

public function authorize()
{
    return true;  // Set this to "true" else Unauthorized error will be thrown
}

public function rules()
{
    return [
        'email'     => ['required', 'string', 'email'],
        'password'  => ['required', 'string', 'min:6'],
    ];
}
```
[ 2 ]
 * We will inject **RegistrationRequest** into our register method. It will take care of the validation and provides the validated fields to our controller method.

```php
// RegistrationController.php

use App\Http\Requests\RegistrationRequest; // Don't forget to import

public function register(RegistrationRequest $requestFields)  // injected Request class
{

    // This will throw an error, since data passed to create method
    // must be an array, but $requestFileds contains an object.
    // We will fix it in next section
    \App\User::create($requestFields);  

    return redirect('/login');
}
```
 * Similarly, inject **LoginRequest** into our authenticate method, it will do the validation and provide the validated fields to our controller method.

```php
// LoginController.php

use App\Http\Requests\LoginRequest;  // Don't forget to import

public function authenticate(LoginRequest $requestFields)
{
    // Returned validated fields also contain the csrf token,
    // therefore, we pick only email and password.
    $attributes = $requestFields->only(['email', 'password']);

    if (Auth::attempt($attributes)) {
        return redirect()->route('dashboard');
    }
}
```

[ 3 ] 

 We will create a Trait called **RegisterUser**
 * Create a folder called *Traits* inside *app* folder and create a file *RegisterUser.php* and the namespace will be **`App\Traits`**

```php
<!-- RegisterUser.php -->

<?php

namespace App\Traits;

trait RegisterUser
{
    public function registerUser($fields)
    {
        $user = \App\User::create([
            'name'      => $fields->name,
            'email'     => $fields->email,
            'password'  => $fields->password
        ]);
        return $user;
    }
}

```
So, we moved our logic from register method to this trait, next we have to use it in our controller or any place we need.

Now, let's use the trait

 * In RegistrationController.php add the below code,
```php
use App\Traits\RegisterUser;  // Don't forget to import

class RegistrationController extends Controller
{
    use RegisterUser;  // call RegisterUser class here
```
 That's how we use a trait, cool right!😎

 * Now change the register method as mentioned below,
```php
public function register(RegistrationRequest $requestFields)
{
    $user = $this->registerUser($requestFields);
    return redirect('/login');
}
```

**Note** : **`$this->registerUser($requestFields)`** will call the **registerUser** method inside *RegisterUser* trait.

####Why we separated only the register logic ?
Consider an application where we defined a *Super-Admin* who can create users inside that application. On that case we need not duplicate the registration process, rather we will just use this trait.

Let's see our final code, the code below only shows the authenticate and register methods, since those were the only places we tweaked.

```php
<!-- LoginController.php -->

<?php

namespace App\Http\Controllers;

use App\Http\Requests\LoginRequest;

class LoginController extends Controller
{
    public function authenticate(LoginRequest $requestFields)
    {
        $attributes = $requestFields->only(['email', 'password']);
        if (Auth::attempt($attributes)) {
            return redirect()->route('dashboard');
        }
    }
}
```
```php
<!-- RegistrationController.php -->

<?php

namespace App\Http\Controllers;

use App\Traits\RegisterUser;
use App\Http\Requests\RegistrationRequest;

class RegistrationController extends Controller
{
    use RegisterUser;

    public function register(RegistrationRequest $requestFields)
    {
        $user = $this->registerUser($requestFields);
        return redirect('/login');
    }
}

```

###And that's it, let's wrap it up!

 * We created login and registration endpoints and controllers.
 * We moved our validation logic to separate request files.
 * We created a trait and moved registration logic to avoid code duplication.
 * Our controller looks simple and readable.

 You can add more customization by adding roles, policies etc., which I leave it for another post.

[Github repo link](https://github.com/SujithJr/laravel-auth-custom) for this project. It includes the blade files too.

Any questions.? Leave them in comments 🤠

See ya!

<img src="https://media.giphy.com/media/SFKepMDcBpVib3BdVh/giphy.gif"  width="300">