W3update

Forgotten Your Laravel Password? The Complete 2025 Guide to Resetting Passwords (Breeze, Fortify, Jetstream & CLI) A step‑by‑step, SEO‑friendly guide covering every way to reset a password in Laravel—UI, API, CLI, multi‑auth, and troubleshooting. Table of Contents Quick Fixes How Laravel Password Reset Works Method 1: UI Flow with Breeze/Jetstream (Blade/Livewire/Inertia) If your app already […]

0
4

Forgotten Your Laravel Password? The Complete 2025 Guide to Resetting Passwords (Breeze, Fortify, Jetstream & CLI)

A step‑by‑step, SEO‑friendly guide covering every way to reset a password in Laravel—UI, API, CLI, multi‑auth, and troubleshooting.


Table of Contents

  1. Quick Fixes
  2. How Laravel Password Reset Works
  3. Method 1: UI Flow with Breeze/Jetstream (Blade/Livewire/Inertia)
  4. Method 2: API Flow with Laravel Fortify (for SPA/Mobile)
  5. Method 3: Admin/CLI Reset Without Email (Tinker)
  6. Method 4: Programmatically Send a Reset Link (Controllers/Jobs)
  7. Method 5: Multi‑Auth Setups (Users/Admins) & Custom Brokers
  8. Troubleshooting & Common Errors
  9. Security Best Practices
  10. FAQs
  11. Copy‑Paste Snippets

Quick Fixes

  • Can’t receive the reset email? Verify MAIL_MAILER, MAIL_HOST, MAIL_PORT, MAIL_FROM_ADDRESS, and APP_URL in .env. If you’re using queues, run your worker: php artisan queue:work.
  • Need a fast admin override? Use Tinker to set a new password:
php artisan tinker
>>> $u = App\Models\User::where('email','user@example.com')->first();
>>> $u->forceFill(['password' => Hash::make('NewStrong!Pass1')])->save();
  • SPA/mobile app? Hit Fortify endpoints: POST /forgot-password then POST /reset-password with token, email, password, password_confirmation.

How Laravel Password Reset Works

  • Password Broker: The broker manages tokens and emails. Use Password::broker(); configure brokers in config/auth.php under passwords.
  • Tokens Table: Modern Laravel stores hashed tokens in the password_reset_tokens table with email, token, and created_at columns.
  • Expiry & Throttle: The token lifetime and request throttling are broker-specific in config/auth.php (e.g., expire in minutes, throttle between requests). Defaults are commonly 60 minutes for expiry.
  • Notifications: Reset emails are sent via Illuminate\Auth\Notifications\ResetPassword. You can customize the email content, URL, and mailer.

Method 1: UI Flow with Breeze/Jetstream (Blade/Livewire/Inertia)

If your app already has authentication scaffolding, you often get a ready‑made Forgot Password page and a Reset Password form.

A) Install Breeze (Blade example)

composer require laravel/breeze --dev
php artisan breeze:install blade
php artisan migrate
npm install && npm run build

This scaffolds routes like /forgot-password and /reset-password with controllers and views.

B) Configure Mail

APP_URL=https://your-app.test
MAIL_MAILER=smtp
MAIL_HOST=smtp.mailtrap.io
MAIL_PORT=2525
MAIL_USERNAME=xxxx
MAIL_PASSWORD=xxxx
MAIL_ENCRYPTION=tls
MAIL_FROM_ADDRESS=no-reply@your-app.test
MAIL_FROM_NAME="Your App"

Tip: In local development, use log or mailtrap; in production, use a real SMTP (or Postmark, SES, Mailgun).

C) Use the Flow

  1. Visit /forgot-password, enter your email.
  2. Click the link from your inbox.
  3. Set a new password on /reset-password.

D) Customize the Reset Email

In App\Providers\AuthServiceProvider:

use Illuminate\Auth\Notifications\ResetPassword;

