Be part of JetBrains PHPverse 2026 on June 9 – a free online event bringing PHP devs worldwide together.

pstephan1187's avatar

Laravel 5 Password Broker

There seems to be a lot of magic surrounding the PasswordBroker that comes with Laravel 5 out of the box. A couple questions:

  1. Where in the code is the Illuminate\Auth\Passwords\PasswordBroker class associated with the Illuminate\Contracts\Auth\PasswordBroker interface? It is not in the App\Providers\AppServiceProvider or in the bootstrap/app.php file. Where is it? Where can I override it?
  2. I see that it (the default password broker class) is being passed a mailer contract in the constructor. I am using mailgun in my app, but the PB (password broker) is not utilizing mail gun. How can I make sure mailgun is used?

Thanks for the help

0 likes
21 replies
kfirba's avatar
kfirba
Best Answer
Level 50

@pstephan1187 Hello there :)

  1. You can find it at vendor/laravel/framework/src/Illuminate/Foundation/Application.php at line 820:
public function registerCoreContainerAliases()
{
    $aliases = array(
    // .. bunch more
    'auth.password'  => ['Illuminate\Auth\Passwords\PasswordBroker', 'Illuminate\Contracts\Auth\PasswordBroker'],
    // .. and more
    );
}

\2. It seems like the only occasion the PasswordBroker class is using the mailer is for sending a reset email. I don't see a reason why it won't use your configured mail provider. You can take a look at vendor/laravel/framework/src/Illuminate/Mail/MailServiceProvider.php and look in the method registerSwiftMailer() which calls to registerSwiftTransport() which eventually instantiate a TransportManager class. Taking a look at that class at: vendor/laravel/framework/src/Illuminate/Mail/TransportManager.php you can see that this class reads your configuration file.

Taking it further, you can take a look at vendor/laravel/framework/src/Illuminate/Auth/Passwords/PasswordResetServiceProvider.php . Now look for the method registerPasswordBroker() which is at line 32. The method is well documented but I think what you are looking for is at line 48:

return new PasswordBroker(
    $tokens, $users, $app['mailer'], $view
);                  

As you can see it resolves the mailer class from the IoC container (take a look above at the MailServiceProvider). You can implement your own PasswordBroker instance (make sure you implement the contracts needed) and simply replace it with your own implementation.

1 like
pstephan1187's avatar

Wow. Thanks a lot for all that. I will go in and try to get my own implementation set up now. I was hoping the password broker class was assigned somewhere in the app folder for easy overriding, but maybe I can override just the same. I appreciate you pointing me in the right direction.

kfirba's avatar

@pstephan1187 No problem :)

You can override it if you re-register the password.auth key in the IoC container. Good luck!

perkola's avatar

@kfirba I want to override the PasswordBroker but I don't know how or where to re-register the key in the IoC container. Can you be more specific? Thanks!

kfirba's avatar

@will3 Hey.

You can do so by registering the key again in AppServiceProvider or any other custom provider you want to create. Once you decide which service provider you want to put it in, add this code to your register method in the service provider:

app()->bind('password.auth', function() {
    // your binding logic
});

// or, you can bind a class
app()->bind('password.auth', 'App/Authentication/Password/CustomPasswordBroker');

Note: If needed, you can also use the singleton method instead of the bind method.

1 like
perkola's avatar

@kfirba thanks for a fast reply.

This works if i register the key in AppServiceProvider but it does not work when i register it in a custom service provider located in a workbench package. I have registered my package service provider in config/app.php and I've used the correct namespace. I get the Target [...] is not instantiable. exception.

kfirba's avatar

@perkola I'm not sure what you are trying to do but it seems like you have tried to bind an interface or an abstract class as a replacement for password.auth .

I haven't yet released any package so I may not understand what you mean, but it would be helpful if you could post your code.

perkola's avatar

@kfirba Sorry I will try to be more specific. In my Custom\Package\CustomServiceProvider I have the following:

public function register()
{
    $this->app->singleton('Custom\Package\Contracts\PasswordBroker', function($app)
    {
        ...
        return new \Custom\Package\PasswordBroker(...);
    });
}

Note that this exact code works if placed it in App\Providers\AppServiceProvider. And in my config/app.php I have the following line:

'providers' => [
    ...
    'Custom\Package\CustomServiceProvider',
    ...

],

The code is the same as it seems to be loaded as a provider but it does not work. Any ideas?

robgordijn's avatar

I had the same problem, it seems like you can't just swap out a binding, so this is my fix:

  1. Create your own PasswordResetServiceProvider in app/providers, let it extend on Illuminate\Auth\Passwords\PasswordResetServiceProvider

  2. In config/app.php replace the Illuminate service with your own service.

  3. In your service, overwrite the registerPasswordBroker() method, where you register your custom PasswordBroker in the closure.

  4. PasswordBroker should be something like:

namespace App\Services;

use Closure;
use Illuminate\Contracts\Auth\PasswordBroker as PasswordBrokerContract;
use Illuminate\Contracts\Auth\CanResetPassword as CanResetPasswordContract;

