In Laravel, how does the Password broker carry a reset from Password::sendResetLink to Password::reset, and what does your own code still have to do?
answer
- a status string, never a boolean
- user provider looks the email up
- hashed token row keyed by email
- your closure saves the password
- PasswordReset event is yours to fire
basics
~20 sPassword::sendResetLink finds the user by email, stores a hashed token and mails a link. Password::reset re-checks email and token, runs your closure to save the new password, deletes the token, and both return a status string.
solid answer
~40 sThe `Password` facade proxies the broker named in `config/auth.php` (`users` by default), which finds accounts through that broker's user provider. `Password::sendResetLink($request->only('email'))` returns `passwords.user` when nobody matches and `passwords.throttled` when a token was issued inside the throttle window; otherwise it stores a hashed token in `password_reset_tokens`, calls `sendPasswordResetNotification($token)` on the user and returns `passwords.sent`. `Password::reset()` takes email, password and token, re-finds the user, checks the token is present, unexpired and matches the hash, then calls your closure with the user and the plain password. Your closure must hash and save the password, and the documented one also rotates `remember_token` and dispatches `PasswordReset`; the broker only deletes the token and returns `passwords.reset`. Validating the new password is also yours, before the call. Compare statuses with `Password::ResetLinkSent` or `Password::PasswordReset` and show them with `__($status)`.
code
php · 15 lines<?php
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Password;
use Illuminate\Support\Facades\Route;
Route::post('/forgot-password', function (Request $request) {
$request->validate(['email' => 'required|email']);
$status = Password::sendResetLink($request->only('email'));
return $status === Password::ResetLinkSent
? back()->with('status', __($status))
: back()->withErrors(['email' => __($status)]);
})->middleware('guest')->name('password.email');go deeper
Recall the two calls, Password::sendResetLink and Password::reset, the four fields the reset form posts, and that both calls return a status string you translate with __().
Explain what the broker does at each step: provider lookup, throttle check, hashed token storage, expiry and hash check, token deletion, and which work the closure owns.
Show you know the closure is the security boundary for side effects: rotating remember_token, dispatching PasswordReset, and deciding whether to end other sessions or sign the user in.
Weigh building on the broker directly versus letting Fortify drive it, and how many brokers a product with several account types really needs.
## The pieces behind the facade Laravel's password reset is built from a few small parts, and knowing which part does what is most of the interview answer: - **`Illuminate\Support\Facades\Password`** resolves the `PasswordBrokerManager`, which hands out one **password broker** per entry under `passwords` in `config/auth.php`. The default broker is `auth.defaults.passwords`, which the skeleton reads from `AUTH_PASSWORD_BROKER` and falls back to `users`. - A **broker** (`Illuminate\Auth\Passwords\PasswordBroker`) combines a **user provider** (the same Eloquent or database provider your guards use) with a **token repository** (database table or cache store). - The user model must implement the `CanResetPassword` contract. The framework's `Illuminate\Foundation\Auth\User` base class already does, and its `CanResetPassword` trait supplies `getEmailForPasswordReset()` and `sendPasswordResetNotification($token)`. ## Step 1: sending the link On an e-learning site, a student who forgot their password posts their email to a `password.email` route. The handler validates the input and calls `Password::sendResetLink($request->only('email'))`. Inside, the broker: 1. Asks the user provider for a user matching the credentials (the stock providers ignore any key containing `password`). No match returns `passwords.user`. 2. Asks the token repository whether this user received a token inside the `throttle` window. If so it returns `passwords.throttled` and sends nothing. 3. Creates a token: a random string run through HMAC-SHA256 with the app key. The repository deletes the user's previous row first, then stores the token **hashed with the app's Hasher** together with `created_at`. 4. Calls `$user->sendPasswordResetNotification($token)` with the plain token, dispatches `PasswordResetLinkSent` and returns `passwords.sent`. If you pass a closure as the second argument, step 4 is replaced: the closure receives the user and the plain token, and its return value (or `passwords.sent` when it returns null) becomes the status. ## Step 2: resetting the password The link lands on a `password.reset` route carrying the token and email. The form posts `token`, `email`, `password` and `password_confirmation` to a handler that validates them and calls `Password::reset($credentials, $closure)`. The broker: 1. Finds the user again from the credentials minus the token; no match returns `passwords.user`. 2. Asks the repository whether a token exists for that email, is younger than `expire` minutes, and matches the submitted value under `Hash::check`. Any failure returns `passwords.token`. 3. Calls **your closure** with the user and the plain new password. 4. Deletes the token, so the link cannot be replayed, and returns `passwords.reset`. ## The status strings Both methods return a translation key, not a boolean. The facade exposes constants in two spellings (`Password::ResetLinkSent` and the older `Password::RESET_LINK_SENT`): | Facade constant | Value | Default English line | |---|---|---| | `Password::ResetLinkSent` | `passwords.sent` | We have emailed your password reset link. | | `Password::PasswordReset` | `passwords.reset` | Your password has been reset. | | `Password::InvalidUser` | `passwords.user` | We can't find a user with that email address. | | `Password::InvalidToken` | `passwords.token` | This password reset token is invalid. | | `Password::ResetThrottled` | `passwords.throttled` | Please wait before retrying. | The lines live in the framework's `passwords` language file; run `lang:publish` to get a `lang/` directory you can edit. ## What the broker leaves to you - **Validation.** `reset()` never looks at `password_confirmation` or password strength; run `$request->validate()` first. - **Saving the password.** The closure must set a hashed password and call `save()`. - **Side effects.** The documented closure also rotates `remember_token` with `setRememberToken(Str::random(60))` and dispatches `Illuminate\Auth\Events\PasswordReset`; the broker does neither. - **Signing in.** The broker never logs the user in; redirect to the login page or sign them in yourself. ```php $status = Password::reset( $request->only('email', 'password', 'password_confirmation', 'token'), function (User $user, string $password) { $user->forceFill(['password' => Hash::make($password)]) ->setRememberToken(Str::random(60)); $user->save(); event(new PasswordReset($user)); } ); ``` ## More than one broker The e-learning site may keep students and instructors in different tables with different providers. Each account type gets its own entry under `passwords`, and code picks it explicitly: - `Password::broker('instructors')->sendResetLink($request->only('email'))` uses the `instructors` entry, its provider and its token table or store. - `Password::sendResetLink(...)` with no broker uses `auth.defaults.passwords`. - The manager caches each broker it builds, so repeated calls in one request reuse the same instance. Asking for a broker name that has no config entry throws `InvalidArgumentException` ("Password resetter [name] is not defined."), which usually surfaces as a typo in a new admin reset screen. ## Common mistakes - Treating the return value as truthy: every status is a non-empty string, so `if ($status)` always passes. - Assuming the broker hashes the password or fires `PasswordReset`. - Forgetting that a second request for a link deletes the first token, so only the newest email works.
- What changes when you pass a closure as the second argument to Password::sendResetLink?The broker still finds the user, applies the throttle and creates the token, but it no longer calls `sendPasswordResetNotification()`. Your closure receives the user and the plain token and decides how to deliver it; its return value becomes the status, or `passwords.sent` if it returns null. The `PasswordResetLinkSent` event is not dispatched on that path.
- How would the e-learning site give instructors a separate reset flow from students?Add a second entry under `passwords` in `config/auth.php`, for example `instructors`, pointing at the instructors' user provider and its own table or cache store. Then call `Password::broker('instructors')->sendResetLink(...)` and `->reset(...)`. Without an argument the facade uses the default broker from `auth.defaults.passwords`.
- Does Password::reset check that password and password_confirmation match?No. The broker only uses the email to find the user, and the token and expiry to accept the request; it passes the new password to your closure unchecked. The `confirmed` rule and any strength rules belong in `$request->validate()` before `reset()` is called.
saying these in an interview costs you the question
- Password::sendResetLink returns true or false depending on whether mail went out
- The broker hashes the new password and saves the user model itself
- Password::reset enforces password_confirmation and the password rules
- The reset token is stored in plain text so it can be compared directly
- The broker dispatches the PasswordReset event automatically after a reset
- A used reset token stays valid until it expires