public function boot(): void
{
    ResetPassword::toMailUsing(function ($notifiable, $token) {
        $url = url(route('password.reset', [
            'token' => $token,
            'email' => $notifiable->getEmailForPasswordReset(),
        ], false));

        return (new \Illuminate\Notifications\Messages\MailMessage)
            ->subject('Reset your password')
            ->line('You are receiving this email because we received a password reset request for your account.')
            ->action('Reset Password', $url)
            ->line('This link will expire in 60 minutes.')
            ->line('If you did not request a password reset, no further action is required.');
    });
}

Jetstream users (Livewire or Inertia) get the same functionality, powered by Fortify under the hood.


Method 2: API Flow with Laravel Fortify (for SPA/Mobile)

Fortify exposes endpoints you can call from your SPA or mobile client.

Endpoints (default)

  • POST /forgot-password – Body: { email }. Sends email with reset link.
  • POST /reset-password – Body: { token, email, password, password_confirmation }.

Example cURL

# Request a reset link
curl -X POST https://your-app.test/forgot-password \
  -H "Accept: application/json" \
  -d "email=user@example.com"

# Reset the password
curl -X POST https://your-app.test/reset-password \
  -H "Accept: application/json" \
  -d "email=user@example.com" \
  -d "token=PASTE_TOKEN_FROM_EMAIL" \
  -d "password=NewStrong!Pass1" \
  -d "password_confirmation=NewStrong!Pass1"

Using Sanctum for SPA? Ensure you first hit /sanctum/csrf-cookie to prime CSRF cookies for stateful requests in browsers.


Method 3: Admin/CLI Reset Without Email (Tinker)

Perfect when the user has no mailbox access or email is misconfigured.

php artisan tinker
>>> use App\Models\User; use Illuminate\Support\Facades\Hash;
>>> $u = User::where('email','user@example.com')->first();
>>> $u->forceFill(['password' => Hash::make('NewStrong!Pass1')])->save();

Force password change on next login (optional design): add a boolean column must_change_password and redirect those users to a change‑password screen after login.

Avoid updating the password column with plain text. Always hash with Hash::make().


Method 4: Programmatically Send a Reset Link (Controllers/Jobs)

Trigger reset links from an admin panel or background job.

use Illuminate\Support\Facades\Password;

public function sendLink(string $email)
{
    $status = Password::sendResetLink(['email' => $email]);

    return $status === Password::RESET_LINK_SENT
        ? back()->with(['status' => __($status)])
        : back()->withErrors(['email' => __($status)]);
}

Generate the token yourself and craft a custom URL/email:

use Illuminate\Support\Facades\Password;

$token = Password::createToken($user);
$url = url(route('password.reset', ['token' => $token, 'email' => $user->email], false));
// Send $url via your own Mailable/Notification

Method 5: Multi‑Auth Setups (Users/Admins) & Custom Brokers

Define separate brokers for different guards (e.g., users, admins). In config/auth.php:

'passwords' => [
    'users' => [
        'provider' => 'users',
        'table' => 'password_reset_tokens',
        'expire' => 60,
        'throttle' => 60,
    ],
    'admins' => [
        'provider' => 'admins',
        'table' => 'password_reset_tokens',
        'expire' => 30,
        'throttle' => 60,
    ],
],

Use a specific broker:

Password::broker('admins')->sendResetLink(['email' => $adminEmail]);

If admins live in a different table/model/provider, ensure the providers section is configured and your admin model implements CanResetPassword (via Illuminate\Auth\Passwords\CanResetPassword trait or Jetstream/Fortify conventions).