use Illuminate\Auth\Passwords\PasswordBroker as BasePasswordBroker;

class PasswordBroker extends BasePasswordBroker implements PasswordBrokerContract
{
    public function emailResetLink(CanResetPasswordContract $user, $token, Closure $callback = null)
    {
        $view = $this->emailView;

        // edit whatever here
        return $this->mailer->send($view, compact('token', 'user'), function($m) use ($user, $token, $callback)
        {
            $m->to($user->getEmailForPasswordReset());

            if ( ! is_null($callback)) call_user_func($callback, $m, $user, $token);
        });
    }
}
opheliadesign's avatar

I really hope future releases of Laravel 5 make it easier to change some of this stuff. I did not have the time to figure out how to hack and override the auth system and just reverted back to how I would have handled it with Laravel 4. After about 30 minutes of trying to figure out how to redirect to /members instead of /home after signing in I tapped out.

2 likes
willvincent's avatar

@opheliadesign For future reference, that redirection happens in the RedirectIfAuthenticated.php middleware.

So it's not a matter of it being difficult to change, just unfamiliar where it's occurring.

opheliadesign's avatar

@willvincent Right you are! Actually, I just remembered that was not the problem (well, it was very briefly, but I figured it out), it was actually using username instead of email for logging in - how would I go about changing that?

willvincent's avatar

Ahh.. that's fairly trivial too, @opheliadesign (once you wrap your head around it, anyway)...

Ok, so if you look at the AuthController that comes out of the box, you'll see it uses the AuthenticatesAndRegistersUsers trait.

So, all you need to do to take a username instead of an email is copy the postLogin method from that trait into the AuthController and change it like so:

  /**
   * Handle a login request to the application.
   *
   * @param  \Illuminate\Http\Request  $request
   * @return \Illuminate\Http\Response
   */
  public function postLogin(\Illuminate\Http\Request $request)
  {
    $this->validate($request, [
      'username' => 'required', 'password' => 'required',
    ]);

    $credentials = $request->only('username', 'password');

    if ($this->auth->attempt($credentials, $request->has('remember')))
    {
      return redirect()->intended($this->redirectPath());
    }

    return redirect($this->loginPath())
      ->withInput($request->only('username', 'remember'))
      ->withErrors([
        'username' => 'These credentials do not match our records.',
      ]);
  }

Then, update the login view to collect the username instead of email

<div class="form-group">
  <label class="col-md-4 control-label">Username</label>
  <div class="col-md-6">
    <input type="text" class="form-control" name="username" value="{{ old('username') }}">
  </div>
</div>

That's it. You're done.

opheliadesign's avatar

@willvincent Fair enough, and that's basically what I did, but you're still totally changing how this class is configured out of the box to simply use username. Here is the initial state of the class:

class AuthController extends Controller {

    /*
    |--------------------------------------------------------------------------
    | Registration & Login Controller
    |--------------------------------------------------------------------------
    |
    | This controller handles the registration of new users, as well as the
    | authentication of existing users. By default, this controller uses
    | a simple trait to add these behaviors. Why don't you explore it?
    |
    */

    use AuthenticatesAndRegistersUsers;

    /**
     * Create a new authentication controller instance.
     *
     * @param  \Illuminate\Contracts\Auth\Guard  $auth
     * @param  \Illuminate\Contracts\Auth\Registrar  $registrar
     * @return void
     */
    public function __construct(Guard $auth, Registrar $registrar)
    {
        $this->auth = $auth;
        $this->registrar = $registrar;

        $this->middleware('guest', ['except' => 'getLogout']);
    }

}

Lots of voodoo and no clear cut way to make a simple change like email -> username.

willvincent's avatar

@opheliadesign Well, the out of the box is just a starting point, it's not necessarily gospel. ;) And it's not really voodoo either. By default it uses the trait, which as provided by default uses email for authentication. The ability to override that behavior is what makes traits kind of awesome.

It's right there in the comment...

By default, this controller uses a simple trait to add these behaviors. Why don't you explore it?

That's basically saying, "Hey, we gave you some default behaviors to potentially save you time, but, please, change it to suit your needs."

And if you're using an ide like phpstorm, you can easily click through to see what the trait is providing to the controller by command-clicking on a mac, or control-clicking on windows.

1 like
opheliadesign's avatar

@willvincent Looking at it again it is a bit more clear. Overall I'm loving Laravel 5 but it is certainly a leap from 4.x. Thanks for the replies. :)

1 like
willvincent's avatar

@opheliadesign I totally understand your frustration. I resisted OO for a long time because it often obfuscates what's going on.. at least to me. Laravel 5 definitely is more challenging to wrap your head around. But it's still so new, that there's not a lot of really in depth information available aside from the docs, the handful of videos here, and looking at the code itself.

Waiting for the 'a ha' moment to hit can be frustrating. :)

1 like
opheliadesign's avatar

