By default Laravel 5.5's password reset system works on email, but I need to add support for a mobile number (verify by OTP and generate a token and redirect to password reset page). I am doing all this part and I have created a mobile column on password_resets table.
But the problem is \Illuminate\Auth\Passwords\DatabaseTokenRepository
&& \Illuminate\Auth\Passwords\TokenRepositoryInterface
ON exist
method and it doesn't seem configurable.
public function exists(CanResetPasswordContract $user, $token)
{
$record = (array) $this->getTable()->where(
'email', $user->getEmailForPasswordReset()
)->first();
return $record &&
! $this->tokenExpired($record['created_at']) &&
$this->hasher->check($token, $record['token']);
}
I need to override that method. There's so much inheritance going on. What classes do I need to extend and how to override that method.
First, we will need a pair of routes to handle allowing the user to request a password reset link via their email address. Second, we will need a pair of routes to handle actually resetting the password once the user visits the password reset link that is emailed to them and completes the password reset form.
Register route in api. php file. Route::post('password/email', 'ForgotPasswordController@forgot'); Now, sendResetLink function will send a reset password link with email and token in url.
If you want to override behaviour of \Illuminate\Auth\Passwords\DatabaseTokenRepository
methods, you will have to build your own token repository, overriding methods in the existing repository that currently just check for the 'email' column in the database. Ensure you've created a migration to add the appropriate column:
<?php
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
return new class extends Migration
{
public function up(): void
{
$tname = config("auth.passwords.users.table");
Schema::table($tname, fn (Blueprint $t) => $t->string("mobile", 16));
}
public function down(): void
{
$tname = config("auth.passwords.users.table");
Schema::table($tname, fn (Blueprint $t) => $t->dropColumn("mobile"));
}
}
Then create your custom repository:
app/Auth/DatabaseTokenRepository.php
<?php
namespace App\Auth;
use Illuminate\Auth\Passwords\DatabaseTokenRepository as DatabaseTokenRepositoryBase;
use Illuminate\Contracts\Auth\CanResetPassword as CanResetPasswordContract;
use Illuminate\Support\Carbon;
class DatabaseTokenRepository extends DatabaseTokenRepositoryBase;
{
//
// Override these methods to use mobile as well as email
//
public function create(CanResetPasswordContract $user)
{
$email = $user->getEmailForPasswordReset();
$mobile = $user->getMobileForPasswordReset();
$this->deleteExisting($user);
$token = $this->createNewToken();
$this->getTable()->insert($this->getPayload($email, $mobile, $token));
return $token;
}
protected function deleteExisting(CanResetPasswordContract $user)
{
return $this->getTable()
->where("email", $user->getEmailForPasswordReset())
->orWhere("mobile", $user->getMobileForPasswordReset())
->delete();
}
protected function getPayload($email, $mobile, $token): array
{
return [
"email" => $email,
"mobile" => $mobile,
"token" => $this->hasher->make($token),
"created_at" => new Carbon(),
];
}
public function exists(CanResetPasswordContract $user, $token)
{
$record = (array) $this->getTable()
->where("email", $user->getEmailForPasswordReset())
->orWhere("mobile", $user->getMobileForPasswordReset())
->first();
return $record &&
! $this->tokenExpired($record["created_at"]) &&
$this->hasher->check($token, $record["token"]);
}
public function recentlyCreatedToken(CanResetPasswordContract $user)
{
$record = (array) $this->getTable()
->where("email", $user->getEmailForPasswordReset())
->orWhere("mobile", $user->getMobileForPasswordReset())
->first();
return $record && $this->tokenRecentlyCreated($record['created_at']);
}
}
Now you will need to use this custom token repository instead of the default one. So you have to override another class.
app/Auth/PasswordBrokerManager.php
<?php
namespace App\Auth;
use Illuminate\Support\Str;
use Illuminate\Auth\Passwords\PasswordBrokerManager as PasswordBrokerManagerBase;
class PasswordBrokerManager extends PasswordBrokerManagerBase
{
protected function createTokenRepository(array $config)
{
$key = $this->app['config']['app.key'];
if (Str::startsWith($key, 'base64:')) {
$key = base64_decode(substr($key, 7));
}
$connection = $config['connection'] ?? null;
// return an instance of your new repository
// it's in the same namespace, no need to alias it
return new DatabaseTokenRepository(
$this->app['db']->connection($connection),
$this->app['hash'],
$config['table'],
$key,
$config['expire']
);
}
}
Now you have created a custom broker to use your custom repository. You need a new service provider to make use of it.
app/Providers/PasswordResetServiceProvider.php
<?php
namespace App\Providers;
use App\Auth\PasswordBrokerManager;
use Illuminate\Auth\Passwords\PasswordResetServiceProvider as PasswordResetServiceProviderBase;
class PasswordResetServiceProvider extends PasswordResetServiceProviderBase
{
protected function registerPasswordBroker()
{
$this->app->singleton('auth.password', function ($app) {
// reference your new broker
// the rest of the method code is unchanged
return new PasswordBrokerManager($app);
});
$this->app->bind('auth.password.broker', function ($app) {
return $app->make('auth.password')->broker();
});
}
}
Next, replace default password reset service provider with the custom one in your application config: app/config/app.php
<?php
return [
"providers" => [
...
// Illuminate\Auth\Passwords\PasswordResetServiceProvider::class,
App\Providers\PasswordResetServiceProvider::class,
...
],
];
And finally, define the getMobileForPasswordReset()
method on your user model:
<?php
namespace App;
use Illuminate\Foundation\Auth\User as Authenticatable;
...
class User extends Authenticatable
{
...
public method getMobileForPasswordReset()
{
return $this->mobile;
}
}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With