Registration

Introduction

You have heard people talking about the awesome features of authentication guards in Laravel and how we can use them to authenticate multiple user providers / models. But do not know where to begin. Don’t worry, we will cover nearly every puzzle about session based MultiAuth (that’s what most people say) in 3 chapters.

Before proceeding with how to get things done, i strongly recommend to go through documentation of Authentication in laravel

Authentication in Laravel 5.4

TL;DR

If you are getting bored and do not have patience or will to follow the rest of chapter. Then you can refer the repository. Check the commit history for details.

Repository for Multi Auth in Laravel

Why do we need it

There are instances where we wanted to have a separate authentications logic for administrators apart from default user, some may argue that authorization/roles can accomplish this goal. But there are situations where we want to have different rules and relationships for user and administrator.

Chapter I :: Registration

In the first chapter, we will cover registration of our custom user whom we will call SELLER (don’t like to call it admin). We will not be using new features of laravel 5.4 like Markdown Mail & Notifications & Fluent Routing because we want this steps to work in Laravel 5.3 too. So, Let’s dig in.

You can install a fresh copy of Laravel or can use your existing project (must be in v5.4 or v5.3)

Now let’s run Laravel’s authentication scaffolding

php artisan make:auth

Well you might be wondering, this topic is about MultiAuth,why should we care about laravel’s default user. You are right, this step is entirely optional, you can skip it if you want.

Let’s create a new model and migration for our custom user Seller.

php artisan make:model Seller -m

Open up the seller’s migration file, and add the necessary columns to the table.

public function up()
    {
        Schema::create('sellers', function (Blueprint $table) {
          $table->increments('id');
          $table->string('name');
          $table->string('email')->unique();
          $table->string('password');
          $table->rememberToken();
          $table->timestamps();
        });
    }

    public function down()
    {
        Schema::dropIfExists('sellers');
    }

Now we can run the migrations, this will create Sellers table along with other tables if any.

php artisan migrate

Setting Expectations

“In this series first we would be setting our exceptions, then would work together to accomplish them.”

Lets begin by adding registration related routes in web routes of our project. Open routes/web.php file and add the below routes for now.

//web.php

//other routes

Route::get('seller_register', 'SellerAuth\RegisterController@showRegistrationForm');
Route::post('seller_register', 'SellerAuth\RegisterController@register');

Route::get('/seller_home', function(){
  return view('seller.home');
});

You can replace the URL term seller_register with a name of your choice, but you have to be consistent with this name to avoid confusion.

Accomplish the Expectations

Now let’s work on the first route Route::get(‘seller_register’, ‘SellerAuth\RegisterController @showRegistrationForm’); . It says when GET request is made for seller_register page, laravel will call showRegistrationForm() method of SellerAuth\RegisterController.

Lets create our RegisterController for seller

php artisan make:controller SellerAuth/RegisterController

Lets begin editing our Registration controller (SellerAuth/RegisterController) by adding showRegistrationForm()

//RegisterController.php

namespace App\Http\Controllers\SellerAuth;

use Illuminate\Http\Request;
use App\Http\Controllers\Controller;

class RegisterController extends Controller
{
  //shows registration form to seller
  public function showRegistrationForm()
  {
      return view('seller.auth.register');
  }
}

Now we need to create our view, let’s begin with adding seller folder in the resources/views folder where we shall place all our views related to seller’s login and registration.

Before creating our registration page, let’s first create a layout.blade.php file in seller folder, which will be used by other views.

layouts

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="utf-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1">

    <!-- CSRF Token -->
    <meta name="csrf-token" content="{{ csrf_token() }}">

    <title>{{ config('app.name', 'MultiAuth') }}</title>

    <!-- Styles -->
    <link href="/css/app.css" rel="stylesheet">

    <!-- Scripts -->
    <script>
        window.Laravel = <?php echo json_encode([
            'csrfToken' => csrf_token(),
        ]); ?>
    </script>
</head>
<body>
    <div id="app">
        <nav class="navbar navbar-default navbar-static-top">
            <div class="container">
                <div class="navbar-header">

                    <!-- Collapsed Hamburger -->
                    <button type="button" class="navbar-toggle collapsed" data-toggle="collapse" data-target="#app-navbar-collapse">
                        <span class="sr-only">Toggle Navigation</span>
                        <span class="icon-bar"></span>
                        <span class="icon-bar"></span>
                        <span class="icon-bar"></span>
                    </button>

                    <!-- Branding Image -->
                    <a class="navbar-brand" href="{{ url('/') }}">
                        {{ config('app.name', 'Laravel') }}
                    </a>
                </div>

                <div class="collapse navbar-collapse" id="app-navbar-collapse">
                    <!-- Left Side Of Navbar -->
                    <ul class="nav navbar-nav">
                        &nbsp;
                    </ul>

                    <!-- Right Side Of Navbar -->
                    <ul class="nav navbar-nav navbar-right">
                        <!-- Authentication Links -->
                        @if (Auth::guard('web_seller')->guest())

                            <!--Seller Login and registration Links -->

                            <li><a href="{{ url('/seller_login') }}">Seller Login</a></li>
                            <li><a href="{{ url('/seller_register') }}">Seller Registration</a></li>
                        @else
                            <li class="dropdown">
                                <a href="#" class="dropdown-toggle" data-toggle="dropdown" role="button" aria-expanded="false">
                                    {{ Auth::guard('web_seller')->user()->name }} <span class="caret"></span>
                                </a>

                                <ul class="dropdown-menu" role="menu">
                                    <li>
                                        <a href="{{ url('/seller_logout') }}"
                                            onclick="event.preventDefault();
                                                     document.getElementById('logout-form').submit();">
                                            Logout
                                        </a>

                                        <form id="logout-form" action="{{ url('/seller_logout') }}" method="POST" style="display: none;">
                                            {{ csrf_field() }}
                                        </form>
                                    </li>
                                </ul>
                            </li>
                        @endif
                    </ul>
                </div>
            </div>
        </nav>

        @yield('content')
    </div>

    <!-- Scripts -->
    <script src="/js/app.js"></script>
