Laravel Native Multi-auth Password Reset

This pertains to Laravel 5.4, and probably above.

Example: You have another auth provider called 'customer'.

Tldr;

  • Copy both existing password controllers
  • Add a boker method in new ForgotPasswordController
  • Add a showLinkRequestForm in new ForgotPasswordController and point it to a new view
  • Update new ResetPasswordController: Set $redirectTo path set __constructt() middleware to guest: Add broker method Add showResetForm method, pointing to new view

  • Create new password views (Copy existing /views/auth/passwords) Update the post action in both email & reset templates. Change the headings of each templete so can distigiwsh Update login page template 'forgot my password' link

  • Create new password broker (config/auth.php)

  • Edit the related user model Add method 'sendPasswordResetNotification' Make sure you import the notification (use App\Notifica...) Make sure you import (& use) Notifiable (use Illumina..\Notifiations\Notifiable)
  • Create new ResetPasswordNotification Remember to change the url route in the email message
  • Add routes to your new forgotten passsword/Reset controllers

Step 1 - Duplicate ForgotPasswordController & ResetPasswordController

Found in your: /var/www/app/Http/Controllers/Auth
The existing ForgotPasswordController & ResetPasswordController controllers can be used as templates.

cd /var/www/app/Http/Controllers/Auth
cat ForgotPasswordController.php > CustomerForgotPasswordController.php
cat ResetPasswordController.php > CustomerResetPasswordController.php

Rename the contoller classses

Because we just copied them, we need to rename is class appropriately:
vim CustomerForgotPasswordController.php vim CustomerResetPasswordController.php

Change the class names accordingly:
Don't change your existing ForgotPasswordController or ResetPasswordController controllers!
- ForgotPasswordController becomes CustomerForgotPasswordController - ResetPasswordController becomes CustomerResetPasswordController

Run composer dumpautoload to update the autoloader because of new classes in the classmap (see Composer Docs).

Step 2 Add the Password class to each Forgot/Reset password controller

The Password class (vendor/Illuminate/Support/Facades/Password.php)extends the Facade class. It allows access to the static member functions that the Password class provides.

Edit each of the new controllers and add
use Password; To include the password Facades.

Step 3 Add the Auth & Request Facade to the new CustomerResetPasswordController

use Auth; use Illuminate\Http\Request;

Step 3.1 Ass the guard() method to ResetPasswordController

protected function guard()
{
    return Auth::guard('customer');
}

Step 4 Set redirect location upon successful reset & login

Alter the protected $redirectTo to where the customer should be directed to after resetting their password:

It will be defaulted to /home since we copied it earlier.

File: CustomerResetPasswordController.php
protected $redirectTo = '/customer';

Step 5 Set guest middle ware on each password controllers __construct

This prevents already logged in users from resetting their password
e.g.
public function __construct() { $this->middleware('customer'); }

Step 6 - Add a new password broker for your user type

