In Laravel's config/auth.php, what do a password broker's expire and throttle settings do, and how do the database and cache token drivers differ?
answer
- one is minutes, the other seconds
- passwords.token versus passwords.throttled
- throttle is per account, not per IP
- auth:clear-resets for leftover rows
- cache:clear can wipe pending links
basics
~20 sexpire is how many minutes a reset token stays valid; throttle is how many seconds before the same account gets another link (both 60 in the skeleton). Database-driver rows need auth:clear-resets; cache-driver entries expire by TTL.
solid answer
~40 sEach entry under `passwords` in `config/auth.php` has `expire`, in **minutes**, and `throttle`, in **seconds**; the skeleton sets both to 60. A token older than `expire` fails the check and `Password::reset()` returns `passwords.token`. If the same account asks again inside `throttle`, `Password::sendResetLink()` returns `passwords.throttled` and sends nothing; the check is keyed by the account, so it does not limit one client trying many addresses. The default `database` driver stores one row per email in `password_reset_tokens`; expired rows stay until `php artisan auth:clear-resets` removes them, usually on a schedule. Setting `'driver' => 'cache'` (optionally with `'store'`) keeps the hashed token in a cache store with a TTL equal to `expire`, needs no table and cleans itself up, but `php artisan cache:clear` on that store drops every pending link.
go deeper
Remember the units and defaults: expire is 60 minutes and throttle is 60 seconds in the skeleton, and each produces its own status string.
Explain where each check runs, why a missing throttle key disables throttling, and what auth:clear-resets does for each driver.
Show operational judgment: schedule pruning, isolate the cache store from cache:clear, share it across servers, and add route rate limits because the broker throttle is per account.
Frame the expiry and throttle values as a tradeoff between exposure window, inbox abuse and support load, and set them per account type if brokers differ.
## Where the settings live A **password broker** is configured under the `passwords` key of `config/auth.php`. The skeleton ships one broker named `users`: ```php 'passwords' => [ 'users' => [ 'provider' => 'users', 'table' => env('AUTH_PASSWORD_RESET_TOKEN_TABLE', 'password_reset_tokens'), 'expire' => 60, 'throttle' => 60, ], ], ``` There is no `driver` key in that file, so the **database** token repository is used. The `PasswordBrokerManager` reads each key when it builds the broker. ## `expire`: how long a link works - The value is in **minutes**; the manager multiplies it by 60 and falls back to 60 minutes when the key is missing. - The check happens when the token is used: `Password::reset()` loads the stored record, and if `created_at` plus `expire` is in the past, it returns `passwords.token` exactly as for a wrong token. - The default reset email reads the same value, so the "This password reset link will expire in :count minutes" line matches the default broker's setting. On an e-learning site, a student who requests a link at 09:00 and clicks it at 10:05 with the default settings gets the generic "invalid token" message and must ask again. ## `throttle`: how often one account can ask - The value is in **seconds**. Before creating a new token, the broker asks the repository whether this user's current token is younger than `throttle`; if so, `sendResetLink()` returns `passwords.throttled` and sends no mail. - If the key is **missing**, the manager passes 0 and throttling is **off**; a value of 0 or below also disables it. - It is keyed by the account's email, so it stops one mailbox from being flooded. It does **not** stop one client cycling through thousands of addresses; that needs rate limiting on the route itself. - Each successful request **replaces** the earlier token, so only the newest email's link works. ## The two token drivers compared | Aspect | `database` (default) | `cache` | |---|---|---| | Storage | a row in `password_reset_tokens`: `email` (primary key), hashed `token`, `created_at` | a cache entry holding the hashed token and its creation time | | Key | the email column | a SHA-256 hash of the email | | Extra config | `table`, optional `connection` | `'driver' => 'cache'`, optional `store` | | Expired entries | remain until pruned | vanish when the TTL (equal to `expire`) runs out | | `auth:clear-resets` | deletes rows older than `expire` | does nothing | | Main risk | table growth if never pruned | `cache:clear` wipes every pending link | ## Operating each driver For the **database** driver, schedule the pruning command: ```php use Illuminate\Support\Facades\Schedule; Schedule::command('auth:clear-resets')->everyFifteenMinutes(); ``` The command takes an optional broker name (`auth:clear-resets instructors`) and otherwise prunes the default broker. The table name can be changed with `AUTH_PASSWORD_RESET_TOKEN_TABLE`. For the **cache** driver: 1. Add `'driver' => 'cache'` to the broker entry. 2. Point `'store'` at a dedicated store defined in `config/cache.php`, so a deploy step that runs `php artisan cache:clear` on the default store does not invalidate links students are about to click. 3. Make sure that store is shared by every app server; a per-server store would accept the token only on the machine that issued it. ## A worked timeline With the skeleton's values (`expire` 60, `throttle` 60) and the database driver, one student's morning looks like this: 1. **09:00:00** `sendResetLink()` finds the student, sees no row, inserts a hashed token and returns `passwords.sent`. 2. **09:00:20** a second request finds a row only 20 seconds old and returns `passwords.throttled`; no email is sent and the first link still works. 3. **09:02:00** a third request is outside the throttle window, so the old row is deleted, a new token is stored and the first email's link is now dead. 4. **09:30:00** the student uses the newest link; `reset()` accepts it, runs the closure and deletes the row. 5. Had they waited until after **10:02:00**, `reset()` would have returned `passwords.token` and the stale row would sit in the table until the next `auth:clear-resets` run. ## Choosing values - Shorter `expire` shrinks the window in which a leaked email is useful, at the cost of more "link expired" support tickets. - A `throttle` of zero lets anyone trigger a stream of emails to a victim's inbox; keep it on. - Neither setting replaces a request rate limit on the forgot-password route.
- What happens if you delete the throttle key from a broker entry?Throttling switches off. The manager passes `$config['throttle'] ?? 0` to the repository, and a value of 0 or below makes the recently-created check return false, so every request creates a fresh token and sends another email. The repository's own constructor default of 60 never applies, because the manager always passes a value.
- Why does auth:clear-resets do nothing for a broker on the cache driver?The cache repository's `deleteExpired()` method is empty: each entry is written with a TTL equal to `expire`, so the store evicts it on its own. The command only matters for the database driver, whose rows would otherwise stay in `password_reset_tokens` forever.
saying these in an interview costs you the question
- expire is in seconds, so the skeleton's links last one minute
- The throttle setting rate-limits the forgot-password route per IP address
- Expired rows are deleted automatically by the database driver
- Requesting a second link leaves the first link valid as well
- The cache driver survives php artisan cache:clear on its store