</body>
</html>

We have defined new URLs and a strange word Auth::guard(‘web_seller’), do not worry we will cover them one by one.

Now let’s create the register.blade.php view in seller/auth folder, which laravel came across at showRegistrationForm() method in RegisterController.

Register

@extends('seller.layouts')

@section('content')
class="container"> div class="row"> div class="col-md-8 col-md-offset-2"> div class="panel panel-success"> div class="panel-heading">Register A Sellerdiv> div class="panel-body"> form class="form-horizontal" role="form" method="POST" action="{{ url('/seller_register') }}"> {{ csrf_field() }}
form-group{{ $errors->has('name') ? ' has-error' : '' }}"> name" class="col-md-4 control-label">Name
col-md-6"> name" type="text" class="form-control" name="name" value="{{ old('name') }}" required autofocus> @if ($errors->has('name')) help-block"> {{ $errors->first('name') }} @endif
</div>
form-group{{ $errors->has('email') ? ' has-error' : '' }}"> email" class="col-md-4 control-label">E-Mail Address
col-md-6"> email" type="email" class="form-control" name="email" value="{{ old('email') }}" required> @if ($errors->has('email')) help-block"> {{ $errors->first('email') }} @endif
</div>
form-group{{ $errors->has('password') ? ' has-error' : '' }}"> password" class="col-md-4 control-label">Password
col-md-6"> password" type="password" class="form-control" name="password" required> @if ($errors->has('password')) help-block"> {{ $errors->first('password') }} @endif
</div>
form-group"> password-confirm" class="col-md-4 control-label">Confirm Password
col-md-6"> password-confirm" type="password" class="form-control" name="password_confirmation" required>
</div>
form-group">
col-md-6 col-md-offset-4"> submit" class="btn btn-success"> Register Seller
</div> </form> </div> </div> </div> </div> </div> @endsection

Lets work on the next route Route::post(‘seller_register’, ‘SellerAuth\RegisterController@register’);, where we handle data POST’ed from register view. Let’s create register() method in our RegisterController.

//RegisterController.php

namespace App\Http\Controllers\SellerAuth;

use Illuminate\Http\Request;
use App\Http\Controllers\Controller;

class RegisterController extends Controller
{
    //shows registration form to seller
    public function showRegistrationForm()
    {
        return view('seller.auth.register');
    }

  //Handles registration request for seller
    public function register(Request $request)
    {

       //Validates data
        $this->validator($request->all())->validate();

       //Create seller
        $seller = $this->create($request->all());

        //Authenticates seller
        $this->guard()->login($seller);

       //Redirects sellers
        return redirect($this->redirectPath);
    }
}

Ok. Thats a lot of steps for one method, we will go through one step at a time.

The first step taken by this method is validation of the request sent from register view via validator() method. Therefore let’s create the validator() method in our RegisterController.


//RegisterController.php

namespace App\Http\Controllers\SellerAuth;

use Illuminate\Http\Request;
use App\Http\Controllers\Controller;

//Validator facade used in validator method
use Illuminate\Support\Facades\Validator;

class RegisterController extends Controller
{
    //shows registration form to seller
    public function showRegistrationForm()
    {
        return view('seller.auth.register');
    }

  //Handles registration request for seller
    public function register(Request $request)
    {

       //Validates data
        $this->validator($request->all())->validate();

       //Create seller
        $seller = $this->create($request->all());

        //Authenticates seller
        $this->guard()->login($seller);

       //Redirects sellers
        return redirect($this->redirectPath);
    }

    //Validates user's Input
    protected function validator(array $data)
    {
        return Validator::make($data, [
            'name' => 'required|max:255',
            'email' => 'required|email|max:255|unique:sellers',
            'password' => 'required|min:6|confirmed',
        ]);
    }
}

The next step taken by register() method is creation of seller . We need declare create() method for this purpose in RegisterController.

//RegisterController.php

namespace App\Http\Controllers\SellerAuth;

use Illuminate\Http\Request;
use App\Http\Controllers\Controller;

//Validator facade used in validator method
use Illuminate\Support\Facades\Validator;

//Seller Model
use App\Seller;

class RegisterController extends Controller
{
    //shows registration form to seller
    public function showRegistrationForm()
    {
        return view('seller.auth.register');
    }

  //Handles registration request for seller
    public function register(Request $request)
    {

       //Validates data
        $this->validator($request->all())->validate();

       //Create seller
        $seller = $this->create($request->all());

        //Authenticates seller
        $this->guard()->login($seller);

       //Redirects sellers
        return redirect($this->redirectPath);
    }

    //Validates user's Input
    protected function validator(array $data)
    {
        return Validator::make($data, [
            'name' => 'required|max:255',
            'email' => 'required|email|max:255|unique:sellers',
            'password' => 'required|min:6|confirmed',
        ]);
    }

    //Create a new seller instance after a validation.
    protected function create(array $data)
    {
        return Seller::create([
            'name' => $data['name'],
            'email' => $data['email'],
            'password' => bcrypt($data['password']),
        ]);
    }

}