@willvincent I was actually getting really into OO before I discovered Laravel. Prior to L5, Laravel was kind of like "Diet OO" - it was "sort of" OO as you coded for it but not 100%. I think I have heard Taylor talk about this a bit. L5 is more what an OO framework should be, I just got lazy lol

1 like
willvincent's avatar

@opheliadesign I've been working primarily with Drupal (very much not OO) for the past 8yrs or so, Drupal 8 is a complete 180 from what it has been though. Still a funky hybrid of procedural and OO, but there are a lot of symfony components in it now, so the time has come to beef up my OO chops. So I decided to dive into Laravel.. Plus, laravel is pretty nice, and I figured why not dig into the latest and greatest, thus Laravel 5. ;)

1 like
daleramirez's avatar

My solution to extend or override the PasswordBroker Class:

  • Comment out 'Illuminate\Auth\Passwords\PasswordResetServiceProvider' in config/app.php
  • Creating your own PasswordBroker Class that extends illuminate PasswordBroker

namespace App\Model;
use Illuminate\Auth\Passwords\PasswordBroker as BasePasswordBroker;
class PasswordBroker extends BasePasswordBroker {
    // override illuminate methods here
}

- Then bind your new PasswordBroker class to AppServiceProvider at app/Providers/AppServiceProvider.php in the register method.
    $this->app->bind('App\Model\PasswordBroker', function($app) {
        $key = $app['config']['app.key'];
        $userToken = new \App\Model\NewUserToken; 
        $tokens = new \App\Repository\NewTokenRepository($key,$userToken);
        $user = new \App\Model\NewUser;
        $view = $app['config']['auth.password.email'];
        return new \App\Model\PasswordBroker($tokens, $users, $app['mailer'], $view);
    });

On the code above you can see i instantiate new user token and the user model.

sjmolinski's avatar

I have read through all of the examples in this thread and something still seems to be missing to solve my own dilemma. My company is currently in the process of transitioning our system over to a complete Laravel solution. We have an old database with that can't be modified much to convert to the Laravel, and hence has some old naming conventions that we must suffer with for the moment.

Our current database uses "Login" and "Password" as columns for a user credentials. While looking at how the PasswordBroker works I realized I would need to implement changes for the trait CanResetPassword, class DatabaseTokenRepository, the trait ResetPasswords and obviously the class PasswordBroker. So I copied the files into a "Auth" folder inside my app so I could go about extending them as needed. Made the changes and finally modified the PasswordResetServiceProvider.

<?php namespace App\Auth\Passwords;

use Closure;
use App\User;
use Illuminate\Contracts\Auth\UserProvider;
use Illuminate\Contracts\Mail\Mailer as MailerContract;
use Illuminate\Contracts\Auth\PasswordBroker as PasswordBrokerContract;
use Illuminate\Contracts\Auth\CanResetPassword as CanResetPasswordContract;

use Illuminate\Auth\Passwords\PasswordBroker as BasePasswordBroker;
use Illuminate\Auth\Passwords\TokenRepositoryInterface as TokenRepositoryInterface;

class PasswordBroker extends BasePasswordBroker implements PasswordBrokerContract
{
    /**
     * Create a new password broker instance.
     *
     * @param  \Illuminate\Auth\Passwords\TokenRepositoryInterface  $tokens
     * @param  \Illuminate\Contracts\Auth\UserProvider  $users
     * @param  \Illuminate\Contracts\Mail\Mailer  $mailer
     * @param  string  $emailView
     * @return void
     */
    public function __construct(TokenRepositoryInterface $tokens,
                                UserProvider $users,
                                MailerContract $mailer,
                                $emailView)
    {
        $this->users = $users;
        $this->mailer = $mailer;
        $this->tokens = $tokens;
        $this->emailView = $emailView;
    }

    public function emailResetLink(CanResetPasswordContract $user, $token, Closure $callback = null)
    {

        $view = $this->emailView;
        // edit whatever here
        return $this->mailer->send($view, compact('token', 'user'), function($m) use ($user, $token, $callback)
        {
            $m->to($user->getEmailForPasswordReset());

            if ( ! is_null($callback)) call_user_func($callback, $m, $user, $token);
        });
    }
    /**
     * Get the user for the given credentials.
     *
     * @param  array  $credentials
     * @return \Illuminate\Contracts\Auth\CanResetPassword
     *
     * @throws \UnexpectedValueException
     */
    public function getUser(array $credentials)
    {
        $credentials = array_except($credentials, ['token']);
        //['Login'=>'eddie@apex']
        $user = User::where('Login', '=' , $credentials['Login'])->first();

        if ($user && ! $user instanceof CanResetPasswordContract)
        {
            throw new UnexpectedValueException("User must implement CanResetPassword interface.");
        }

        return $user;
    }

}

In the end I always seem to get a very similar error.
"BindingResolutionException in Container.php line 800: Target [Illuminate\Contracts\Auth\UserProvider] is not instantiable."

Can someone help point me in the right direction?

1 like

Please or to participate in this conversation.