Skip to content

How to Implement a Password Reset Feature in Laravel 11

In this article, I’ll walk you through implementing a password reset feature in Laravel 11. This guide builds on our previous discussions on user registration, login, and logout functionalities. By the end of this article, you’ll have a secure and user-friendly way for your users to reset their passwords if they ever forget them.

Why Password Reset is Essential

Password reset functionality is a crucial part of any application that requires user authentication. It allows users to regain access to their accounts securely, ensuring a smooth user experience and enhancing your app’s overall security.

Prerequisites

Before diving in, ensure that you have the following:

  • A working Laravel 11 application.
  • Basic understanding of Laravel’s authentication system, as covered in our previous articles on user registration, login, and logout.
  • Postman or any other API testing tool installed to test your routes.

Step 1: Setting Up the Password Reset Table

Laravel comes with a migration for the password_resets table by default. However, if you’ve customized your application, you might need to create or modify this table. In our case, we used password_reset_tokens as the table name.

Here’s a quick refresher on the migration file:

use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;

class CreatePasswordResetTokensTable extends Migration
{
    public function up()
    {
        Schema::create('password_reset_tokens', function (Blueprint $table) {
            $table->string('email')->index();
            $table->string('token');
            $table->timestamp('created_at')->nullable();
        });
    }

    public function down()
    {
        Schema::dropIfExists('password_reset_tokens');
    }
}

Run the migration to create the table: php artisan migrate

Step 2: Setting Up API Routes

We’ll create API routes for requesting a password reset and for actually resetting the password. These routes will be defined in the routes/api.php file.

use App\Http\Controllers\Api\Auth\PasswordResetController;

Route::post('password/request-reset', [PasswordResetController::class, 'requestPasswordReset']);
Route::post('password/reset', [PasswordResetController::class, 'resetPassword']);



Step 3: Creating the Password Reset Controller

Next, we’ll create a controller to handle the logic for password resets. You can generate the controller using Artisan:
php artisan make:controller Api/Auth/PasswordResetController

Inside PasswordResetController.php, you’ll define the methods for handling the password reset process.

namespace App\Http\Controllers\Api\Auth;

use App\Http\Controllers\Controller;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\DB;
use Illuminate\Support\Facades\Hash;
use Illuminate\Support\Str;
use Carbon\Carbon;

class PasswordResetController extends Controller
{
    public function requestPasswordReset(Request $request)
    {
        $request->validate(['email' => 'required|email']);

        $existingToken = DB::table('password_reset_tokens')->where('email', $request->email)->first();

        if ($existingToken) {
            DB::table('password_reset_tokens')->where('email', $request->email)->delete();
        }

        $token = Str::random(60);

        DB::table('password_reset_tokens')->insert([
            'email' => $request->email,
            'token' => Hash::make($token),
            'created_at' => Carbon::now()
        ]);

        return response()->json([
            'message' => 'Token generated successfully',
            'token' => $token
        ], 200);
    }

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

        $passwordReset = DB::table('password_reset_tokens')->where('email', $request->email)->first();

        if (!$passwordReset || !Hash::check($request->token, $passwordReset->token)) {
            return response()->json(['message' => 'Invalid token'], 400);
        }

        $user = \App\Models\User::where('email', $request->email)->first();

        if (!$user) {
            return response()->json(['message' => 'User not found'], 404);
        }

        $user->password = Hash::make($request->password);
        $user->setRememberToken(Str::random(60));
        $user->save();

        DB::table('password_reset_tokens')->where('email', $request->email)->delete();

        return response()->json(['message' => 'Password reset successfully'], 200);
    }
}



Step 4: Testing the Password Reset API

Use Postman to test your new API endpoints.

Requesting a Password Reset:

  • Method: POST
  • Endpoint: /api/password/request-reset
  • Body: JSON
{
    "message": "Token generated successfully",
    "token": "your_generated_token_here"
}

Resetting the Password:

  • Method: POST
  • Endpoint: /api/password/reset
  • Body: JSON
{
    "email": "user@example.com",
    "token": "your_generated_token_here",
    "password": "newpassword",
    "password_confirmation": "newpassword"
}

Expected Response:

  • Status Code: 200
  • Body:
{
    "message": "Password reset successfully"
}



Step 5: Handling Common Issues

  • Token Not Found or Invalid: Ensure you’re storing the token securely and verifying it correctly.
  • Expired Tokens: Implement logic to invalidate tokens after a certain period, usually by checking the created_at timestamp.



Conclusion

Implementing a password reset feature is a critical part of any web application’s authentication system. By following this guide, you’ve added another layer of security and convenience for your users.

Remember to test thoroughly and handle edge cases, such as expired tokens or non-existent emails, gracefully. In future articles, we’ll explore how to integrate this functionality with email notifications, enhancing the user experience further.

Make sure to revisit our previous articles on registration, login, and logout if you need to refresh your understanding of the foundational aspects of user authentication in Laravel.

Happy coding!