A seller is created for our application. The next step would be to login this seller.

Laravel’s authentication facilities are made up of guards and user providers. Guards define how users are authenticated for each request. Lets create a guard() method in our controller which will return our custom guard for seller. This will be called in the next step of register() method.

//RegisterController.php

namespace App\Http\Controllers\SellerAuth;

use Illuminate\Http\Request;
use App\Http\Controllers\Controller;

//Validator facade used in validator method
use Illuminate\Support\Facades\Validator;

//Seller Model
use App\Seller;

//Auth Facade used in guard
use Auth;

class RegisterController extends Controller
{
    //shows registration form to seller
    public function showRegistrationForm()
    {
        return view('seller.auth.register');
    }

  //Handles registration request for seller
    public function register(Request $request)
    {

       //Validates data
        $this->validator($request->all())->validate();

       //Create seller
        $seller = $this->create($request->all());

        //Authenticates seller
        $this->guard()->login($seller);

       //Redirects sellers
        return redirect($this->redirectPath);
    }

    //Validates user's Input
    protected function validator(array $data)
    {
        return Validator::make($data, [
            'name' => 'required|max:255',
            'email' => 'required|email|max:255|unique:sellers',
            'password' => 'required|min:6|confirmed',
        ]);
    }

    //Create a new seller instance after a validation.
    protected function create(array $data)
    {
        return Seller::create([
            'name' => $data['name'],
            'email' => $data['email'],
            'password' => bcrypt($data['password']),
        ]);
    }

    //Get the guard to authenticate Seller
   protected function guard()
   {
       return Auth::guard('web_seller');
   }

}

Ok, I know what you might me thinking, what is Auth::guard(‘web_seller’), the Auth::guard() method is used by laravel for authentication, this returns default guard which can authenticate users. But we want to authenticate sellers, so we have tell laravel to authenticate our sellers with our custom guard ‘web_seller’.

So, let us now create our custom guard ‘web_seller’, let’s begin with adding new User Provider for our custom guard. The user provider tells laravel about the model/table it has to refer for custom guard.

To do this open the config/auth.php file and add new user provider seller to providers array.

//auth.php

    'providers' => [
        'users' => [
            'driver' => 'eloquent',
            'model' => App\User::class,
        ],

       //Seller user provider
        'sellers' => [
            'driver' => 'eloquent',  //We are using eloquent model
            'model' => App\Seller::class,
        ],
    ],

Now let’s create our new custom guard in the guards array of the same page config/auth.php. We need to specify the guard driver and user provider for our guard. We will be using session as guard driver (This tutorial is about session based login) and seller as user provider which we have already defined.

//auth.php

 'guards' => [
        'web' => [
            'driver' => 'session',
            'provider' => 'users',
        ],

      //Our new custom driver.
        'web_seller' => [
            'driver' => 'session',
            'provider' => 'sellers',
        ],
],

Our custom guard is ready to be used by laravel for authenticating sellers. The last step taken by our register() method in RegisterController is redirecting seller to a page when login is successful.

So, let’s declare the redirectPath property for the seller in RegisterController, this will redirect sellers to their home page.

//RegisterController.php

namespace App\Http\Controllers\SellerAuth;

use Illuminate\Http\Request;
use App\Http\Controllers\Controller;

//Validator facade used in validator method
use Illuminate\Support\Facades\Validator;

//Seller Model
use App\Seller;

//Auth Facade used in guard
use Auth;

class RegisterController extends Controller
{

    protected $redirectPath = 'seller_home';

    //shows registration form to seller
    public function showRegistrationForm()
    {
        return view('seller.auth.register');
    }

  //Handles registration request for seller
    public function register(Request $request)
    {

       //Validates data
        $this->validator($request->all())->validate();

       //Create seller
        $seller = $this->create($request->all());

        //Authenticates seller
        $this->guard()->login($seller);

       //Redirects sellers
        return redirect($this->redirectPath);
    }

    //Validates user's Input
    protected function validator(array $data)
    {
        return Validator::make($data, [
            'name' => 'required|max:255',
            'email' => 'required|email|max:255|unique:sellers',
            'password' => 'required|min:6|confirmed',
        ]);
    }

    //Create a new seller instance after a validation.
    protected function create(array $data)
    {
        return Seller::create([
            'name' => $data['name'],
            'email' => $data['email'],
            'password' => bcrypt($data['password']),
        ]);
    }

    //Get the guard to authenticate Seller
   protected function guard()
   {
       return Auth::guard('web_seller');
   }

}

We have defined our redirect property and have previously defied the view for it in our route Route::get(‘/seller_home’, function(){ return view(‘seller.home’);});

So let’s create a new view home.blade.php in the seller folder of views.

Home

@extends('seller.layouts')

@section('content')
class="container"> div class="row"> div class="col-md-8 col-md-offset-2"> div class="panel panel-success"> div class="panel-heading">Seller's Dashboarddiv> div class="panel-body"> Greetings.. Seller div> div> div> div> div> @endsection

Looks like we are finally done with all our steps for registering a new seller. But we are expecting an error, you might be wondering why. Laravel expects the user provider used by our custom guard to be an instance of Illuminate\Contracts\ Auth\Authenticatable but we have provided it App\Seller class, therefore it will throw an error message, test out what we accomplished till now if you don’t trust me 🙂

To correct this problem, we need to extend Illuminate\Foundation\Auth\User class in our Seller Model. This class implements Illuminate\Contracts\ Auth\Authenticatable interface and has declared all the necessary methods.

