Mailgun and Password reminders

Standard

I’ve been using my own Mailgun package for most of the applications I’ve developed with Laravel but only recently I needed to implement it for Laravels password reminders.

Because the Mailgun package is not usable as a mail driver I had to change up a few things to get it working correctly. Let me show you how I did it.

Research

The first step is to find out where the reminder email is being sent from. Assuming we generated the RemindersController using the php artisan auth:reminders-controller command — as described in the Laravel Documentation — there should be a postRemind() method within the generated ReminderController.

/**
 * Handle a POST request to remind a user of their password.
 *
 * @return Response
 */
public function postRemind()
{
	switch ($response = Password::remind(Input::only('email')))
	{
		case Password::INVALID_USER:
			return Redirect::back()->with('error', Lang::get($response));

		case Password::REMINDER_SENT:
			return Redirect::back()->with('status', Lang::get($response));
	}
}

This method uses the Password::remind() method to send the email.
Password is a facade for Illuminate\Auth\Reminders\PasswordBroker. (See Documentation for all Facade Class References)

When we open up Illuminate\Auth\Reminders\PasswordBroker and take look at the __construct() method we can see it accepts an instance of \Illuminate\Mail\Mailer as the third parameter.

/**
 * Create a new password broker instance.
 *
 * @param  \Illuminate\Auth\Reminders\ReminderRepositoryInterface  $reminders
 * @param  \Illuminate\Auth\UserProviderInterface  $users
 * @param  \Illuminate\Mail\Mailer  $mailer
 * @param  string  $reminderView
 * @return void
 */
public function __construct(ReminderRepositoryInterface $reminders,
                            UserProviderInterface $users,
                            Mailer $mailer,
                            $reminderView)
{
	$this->users = $users;
	$this->mailer = $mailer;
	$this->reminders = $reminders;
	$this->reminderView = $reminderView;
}

The $this->mailer instance is then used in the sendReminder() method to send out the mail.

/**
 * Send the password reminder e-mail.
 *
 * @param  \Illuminate\Auth\Reminders\RemindableInterface  $user
 * @param  string   $token
 * @param  Closure  $callback
 * @return void
 */
public function sendReminder(RemindableInterface $user, $token, Closure $callback = null)
{
	// We will use the reminder view that was given to the broker to display the
	// password reminder e-mail. We'll pass a "token" variable into the views
	// so that it may be displayed for an user to click for password reset.
	$view = $this->reminderView;

	return $this->mailer->send($view, compact('token', 'user'), function($m) use ($user, $token, $callback)
	{
		$m->to($user->getReminderEmail());

		if ( ! is_null($callback)) call_user_func($callback, $m, $user, $token);
	});
}

Because the Mailgun package behaves almost the same as the default Mailer we can just swap it out.
We need to make sure the PasswordBroker class receives an instance of Mailgun instead of Mailer.

If we open up Illuminate/Auth/Reminders/ReminderServiceProvider and look at the registerPasswordBroker() method we can see that this method passes an instance of $app['mailer'] to the PasswordBroker.

/**
 * Register the password broker instance.
 *
 * @return void
 */
protected function registerPasswordBroker()
{
	$this->app->bindShared('auth.reminder', function($app)
	{
		// The reminder repository is responsible for storing the user e-mail addresses
		// and password reset tokens. It will be used to verify the tokens are valid
		// for the given e-mail addresses. We will resolve an implementation here.
		$reminders = $app['auth.reminder.repository'];

		$users = $app['auth']->driver()->getProvider();

		$view = $app['config']['auth.reminder.email'];

		// The password broker uses the reminder repository to validate tokens and send
		// reminder e-mails, as well as validating that password reset process as an
		// aggregate service of sorts providing a convenient interface for resets.
		return new PasswordBroker(

			$reminders, $users, $app['mailer'], $view

		);
	});
}

Hands on

Now we know what things to change we can get our hands dirty.

We need to overwrite two files;
- Illuminate\Auth\Reminders\ReminderServiceProvider
- Illuminate\Auth\Reminders\PasswordBroker

I’m using a custom namespace. For example purposes I named it Acme.
Make sure your custom namespace is autoloaded correctly.

Create the following file:
app/Acme/Auth/Reminders/PasswordBroker.php

Paste in the following:


namespace Acme\Auth\Reminders;

use Bogardo\Mailgun\Mailgun;
use Illuminate\Auth\Reminders\ReminderRepositoryInterface;
use Illuminate\Auth\UserProviderInterface;

class PasswordBroker extends \Illuminate\Auth\Reminders\PasswordBroker {

    /**
     * @param ReminderRepositoryInterface $reminders
     * @param UserProviderInterface $users
     * @param Mailgun $mailer
     * @param string $reminderView
     */
    public function __construct(ReminderRepositoryInterface $reminders, UserProviderInterface $users, Mailgun $mailer, $reminderView)
    {
        $this->users = $users;
        $this->mailer = $mailer;
        $this->reminders = $reminders;
        $this->reminderView = $reminderView;
    }

}

What we did was change construct method to accept an instance of Mailgun instead of Mailer.

Next up, create the following file:
app/Acme/Auth/Reminders/ReminderServiceProvider.php

And paste in the following:


namespace Acme\Auth\Reminders;

class ReminderServiceProvider extends \Illuminate\Auth\Reminders\ReminderServiceProvider
{
    /**
     * Register the password broker instance.
     *
     * @return void
     */
    protected function registerPasswordBroker()
    {
        $this->app->bindShared('auth.reminder', function($app)
        {
            // The reminder repository is responsible for storing the user e-mail addresses
            // and password reset tokens. It will be used to verify the tokens are valid
            // for the given e-mail addresses. We will resolve an implementation here.
            $reminders = $app['auth.reminder.repository'];

            $users = $app['auth']->driver()->getProvider();

            $view = $app['config']['auth.reminder.email'];

            // The password broker uses the reminder repository to validate tokens and send
            // reminder e-mails, as well as validating that password reset process as an
            // aggregate service of sorts providing a convenient interface for resets.
            return new PasswordBroker(

                $reminders, $users, $app->make('mailgun'), $view

            );
        });
    }
}

In here we’ve changed the third parameter for new PasswordBroker()
from $app['mailer'] to $app->make('mailgun').

Lastly, to make sure our new ReminderServiceProvider is being used instead of the default one we need to make one last change in app/config.php.
In the providers array, locate 'Illuminate\Auth\Reminders\ReminderServiceProvider' and remove it.
Next, add the new service provider: 'Acme\Auth\Reminders\ReminderServiceProvider'

And we’re done.

Now, all password reminder emails are sent with Mailgun.

2 thoughts on “Mailgun and Password reminders

  1. Dave

    Hi,

    thanks so much for posting this, just what I needed! Have got Mailgun working in my app, but I’m trying to get your method here working for password resets but I get an error:

    Class 'Acme\Auth\Reminders\ReminderServiceProvider' not found

    when I go to the /password/remind page… any ideas as to what I might be missing?

    Thanks!

    • chris

      Hi Dave,
      Sorry for the late reply.
      Have you managed to get it working yet?
      If not, please contact me or submit an issue on github and I’ll help you out.

Leave a Reply to Dave Cancel reply

Your email address will not be published. Required fields are marked *

You may use these HTML tags and attributes: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>