DEV Community

Rabeea Ali
Rabeea Ali

Posted on • Updated on

How to reset password via code using Laravel API

Every Laravel project has build in the password reset process, it is working perfectly with web, but once when you come to mobile apps you going to face a problem with this way, because you make users go to web page to reset their password! & I think this not the best way to do such a thing, so the code password reset is pretty useful when you have mobile apps.

You will find the full code with better code written at the end of post.

Prerequisites:

• A Laravel project
• An Account in Mailtrap & get credentials and put it in .env file.

Step 1: Create a new reset code table

Each Laravel project comes with password_resets table, I will leave this table(If your Laravel app just API for mobile apps then use password_resets) for web users and create another table for mobile apps users as follow:

1- run this command in terminal

 php artisan make:model ResetCodePassword -m
Enter fullscreen mode Exit fullscreen mode

2- open reset_code_passwords_table migration and will be something like this:

Schema::create('reset_code_passwords', function (Blueprint $table) {
    $table->string('email')->index();
    $table->string('code');
    $table->timestamp('created_at')->nullable();
});
Enter fullscreen mode Exit fullscreen mode

Now migrate the table with php artisan migrate.

3- open ResetCodePassword then fill the $fillable with columns names:

protected $fillable = [
    'email',
    'code',
    'created_at',
];
Enter fullscreen mode Exit fullscreen mode

Note: feel free to change anything as you need

Step 2: Create files

  • Create a new directory in controllers called Api then create these three controllers:
ForgotPasswordController.php   // step 1
CodeCheckController.php        // step 2
ResetPasswordController.php    // step 3
Enter fullscreen mode Exit fullscreen mode
  • Create a mail class for email:
php artisan make:mail SendCodeResetPassword
Enter fullscreen mode Exit fullscreen mode

Now let's fill these files, first one ForgotPasswordController.php will be something like this:

class ForgotPasswordController extends Controller
{
    public function __invoke(Request $request)
    {
        $data = $request->validate([
            'email' => 'required|email|exists:users',
        ]);

        // Delete all old code that user send before.
        ResetCodePassword::where('email', $request->email)->delete();

        // Generate random code
        $data['code'] = mt_rand(100000, 999999);

        // Create a new code
        $codeData = ResetCodePassword::create($data);

        // Send email to user
        Mail::to($request->email)->send(new SendCodeResetPassword($codeData->code));

        return response(['message' => trans('passwords.sent')], 200);
    }
}
Enter fullscreen mode Exit fullscreen mode

in CodeCheckController.php:

class CodeCheckController extends Controller
{
    public function __invoke(Request $request)
    {
        $request->validate([
            'code' => 'required|string|exists:reset_code_passwords',
        ]);

        // find the code
        $passwordReset = ResetCodePassword::firstWhere('code', $request->code);

        // check if it does not expired: the time is one hour
        if ($passwordReset->created_at > now()->addHour()) {
            $passwordReset->delete();
            return response(['message' => trans('passwords.code_is_expire')], 422);
        }

        return response([
            'code' => $passwordReset->code,
            'message' => trans('passwords.code_is_valid')
        ], 200);
    }
}
Enter fullscreen mode Exit fullscreen mode

in CodeCheckController.php:

class CodeCheckController extends Controller
{
    public function __invoke(Request $request)
    {
        $request->validate([
            'code' => 'required|string|exists:reset_code_passwords',
            'password' => 'required|string|min:6|confirmed',
        ]);

        // find the code
        $passwordReset = ResetCodePassword::firstWhere('code', $request->code);

        // check if it does not expired: the time is one hour
        if ($passwordReset->created_at > now()->addHour()) {
            $passwordReset->delete();
            return response(['message' => trans('passwords.code_is_expire')], 422);
        }

        // find user's email 
        $user = User::firstWhere('email', $passwordReset->email);

        // update user password
        $user->update($request->only('password'));

        // delete current code 
        $passwordReset->delete();

        return response(['message' =>'password has been successfully reset'], 200);
    }
}
Enter fullscreen mode Exit fullscreen mode

Step 3: Handel Mail and blade file

In SendCodeResetPassword.php mail:

class SendCodeResetPassword extends Mailable implements ShouldQueue
{
    use Queueable, SerializesModels;

    public $code;

    public function __construct($code)
    {
        $this->code = $code;
    }

    public function build()
    {
        return $this->markdown('emails.send-code-reset-password');
    }
}
Enter fullscreen mode Exit fullscreen mode

In send-code-reset-password.blade.php

@component('mail::message')
<h1>We have received your request to reset your account password</h1>
<p>You can use the following code to recover your account:</p>

@component('mail::panel')
{{ $code }}
@endcomponent

<p>The allowed duration of the code is one hour from the time the message was sent</p>
@endcomponent
Enter fullscreen mode Exit fullscreen mode

Step 4: Create routes

In routes/api.php create links for your controllers:

Route::post('password/email',  ForgotPasswordController::class);
Route::post('password/code/check', CodeCheckController::class);
Route::post('password/reset', ResetPasswordController::class);
Enter fullscreen mode Exit fullscreen mode

Now you can open postman and try these links:

http://your-domain.com/api/password/email       => step 1
http://your-domain.com/api/password/code/check  => step 2
http://your-domain.com/api/password/reset       => step 3
Enter fullscreen mode Exit fullscreen mode

Source code on Github

Discussion (0)