So let’s extend Illuminate\Foundation\Auth\User class and add mass assignable & hidden attributes in our Seller Model (app/Seller.php).

//Seller.php

namespace App;

//Class which implements Illuminate\Contracts\Auth\Authenticatable
use Illuminate\Foundation\Auth\User as Authenticatable;

class Seller extends Authenticatable
{
  //Mass assignable attributes
  protected $fillable = [
      'name', 'email', 'password',
  ];

  //hidden attributes
   protected $hidden = [
       'password', 'remember_token',
   ];

}

End of Chapter

Everything looks great, let’s test our application.

RegistrationHome Page

We have successfully registered a new Seller and logged him/her in to the system.

For practice, repeat this process for a new type of user model.

But wait, how do we log seller out of the system and login existing sellers… We cover these two topics in next chapter..

 

Login, Logout & Middleware

In our last chapter, we went through the process of creating a custom guard and registered a seller. In this chapter we will cover Login, Logout & Middleware features for our user model Seller.

TL;DR

If you are getting bored and do not have patience or will to follow the rest of chapter. Then you can refer the repository. Check the commit history for details.

Repository for Multi Auth in Laravel

Setting Expectations For Login & Logout

Lets begin this chapter by setting our expectations, which is adding routes for Login and Logout.

//web.php

//other routes

Route::post('seller_logout', 'SellerAuth\LoginController@logout');
Route::get('seller_login', 'SellerAuth\LoginController@showLoginForm');
Route::post('seller_login', 'SellerAuth\LoginController@login');

Accomplish the Expectations

Now lets create LoginController for seller

php artisan make:controller SellerAuth/LoginController

Lets work on our first route Route::post(‘seller_logout’, ‘SellerAuth\LoginController@logout’);

We need to add logout() method in our LoginController. (Since we did not logged out seller in previous chapter). But unlike previous chapter, where we created all methods in our controller, we will use AuthenticatesUsers trait this time. It will keep our controller page less bloated.

//LoginController.php

namespace App\Http\Controllers\SellerAuth;

use Illuminate\Http\Request;
use App\Http\Controllers\Controller;

//Class needed for login and Logout logic
use Illuminate\Foundation\Auth\AuthenticatesUsers;

class LoginController extends Controller
{
    //Trait
    use AuthenticatesUsers;
}

If we look into the properties of this trait which can be found at path vendor/laravel/framework /src/Illuminate/Foundation/ Auth/AuthenticatesUsers.php, we can observe that it includes lots of methods some of them are logout(), login(), showLoginForm(), gaurd() etc.. We will be using some these methods directly and override few of them.

NEVER EDIT ANYTHING DIRECTLY ANYTHING IN DEPENDENCY FILES( vendor folder ), IF THE LOGIC DOES NOT SUITE YOUR NEED, OVERRIDE THE LOGIC (METHOD OR OTHER PROPERTIES) IN OUR OWN CLASSES.

As you can see logout() method is already defined in the trait, so there is no need to define it again in our Controller. When we look at the first step of the logout() method we can notice that it uses guard method and calls logout() on it. The guard method of this trait returns an instance of default guard of our application, which is User guard. But we want to logout Seller not User. So we need to override guard() method in our LoginController.

//LoginController.php

namespace App\Http\Controllers\SellerAuth;

use Illuminate\Http\Request;
use App\Http\Controllers\Controller;

//Class needed for login and Logout logic
use Illuminate\Foundation\Auth\AuthenticatesUsers;

//Auth facade
use Auth;

class LoginController extends Controller
{
    //Trait
    use AuthenticatesUsers;

    //Custom guard for seller
    protected function guard()
    {
      return Auth::guard('web_seller');
    }
}

We had already created Logout link in the layouts.blade.php file earlier which sends post request to seller_logout url. Now we can logout the seller by clicking on the Logout dropdown.

We are done with Logout logic, let’s move forward with implementing Login logic.

Login

Let’s work on the second route Route::get(‘seller_login’, ‘SellerAuth\LoginController @showLoginForm’);

Now we need a showLoginForm() method, although its already defined in our trait, but returns login view for the User login, we want seller to view Seller login form, So let’s override this method in our LoginController.

//LoginController.php

namespace App\Http\Controllers\SellerAuth;

use Illuminate\Http\Request;
use App\Http\Controllers\Controller;

//Class needed for login and Logout logic
use Illuminate\Foundation\Auth\AuthenticatesUsers;

//Auth facade
use Auth;

class LoginController extends Controller
{
    //Trait
    use AuthenticatesUsers;

    //Custom guard for seller
    protected function guard()
    {
      return Auth::guard('web_seller');
    }

    //Shows seller login form
   public function showLoginForm()
   {
       return view('seller.auth.login');
   }
}

Now let’s create the view which we defined inside showLoginForm(). Add login.blade.php at resources/views/seller/auth folder.

Login

@extends('seller.layouts')

@section('content')
class="container"> div class="row"> div class="col-md-8 col-md-offset-2"> div class="panel panel-success"> div class="panel-heading">Seller Logindiv> div class="panel-body"> form class="form-horizontal" role="form" method="POST" action="{{ url('/seller_login') }}"> {{ csrf_field() }}
form-group{{ $errors->has('email') ? ' has-error' : '' }}"> email" class="col-md-4 control-label">E-Mail Address
col-md-6"> email" type="email" class="form-control" name="email" value="{{ old('email') }}" required autofocus> @if ($errors->has('email')) help-block"> {{ $errors->first('email') }} @endif
</div>
form-group{{ $errors->has('password') ? ' has-error' : '' }}"> password" class="col-md-4 control-label">Password
col-md-6"> password" type="password" class="form-control" name="password" required> @if ($errors->has('password')) help-block"> {{ $errors->first('password') }} @endif
</div>
form-group">
col-md-6 col-md-offset-4">
checkbox"> checkbox" name="remember" {{ old('remember') ? 'checked' : ''}}> Remember Me
</div> </div>
form-group">
col-md-8 col-md-offset-4"> submit" class="btn btn-success"> Login btn btn-link" href="{{ url('/seller_password/reset') }}"> Forgot Your Password?
</div> </form> </div> </div> </div> </div> </div> @endsection