File: www/config/auth.php
Find "Resetting Passwords" and you'll see the array of password brokers.
This requires you to add the name of the auth provider you with to use (e.g. customer').

Your providers array should be used as reference for the correct name of the provider.

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

Note the 'table' element is password_resets for both user types. This is OK, but you may simply create another table if desired.

Step 7 Use new password broker in your new password controllers

In both

  • CustomerForgotPasswordController.php
  • CustomerResetPasswordController.php

Add the following:

protected function broker()
{
    return Password::broker('customers');
}

The argument passed to broker must be the one added in your www/config/auth.php within the passwords array.

Step 8 - Create routes for you new password reset handlers

Copy the existing password routes, and then prefix them with the user type of the controllers you've just created (Customer) in this example.

Route::prefix('customer')->group(function() {
//Customer Password Reset routes Route::post('/password/email','Auth\[email protected]')->name('customer.password.email');
Route::post('/password/reset', 'Auth\[email protected]');
Route::get('/password/reset', 'Auth\[email protected]')->name('customer.password.request');                                     
Route::get('/password/reset/{token}', 'Auth\[email protected]')->name('customer.password.reset');
});

Step 9 - Create Custom password Views for the user type

Navigate to /var/www/resources/views/auth/passwords

  • email.blade.php
  • reset.blade.php

Create copies of these, one for each user type you have in your multi-auth set-up e.g:

  • email.blade.php
  • reset.blade.php
  • customer-email.blade.php
  • customer-reset.blade.php

Edit the post action for customer-email.blade.php and change it to customer.password.email (referring to the named routes we just created in step 8). This ensures the request gets posted to the correct controller!

Edit the template to make it obvious this is the customer reset password form. Change the heading for example.

Step 10 - Override showLinkRequestForm

In your new CustomerForgotPasswordController, override the showLinkRequestForm method to direct users to the correct reset password form.

You can find the template showLinkRequestForm method (which the standard auth (web) provider uses directly) in
vendor/laravel/framework/src/Illuminate/Foundation/Auth/SendsPasswordResetEmails.php:

/** 
 * Display the form to request a password reset link.
 *
 * @return \Illuminate\Http\Response
 */
public function showLinkRequestForm()
{   
    return view('auth.passwords.email');
}

Open your CustomerForgotPasswordController and implement this method, but change the returned view to 'auth.passwords.customer-email'. Example:

/** 
 * Display the form to request a password reset link.
 *
 * @return \Illuminate\Http\Response
 */
public function showLinkRequestForm()
{   
    return view('auth.passwords.customer-email');
}

Step 11 - Create Email Notification for user type

Navigate to /var/www/vendor/laravel/framework/src/Illuminate/Auth/Passwords
Open the file CanResetPassword.php the and locate the method sendPasswordResetNotification. Copy this method into you Customer model (in this example, your users will be different!)

Place the following in your App/Customer.php model, making sure you generate a new Notification for this user type (see step 12)
/** * Send the password reset notification. * * @param string $token * @return void */ public function sendPasswordResetNotification($token) {
$this->notify(new CustomerResetPasswordNotification($token)); }

Step 12 - Create CustomerResetPasswordNotification

A notification class needs to be created for the sending of email alert. Use artisan from the command line to achieve this:

php artisan make:notification CustomerResetPasswordNotification

This will create a new Notification class CustomerResetPasswordNotification in your app\Notifications folder.

Edit your new email notification: vim app/Notifications/CustomerResetPasswordNotification.php

Add a $token public property, and pass this to the __construct:

File: app/Notifications/CustomerResetPasswordNotification.php

public $token;

/** 
 * Create a new notification instance.
 *
 * @return void
 */
public function __construct($token)
{   
    $this->token = $token;
}

If you're wondering were the $token value comes from, it gets passed to the notification be the call to sendPasswordResetNotification() in the Customer model (app/Customer.php) we just added.

Secondly, in the same file (CustomerResetPasswordNotification), customise the url action and email message that gets sent to the user:

/** 
 * Get the mail representation of the notification.
 *
 * @param  mixed  $notifiable
 * @return \Illuminate\Notifications\Messages\MailMessage
 */
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', route('customer.password.reset', $thi->token))
                ->line('If you did not request a password reset, no further action is required.');
}

Note the use of the route() function, which is given the route name (customer.password.reset) which we created earlier, and it's passed the $token to be able to form a password reset link.

Step 11 - Add the Notification class to your user model

Edit your model vim app/Customer.php
and include the CustomerResetPasswordNotification you just created:

use App\Notifications\CustomerResetPasswordNotification;

class Customer extends Authenticatable
{
  ...
}

Step 12 - Add showResetForm method to your CustomerResetController

Navigate to file: /var/www/vendor/laravel/framework/src/Illuminate/Foundation/Auth/ResetsPasswords.php

Copy the showResetForm into your CustomerResetPasswordController.php and change the name of the returned view to auth.passwords.customer-reset.

/** 
 * Display the password reset view for the given token.
 *
 * If no token is present, display the link request form.
 *
 * @param  \Illuminate\Http\Request  $request
 * @param  string|null  $token
 * @return \Illuminate\Contracts\View\Factory|\Illuminate\View\View
 */
public function showResetForm(Request $request, $token = null)
{   
    return view('auth.passwords.customer-reset')->with(
        ['token' => $token, 'email' => $request->email]
    );
}