Troubleshooting & Common Errors

  • No email received
    • Check .env mail settings and APP_URL.
    • In local, set MAIL_MAILER=log and inspect storage/logs/laravel.log.
    • If notifications are queued, make sure a worker is running: php artisan queue:work.
    • Verify MAIL_FROM_ADDRESS is allowed by your SMTP provider (SPF/DKIM configured in DNS).
  • Token expired / invalid
    • Tokens expire based on broker expire value (e.g., 60 minutes). Generate a new link.
    • Deleting old rows from password_reset_tokens can help clean up; Laravel manages this when generating new tokens.
  • 419 Page Expired (CSRF)
    • Happens in browser forms if CSRF token or session is stale.
    • Ensure your frontend uses @csrf in forms or hits /sanctum/csrf-cookie for SPA.
  • Wrong link domain or HTTP/HTTPS mismatch
    • Fix APP_URL and any URL generation logic. Use HTTPS in production.
  • Multi‑auth using wrong broker
    • Calls to Password::broker() must match the provider/guard for that user type.
  • Horizon/queues not processing
    • Check queue name. By default, notifications go to the default queue unless overridden in Notification::viaQueues().
  • Missing tokens table
    • Ensure you have a migration for password_reset_tokens. If using starter kits, run php artisan migrate after installation. Otherwise, create a migration with email, token, created_at.

Security Best Practices

  • Use strong passwords and enforce validation rules (length, mixed case, numbers, symbols).
  • Keep token expiry short (e.g., 60 minutes) and throttle reset requests.
  • Do not disclose whether an email exists—return a generic success message.
  • Force re‑authentication for sensitive actions after password changes.
  • Invalidate other sessions/devices after a password reset (Auth::logoutOtherDevices() if desired).
  • Log password reset events for auditing, but never log tokens or passwords.

FAQs

How long is a Laravel password reset link valid?
Whatever you set in config/auth.php under the broker’s expire key (commonly 60 minutes).

Where are reset tokens stored?
In the password_reset_tokens table; Laravel stores a hashed token, not the raw token.

Can I reset a password without email access?
Yes—use Tinker (CLI) to set a new hashed password or send a reset link programmatically as an admin.

How do I support separate user types (admins, customers)?
Create multiple brokers in config/auth.php and call Password::broker('your-broker').

What about SPA/mobile apps?
Use Fortify’s /forgot-password and /reset-password endpoints; handle CSRF for browser-based SPAs.


Copy‑Paste Snippets

Tinker one‑liner:

php artisan tinker --execute="App\\Models\\User::where('email','user@example.com')->first()->forceFill(['password'=>Illuminate\\Support\\Facades\\Hash::make('NewStrong!Pass1')])->save();"

Send reset link in a controller:

$status = Password::sendResetLink(['email' => request('email')]);
return $status === Password::RESET_LINK_SENT
    ? back()->with('status', __($status))
    : back()->withErrors(['email' => __($status)]);

Generate token & build URL:

$token = Password::createToken($user);
$url = url(route('password.reset', ['token' => $token, 'email' => $user->email], false));

Fortify reset (controller example):

public function reset(Request $request)
{
    $request->validate([
        'token' => 'required',
        'email' => 'required|email',
        'password' => 'required|confirmed|min:8',
    ]);

    $status = Password::reset(
        $request->only('email','password','password_confirmation','token'),
        function ($user, $password) {
            $user->forceFill([
                'password' => Hash::make($password),
            ])->save();

            event(new \Illuminate\Auth\Events\PasswordReset($user));
        }
    );

    return $status === Password::PASSWORD_RESET
        ? response()->json(['message' => __($status)])
        : response()->json(['message' => __($status)], 422);
}

Bonus: Add an Admin “Send Reset Link” Button

  • Create an admin action that takes a user’s email and calls Password::broker('users')->sendResetLink().
  • Show a neutral success message (don’t reveal whether the address exists).
  • Log the action (user id, admin id, timestamp), but never the token.

Conclusion

With Laravel’s brokers, starter kits (Breeze/Jetstream), and Fortify endpoints, you have multiple reliable paths to recover from a forgotten password—whether you’re an end user, a support admin, or a developer maintaining a multi‑auth system. Start with the UI flow, fall back to API or CLI when needed, and keep things secure with short expiries, throttling, and solid SMTP/queue setups.


W
WRITTEN BY

W3update Team

Responses (0 )