Let’s work on third and final route Route::post(‘seller_login’, ‘SellerAuth\LoginController@login’);, where we handle data POST’ed from this form.

The login() method is already defined in AuthenticatesUsers trait. Lets summarize the steps taken by this method.

  1. It validates the login with validateLogin() method, this method checks if username and password are provided. There is no need to modify anything. Lets move to next step.
  2. It throttles the login attempt. So, there is no need for modification in this step too. (Login throttle needs its own tutorial. For now just assume that user’s IP would be blocked for a minute if he/she exceeds maximum failed login attempts). Lets move to next step.
  3. It attempts a login with seller’s username and password via attemptLogin() method. This method uses guard() method to login seller. We have already overridden the guard() method in previous steps to return an instance of our custom seller guard. So let’s move to next step.
  4. If attempt is successful then it uses sendLoginResponse() method to redirect Seller to redirectPath property. redirectPath is not yet defined in our LoginController, let’s do so.
//LoginController.php

namespace App\Http\Controllers\SellerAuth;

use Illuminate\Http\Request;
use App\Http\Controllers\Controller;

//Class needed for login and Logout logic
use Illuminate\Foundation\Auth\AuthenticatesUsers;

//Auth facade
use Auth;

class LoginController extends Controller
{
    //Where to redirect seller after login.
    protected $redirectTo = '/seller_home';

    //Trait
    use AuthenticatesUsers;

    //Custom guard for seller
    protected function guard()
    {
      return Auth::guard('web_seller');
    }

    //Shows seller login form
   public function showLoginForm()
   {
       return view('seller.auth.login');
   }
}

That’s it. We are done with the Seller’s Login logic. If you have noticed, one of the good thing about using the traits is less bloated Controller Pages. (Yeah.. I know i should have used it in first chapter too)

Setting Expectations For Middleware

Middleware are just like filters for incoming requests for your application. If you are new to Middleware in Laravel, then i would suggest you to go through documentation section of laravel.

MiddleWares in Laravel

You might have noticed that we are able to access the login and registration pages for seller even after a successful login. These pages should be only be visible to people who are not logged in. We also might want to restrict guests/non-logged in sellers from accessing the seller’s homepage.

We can use laravel middleware to create this restrictions.

//routes.php

//Logged in users/seller cannot access or send requests these pages
Route::group(['middleware' => 'seller_guest'], function() {

Route::get('seller_register', 'SellerAuth\RegisterController@showRegistrationForm');
Route::post('seller_register', 'SellerAuth\RegisterController@register');
Route::get('seller_login', 'SellerAuth\LoginController@showLoginForm');
Route::post('seller_login', 'SellerAuth\LoginController@login');

});

//Only logged in sellers can access or send requests to these pages
Route::group(['middleware' => 'seller_auth'], function(){

Route::post('seller_logout', 'SellerAuth\LoginController@logout');
Route::get('/seller_home', function(){
  return view('seller.home');
});

});

We have defined two middlewares, seller_guest which will restrict access of pages when seller is logged in and seller_auth which restricts access of certain pages to guests/non-logged in sellers.

Accomplish Expectations

The first step which we need to take is creating of middlewares classes, one for seller_guest and another for seller_auth.

php artisan make:middleware AuthenticateSeller
php artisan make:middleware RedirectIfSellerAuthenticated

Let’s edit our first middleware, AuthenticateSeller which can be found at app/Http/Middleware folder.

This middleware will restricting requests for guest/non-logged in sellers. Which means it will allow only logged in seller to move to the next layers of application.

//AuthenticateSeller.php

namespace App\Http\Middleware;

use Closure;

//Auth Facade
use Auth;

class AuthenticateSeller
{
   public function handle($request, Closure $next)
   {
       //If request does not comes from logged in seller
       //then he shall be redirected to Seller Login page
       if (! Auth::guard('web_seller')->check()) {
           return redirect('/seller_login');
       }

       return $next($request);
   }
}

Now let’s edit the other middleware RedirectIfSellerAuthenticated, which can be found in the same middleware folder.

This middleware will restricting requests for logged in sellers. This means it will not allow logged in seller to move to the next layers of application and will be redirected back.

//RedirectIfSellerAuthenticated.php

namespace App\Http\Middleware;

use Closure;

//Auth Facade
use Auth;

class RedirectIfSellerAuthenticated
{

  public function handle($request, Closure $next)
  {
      //If request comes from logged in user, he will
      //be redirect to home page.
      if (Auth::guard()->check()) {
          return redirect('/home');
      }

      //If request comes from logged in seller, he will
      //be redirected to seller's home page.
      if (Auth::guard('web_seller')->check()) {
          return redirect('/seller_home');
      }
      return $next($request);
  }
}

If you have noticed, we are checking if request came from logged in user or seller. We are doing this because we do not want any logged in person whether its seller or user accessing certain pages.

