0

Does anybody know how to customize ResetPassword logic in Laravel. I want to use custom field 'phone' vs 'email'. Small workaround with create_passwords_resets migration and done

public function up()
{
    Schema::create('password_resets', function (Blueprint $table) {
        $table->string('phone')->index();
        $table->string('token');
        $table->timestamp('created_at')->nullable();
    });
}

But i've started to get exceptions on absent field 'email' users tables simply doesn't have it all.

I'm just trying to use Password::Facade like

$status = Password::sendResetLink($request->only('phone'));

Why in such customizable platform hard-coded things like DatabaseTokenRepository even exist?

   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']);
}

How can i override it?

If i try to implement one of the answers on Stack doing this:

namespace App\Auth;

use Illuminate\Contracts\Auth\CanResetPassword as CanResetPasswordContract;
use Illuminate\Auth\Passwords\DatabaseTokenRepository as DatabaseTokenRepositoryBase;
use Illuminate\Support\Carbon;

class DatabaseTokenRepository extends DatabaseTokenRepositoryBase
{
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"]);
}

It throws exception like:

"Declaration of App\Auth\DatabaseTokenRepository::getPayload($email, $mobile, $token) must be compatible with Illuminate\Auth\Passwords\DatabaseTokenRepository::getPayload($email, $token)" 
gluktd
  • 46
  • 5
  • Does this answer your question? [Password reset in Laravel 5.5 by email or mobile](https://stackoverflow.com/questions/58888554/password-reset-in-laravel-5-5-by-email-or-mobile) – miken32 Jul 19 '21 at 17:30
  • @miken32 No, unfortunatily. Even with deleted ->where('email') at all It gives "Declaration of App\Auth\DatabaseTokenRepository::getPayload($email, $mobile, $token) must be compatible with Illuminate\Auth\Passwords\DatabaseTokenRepository::getPayload($email, $token)" – gluktd Jul 19 '21 at 17:47
  • I see those methods are `protected` now, it must have changed since my answer was posted. You will have to re-implement the entire repository class instead of just extending the class and overriding those methods. – miken32 Jul 19 '21 at 17:59
  • 1
    I updated my answer on the duplicate target. – miken32 Jul 19 '21 at 18:16

1 Answers1

0

Valid workaround is original @miken32 answer on Password reset in Laravel 5.5 by email or mobile

But with complete re-implementation of DatabaseTokenRepository class, like:

  <?php

namespace App\Auth;

use Carbon\Traits\Creator;
use Illuminate\Auth\Passwords\TokenRepositoryInterface;
use Illuminate\Contracts\Auth\CanResetPassword as CanResetPasswordContract;
use Illuminate\Contracts\Hashing\Hasher as HasherContract;
use Illuminate\Database\ConnectionInterface;
use Illuminate\Support\Carbon;
use Illuminate\Support\Str;

class DatabaseTokenRepository implements TokenRepositoryInterface
{
    /**
     * The database connection instance.
     *
     * @var \Illuminate\Database\ConnectionInterface
     */
    protected $connection;
/**
 * The Hasher implementation.
 *
 * @var \Illuminate\Contracts\Hashing\Hasher
 */
protected $hasher;

/**
 * The token database table.
 *
 * @var string
 */
protected $table;

/**
 * The hashing key.
 *
 * @var string
 */
protected $hashKey;

/**
 * The number of seconds a token should last.
 *
 * @var int
 */
protected $expires;

/**
 * Minimum number of seconds before re-redefining the token.
 *
 * @var int
 */
protected $throttle;

/**
 * Create a new token repository instance.
 *
 * @param  \Illuminate\Database\ConnectionInterface  $connection
 * @param  \Illuminate\Contracts\Hashing\Hasher  $hasher
 * @param  string  $table
 * @param  string  $hashKey
 * @param  int  $expires
 * @param  int  $throttle
 * @return void
 */
public function __construct(ConnectionInterface $connection, HasherContract $hasher,
    $table, $hashKey, $expires = 60,
    $throttle = 60)
{
    $this->table = $table;
    $this->hasher = $hasher;
    $this->hashKey = $hashKey;
    $this->expires = $expires * 60;
    $this->connection = $connection;
    $this->throttle = $throttle;
}


/**
 * Determine if the token has expired.
 *
 * @param  string  $createdAt
 * @return bool
 */
protected function tokenExpired($createdAt)
{
    return Carbon::parse($createdAt)->addSeconds($this->expires)->isPast();
}

/**
 * Determine if the given user recently created a password reset token.
 *
 * @param  \Illuminate\Contracts\Auth\CanResetPassword  $user
 * @return bool
 */
public function recentlyCreatedToken(CanResetPasswordContract $user)
{
    $record = (array) $this->getTable()->where(
        'phone', $user->getPhoneForPasswordReset()
    )->first();

    return $record && $this->tokenRecentlyCreated($record['created_at']);
}

/**
 * Determine if the token was recently created.
 *
 * @param  string  $createdAt
 * @return bool
 */
protected function tokenRecentlyCreated($createdAt)
{
    if ($this->throttle <= 0) {
        return false;
    }

    return Carbon::parse($createdAt)->addSeconds(
        $this->throttle
    )->isFuture();
}

/**
 * Delete a token record by user.
 *
 * @param  \Illuminate\Contracts\Auth\CanResetPassword  $user
 * @return void
 */
public function delete(CanResetPasswordContract $user)
{
    $this->deleteExisting($user);
}

/**
 * Delete expired tokens.
 *
 * @return void
 */
public function deleteExpired()
{
    $expiredAt = Carbon::now()->subSeconds($this->expires);

    $this->getTable()->where('created_at', '<', $expiredAt)->delete();
}

/**
 * Create a new token for the user.implements TokenRepositoryInterface
 *
 * @return string
 */
public function createNewToken()
{
    return hash_hmac('sha256', Str::random(40), $this->hashKey);
}

/**
 * Get the database connection instance.
 *
 * @return \Illuminate\Database\ConnectionInterface
 */
public function getConnection()
{
    return $this->connection;
}

/**
 * Begin a new database query against the table.
 *
 * @return \Illuminate\Database\Query\Builder
 */
protected function getTable()
{
    return $this->connection->table($this->table);
}

/**
 * Get the hasher instance.
 *
 * @return \Illuminate\Contracts\Hashing\Hasher
 */
public function getHasher()
{
    return $this->hasher;
}
public function create(CanResetPasswordContract $user)
{
    $mobile = $user->getPhoneForPasswordReset();
    $this->deleteExisting($user);
    $token = $this->createNewToken();
    $this->getTable()->insert($this->getPayload($mobile, $token));
    return $token;
}

protected function deleteExisting(CanResetPasswordContract $user)
{
    return $this->getTable()
        ->where('phone', $user->getPhoneForPasswordReset())
        ->delete();
}

protected function getPayload($mobile, $token)
{
    return ['phone' => $mobile, 'token' => $this->hasher->make($token), 'created_at' => new Carbon];
}

public function exists(CanResetPasswordContract $user, $token)
{
    $record = (array) $this->getTable()
        ->where('phone', $user->getPhoneForPasswordReset())
        ->first();
    return $record &&
        ! $this->tokenExpired($record['created_at']) &&
        $this->hasher->check($token, $record['token']);
}

}

gluktd
  • 46
  • 5