We added logic in middleware classes, let’s register them with the names which we have already defined in our routes. To do this, openapp/Http/Kernel.php file and append our middlewares to $routeMiddleware property.

//kernel.php

protected $routeMiddleware = [
         //other Middlewares

         //add custom middlewares here as key and value pair.
         'seller_auth' => \App\Http\Middleware\AuthenticateSeller::class,
         'seller_guest' => \App\Http\Middleware\RedirectIfSellerAuthenticated::class,
     ];

End of Chapter

We are done.. Lets test out what we accomplished.

Login PageHome Page

But what happens when a seller forgets his/her password. We will cover Reseting passwords for seller in our next chapter.

Reset Password

If you have followed along the previous chapters, you might have reached a point where you are able to login/register with a model of your choice.(Other than laravel’s default user model). But now the real problem arises, how do we reset password for persons getting authenticated to system with this new model (Seller in our case).

TL;DR

If you are getting bored and do not have patience or will to follow the rest of chapter. Then you can refer the repository. Check the commit history for details.

Repository for Multi Auth in Laravel

How it works for laravel

Laravel resets password with password brokers, we will discuss about creation of our custom broker for seller model in the later part of the chapter. I would suggest you to go through the documentation

Password Reset in laravel

Notifications in laravel

Setting Expectations

Like every time, let’s first set our expectations in routes file


//other routes

Route::group(['middleware' => 'seller_guest'], function() {
Route::get('seller_register', 'SellerAuth\RegisterController@showRegistrationForm');
Route::post('seller_register', 'SellerAuth\RegisterController@register');
Route::get('seller_login', 'SellerAuth\LoginController@showLoginForm');
Route::post('seller_login', 'SellerAuth\LoginController@login');

//Password reset routes
Route::get('seller_password/reset', 'SellerAuth\ForgotPasswordController@showLinkRequestForm');
Route::post('seller_password/email', 'SellerAuth\ForgotPasswordController@sendResetLinkEmail');
Route::get('seller_password/reset/{token}', 'SellerAuth\ResetPasswordController@showResetForm');
Route::post('seller_password/reset', 'SellerAuth\ResetPasswordController@reset');

//other routes
});

If you have noticed, i have placed password reset routes in the group with seller_guest middleware, because we do not want logged in user/seller accessing them. Only a non-logged in person should access them.

Accomplish the Expectations

Ok.. Let’s create our ForgotPasswordController. This controller will be responsible for sending password reset notifications to seller.

php artisan make:controller SellerAuth/ForgotPasswordController

Now let’s write the logic for our first route Route::get(‘seller_password/reset’, ‘SellerAuth \ForgotPasswordController @showLinkRequestForm’);

We will be using SendsPasswordResetEmails trait to simply few steps in our ForgotPasswordController.

//ForgotPasswordController.php

namespace App\Http\Controllers\SellerAuth;

use Illuminate\Http\Request;
use App\Http\Controllers\Controller;

//Trait
use Illuminate\Foundation\Auth\SendsPasswordResetEmails;

class ForgotPasswordController extends Controller
{
    //Sends Password Reset emails
    use SendsPasswordResetEmails;
}

If we look at the various methods of this trait which can be found at vendor/laravel/framework /src/Illuminate/Foundation/Auth, we can observe that showLinkRequestForm() method is already defined, but it returns the view used by laravel’s default user. Therefore, we need to override this method in our ForgotPasswordController.

//ForgotPasswordController.php

namespace App\Http\Controllers\SellerAuth;

use Illuminate\Http\Request;
use App\Http\Controllers\Controller;

//Trait
use Illuminate\Foundation\Auth\SendsPasswordResetEmails;

class ForgotPasswordController extends Controller
{
    //Sends Password Reset emails
    use SendsPasswordResetEmails;

    //Shows form to request password reset
    public function showLinkRequestForm()
    {
        return view('seller.passwords.email');
    }
}

Now let’s create the email view in the resources/views/seller/passwords folder.

Email View


//email.blade.php

@extends('seller.layouts')

<!-- Main Content -->
@section('content')
class="container"> div class="row"> div class="col-md-8 col-md-offset-2"> div class="panel panel-success"> div class="panel-heading">Reset Passworddiv> div class="panel-body"> @if (session('status')) div class="alert alert-success"> {{ session('status') }}
@endif <form class="form-horizontal" role="form" method="POST" action="{{ url('/seller_password/email') }}"> {{ csrf_field() }}
form-group{{ $errors->has('email') ? ' has-error' : '' }}"> email" class="col-md-4 control-label">E-Mail Address
col-md-6"> email" type="email" class="form-control" name="email" value="{{ old('email') }}" required> @if ($errors->has('email')) help-block"> {{ $errors->first('email') }} @endif
</div>
form-group">
col-md-6 col-md-offset-4"> submit" class="btn btn-success"> Send Password Reset Link
</div> </form> </div> </div> </div> </div> </div> @endsection

We have created a view where seller can request password reset link which shall be sent to their registered email. Now let’s work on handling the data POST’ed from this view with sendResetLinkEmail() method, for which we have already defined the routeRoute::post(‘seller_password/email’, ‘SellerAuth \ForgotPasswordController @sendResetLinkEmail’);

But wait, SendsPasswordResetEmails trait declared inside our ForgotPasswordController has already defined sendResetLinkEmail() method. Let’s look into this method and analyze each step taken by it.

  1. Method validates seller’s request and checks whether email is provided by seller. So, there is no need to modify this step.
  2. Method calls for a broker() method and uses sendResetLink() to send password reset link and stores the result in $response variable. The broker() method defined in this trait returns default user’s password broker. But we want to reset password of seller. Therefore we need to override broker() method in our ForgotPasswordController. We will discuss about sendResetLink() method later.
  3. Method evaluates $response variable, if response is equal to Password::RESET_LINK_SENT , then system sends back success response. If not, then it returns the error message.

After analyzing the above steps, we have concluded that we need to override broker() method in our ForgotPasswordController.

//ForgotPasswordController

namespace App\Http\Controllers\SellerAuth;

use Illuminate\Http\Request;
use App\Http\Controllers\Controller;

//Trait
use Illuminate\Foundation\Auth\SendsPasswordResetEmails;

//Password Broker Facade
use Illuminate\Support\Facades\Password;

class ForgotPasswordController extends Controller
{
    //Sends Password Reset emails
    use SendsPasswordResetEmails;

    //Shows form to request password reset
    public function showLinkRequestForm()
    {
        return view('seller.passwords.email');
    }

    //Password Broker for Seller Model
    public function broker()
    {
         return Password::broker('sellers');
    }
}

The broker() method returns our own custom password broker for seller model which we have to create now.

For this, let’s open config/auth.php and append our password broker to passwords array

    'passwords' => [
        'users' => [
            'provider' => 'users',
            'table' => 'password_resets',
            'expire' => 60,
        ],

        //Seller password broker
       'sellers' => [
            //user provider for seller
           'provider' => 'sellers',
            //table to store password reset tokens for seller
           'table' => 'seller_password_resets',
           //expire time for these tokens in minutes
           'expire' => 60,
       ],
    ],

In our password broker we mentioned our user provider (seller), expire time of our tokens and a table which stores these token.

The next step would be creation of seller_password_resets table. Let’s create migration for this table

php artisan make:migration create_sellers_password_reset_table --create="seller_password_resets"

Open the migrations file and add the email and token columns.


    public function up()
    {
        Schema::create('seller_password_resets', function (Blueprint $table) {
          $table->string('email')->index();
          $table->string('token')->index();
          $table->timestamp('created_at')->nullable();
        });
    }

    public function down()
    {
        Schema::dropIfExists('seller_password_resets');
    }

Now let’s migrate this table

php artisan migrate

Lets look again at SendsPasswordResetEmails trait, as explained earlier the broker() calls for sendResetLink() method in the second step of sendResetLinkEmail() method. So what is this method and where does it comes from.

The method sendResetLink() is defined in the PasswordBroker class of vendor/laravel/src /framework/Illuminate /Auth/Passwords folder. Let’s look into this method and evaluate the steps

  1. It uses getUser() method to check if seller is present in database and if the model is an instance of CanResetPasswordContract interface. It fetches the model data and stores to $user variable. So we need to handle this part in our Seller model class. If not, exception/error will be displayed.
  2. It calls sendPasswordResetNotification on $user variable and passes newly generated token in it. We also need to handle this part in our Seller model class.
  3. Lastly it returns response informing us that reset password link has been sent to the email.

So there are two things we need to do, our Seller Model should implement CanResetPasswordContract and should have sendPasswordResetNotification() defined.

Don’t be scared. We have already extended the class Illuminate\Foundation\Auth\User in Seller model in our first chapter (Registration). This class has already implemented CanResetPasswordContract interface and uses Illuminate\Auth \Passwords\CanResetPassword trait which has the declared sendPasswordResetNotification() method.

In this Illuminate\Auth \Passwords\CanResetPassword trait, the method sendPasswordResetNotification() notifies with ResetPasswordNotificationclass. But wait, this is the notification is for default User not Seller.

To avoid ambiguous situation, we need to override sendPasswordResetNotification() method in seller model and have the notification sent to our own custom notification class. So, let’s first create SellerResetPasswordNotification class.

php artisan make:notification SellerResetPasswordNotification

Open this class SellerResetPasswordNotification which is placed at app/Notifications folder and add content which should include url to for our next route Route::get(‘seller_password/reset /{token}’, ‘SellerAuth\ResetPasswordController @showResetForm’);

//SellerResetPasswordNotification.php

namespace App\Notifications;

use Illuminate\Bus\Queueable;
use Illuminate\Notifications\Notification;
use Illuminate\Contracts\Queue\ShouldQueue;
use Illuminate\Notifications\Messages\MailMessage;

class SellerResetPasswordNotification extends Notification
{
    //Places this task to a queue if its enabled
    use Queueable;

    //Token handler
    public $token;

    public function __construct($token)
    {
        $this->token = $token;
    }

    //Notifications sent via email
    public function via($notifiable)
    {
        return ['mail'];
    }

    //Content of email sent to the Seller
    public function toMail($notifiable)
    {
        return (new MailMessage)
        ->line('You are receiving this email because we received a password reset request for your account.')
        ->action('Reset Password', url('seller_password/reset', $this->token))
        ->line('If you did not request a password reset, no further action is required.');
    }

}

We have created a notification class for seller. Now let’s complete the pending task, which is overriding sendPasswordResetNotification() method in Seller model. Open app/Seller.php, add this method and pass the token.

//seller.php

namespace App;

use Illuminate\Foundation\Auth\User as Authenticatable;

//Trait for sending notifications in laravel
use Illuminate\Notifications\Notifiable;

//Notification for Seller
use App\Notifications\SellerResetPasswordNotification;

class Seller extends Authenticatable
{

 // This trait has notify() method defined
  use Notifiable;

  //Mass assignable attributes
  protected $fillable = [
      'name', 'email', 'password',
  ];

  //hidden attributes
  protected $hidden = [
      'password', 'remember_token',
  ];

  //Send password reset notification
  public function sendPasswordResetNotification($token)
  {
      $this->notify(new SellerResetPasswordNotification($token));
  }
}

Lets test by using forgot password link to send email to exiting seller.

Forgot_Password_LinkForgot_Password_Page

Received Email

Now clicking on the password reset link mentioned in email will land seller us on our next route Route::get(‘seller_password/reset /{token}’, ‘SellerAuth \ResetPasswordController @showResetForm’);

Let’s create the ResetPasswordController.

php artisan make:controller SellerAuth/ResetPasswordController

We shall use ResetsPasswords trait placed at vendor/laravel /framework/src/llluminate /foundation/Auth in our ResetPasswordController. This trait has all the methods we need to reset seller’s password. We might need to override few methods.

//ResetPasswordController.php

namespace App\Http\Controllers\SellerAuth;

use Illuminate\Http\Request;
use App\Http\Controllers\Controller;

use Illuminate\Foundation\Auth\ResetsPasswords;

class ResetPasswordController extends Controller
{
    //trait for handling reset Password
    use ResetsPasswords;
}

If we look into ResetsPasswords trait we can observe that showResetForm() used in route is already defined but it returns view used for resetting default user’s password. So we need to override this method in our ResetPasswordController and return reset password view for seller.

//ResetPasswordController.php

use Illuminate\Http\Request;
use App\Http\Controllers\Controller;

use Illuminate\Foundation\Auth\ResetsPasswords;

class ResetPasswordController extends Controller
{
    //trait for handling reset Password
    use ResetsPasswords;

    //Show form to seller where they can reset password
    public function showResetForm(Request $request, $token = null)
    {
        return view('seller.passwords.reset')->with(
            ['token' => $token, 'email' => $request->email]
        );
    }
}

We need to create a new view reset.blade.php at resources/views/seller/passwords folder.

Reset View

@extends('seller.layouts')

@section('content')
class="container"> div class="row"> div class="col-md-8 col-md-offset-2"> div class="panel panel-success"> div class="panel-heading">Reset Passworddiv> div class="panel-body"> @if (session('status')) div class="alert alert-success"> {{ session('status') }}
@endif <form class="form-horizontal" role="form" method="POST" action="{{ url('/seller_password/reset') }}"> {{ csrf_field() }} <input type="hidden" name="token" value="{{ $token }}">
form-group{{ $errors->has('email') ? ' has-error' : '' }}"> email" class="col-md-4 control-label">E-Mail Address
col-md-6"> email" type="email" class="form-control" name="email" value="{{ $email or old('email') }}" required autofocus> @if ($errors->has('email')) help-block"> {{ $errors->first('email') }} @endif
</div>
form-group{{ $errors->has('password') ? ' has-error' : '' }}"> password" class="col-md-4 control-label">Password
col-md-6"> password" type="password" class="form-control" name="password" required> @if ($errors->has('password')) help-block"> {{ $errors->first('password') }} @endif
</div>
form-group{{ $errors->has('password_confirmation') ? ' has-error' : '' }}"> password-confirm" class="col-md-4 control-label">Confirm Password
col-md-6"> password-confirm" type="password" class="form-control" name="password_confirmation" required> @if ($errors->has('password_confirmation')) help-block"> {{ $errors->first('password_confirmation') }} @endif
</div>
form-group">
col-md-6 col-md-offset-4"> submit" class="btn btn-success"> Reset Password
</div> </form> </div> </div> </div> </div> </div> @endsection

Now let’s handle the data POST’ed from this form, for which we have defined route Route::post(‘seller_password/reset’, ‘SellerAuth \ResetPasswordController@reset’);

The ResetsPasswords trait used in our ResetPasswordController already has reset() defined. Let’s look into this method and analyze each step taken by this method.

  1. It validates the form request to check whether all the necessary fields are present in the request or not. No need to modify this step.
  2. It calls the reset() method on the password broker which resets password of seller and persists it in sellers database. The broker() method of this class returns laravel’s default user’s password broker. Therefore we need to override this method in our ResetPasswordController class.
  3. It authenticates the Seller with guard() method. The guard() method returns laravel’s default user’s authentication guard. Therefore we need to override this method in our ResetPasswordController class.
  4. If password reset is successful, then seller is redirected to $redirectTo path. We need to define this property within our ResetPasswordController class.
  5. If password reset fails, seller will be redirected back to the reset view and shown an error message.

So, we have concluded that we need to override broker() and guard() method and declare $redirectTo property in ResetPasswordController class.

//ResetPasswordController.php

namespace App\Http\Controllers\SellerAuth;

use Illuminate\Http\Request;
use App\Http\Controllers\Controller;

use Illuminate\Foundation\Auth\ResetsPasswords;

//Auth Facade
use Illuminate\Support\Facades\Auth;

//Password Broker Facade
use Illuminate\Support\Facades\Password;

class ResetPasswordController extends Controller
{
    //Seller redirect path
    protected $redirectTo = '/seller_home';

    //trait for handling reset Password
    use ResetsPasswords;

    //Show form to seller where they can save new password
    public function showResetForm(Request $request, $token = null)
    {
        return view('seller.passwords.reset')->with(
            ['token' => $token, 'email' => $request->email]
        );
    }

    //returns Password broker of seller
    public function broker()
    {
        return Password::broker('sellers');
    }

    //returns authentication guard of seller
    protected function guard()
    {
        return Auth::guard('web_seller');
    }
}

End of Chapter

Advertisements