TheYaXxE
TheYaXxE

Reputation: 4294

How to handle reset-password request after it use/expire

I'm building a reset password function on my site, and it is working pretty good. But I am a little uncertain at some points.

Right now I have a table like this:

password_change_request:

ID    |     user_id       |       token       |      created_time

In the user_id-field the users username will be stored. In the token -field, the genereted and encrypted token will be stored. And finally in the created_time, the timestamp for the request time will be stored.


Make the request:

When users enteres their email in the Forgot Password form, the system will look up in the users-table to check if the user exist. If the user do exist, it will find the users username.

It will also generate a token by encrypting the users email along with a random SALT.

Then it will send a link with the username and token to the entered email-address, like this: http://yoursite.com/login.php?action=reset&token=28130dh29sd129809sda802e&user=yourusername

The token will be extra encrypted when it is stored in the database.

Reset password:

When the user has clicked on the link, in the email, it will lead the user to a site that will check if there exist any request in the password_change_request-table, with the given username and encrypted token.

If it does, it will check if the request is over 2 hours old by calculating the difference between the created_time in the database and the current time.

If it is not over 2 hours old, two fields will appear, where the user can enter a new password, and then reenter it for security reasons.

When the user has entered a new password and then hit on the "Reset" button, a new SALT for the user will be generated and the new password, encrypted with the salt, will be updated in the users-table.

When the new password has been updated and if the request is over 2 hours old, the request will be deleted in from the password_change_request-table.


Is this a good way to do it? It has been inspired by WordPress and other answers on this site.

But one thing I could find was what would happen to the request when it has been used/expired?

Should it just be deleted permanently from the database? I cannot seem to find another way to do so. Without this feature, the user could just be reusing the link, again and again.

So beside if it is a good way or not, the main question is how to handle the requests after they have been used or expired?

Or should I just put the token in a column of the users-table, like WordPress do, and then just clear the field, when request has been used/expired?

Hope someone can help me :)

Upvotes: 0

Views: 2191

Answers (3)

majidarif
majidarif

Reputation: 20105

See how Laravel Framework does it from the docs.

The source can be found here.

Basically it asks the user for an email sends a link to a password reset with hash binded to the email. Hash is validated then a new password and a new password confirmation is asked.

You can learn more by checking the source I linked to on github.

// Create a new reminder record and token.
public function create(RemindableInterface $user)
{
    $email = $user->getReminderEmail();

    // We will create a new, random token for the user so that we can e-mail them
    // a safe link to the password reset form. Then we will insert a record in
    // the database so that we can verify the token within the actual reset.
    $token = $this->createNewToken($user);

    $this->getTable()->insert($this->getPayload($email, $token));

    return $token;
}

// Create a new token for the user.
public function createNewToken(RemindableInterface $user)
{
    $email = $user->getReminderEmail();

    $value = str_shuffle(sha1($email.spl_object_hash($this).microtime(true)));

    return hash_hmac('sha1', $value, $this->hashKey);
}

// Determine if the reminder has expired.
protected function reminderExpired($reminder)
{
    $createdPlusHour = strtotime($reminder->created_at) + $this->expires;

    return $createdPlusHour < $this->getCurrentTime();
}

Upvotes: 1

tormuto
tormuto

Reputation: 585

Yeah since you are using a dedicated table for this task, it's advisable to simple delete the used row entirely; why keeping it?

Secondly, you can decrypt the hash to extract the time since it will be in the same order as when you encrypt it.

   $difference=time()-$extractedTime;       //kindof
   $hoursDifference=ceil($difference/7200); //2*60*60=7200

if this is helpful please take a moment to vote it, Thanks.

Upvotes: 0

tormuto
tormuto

Reputation: 585

It is better to rather include the time in the hash. Then you can extract the time difference when the user come in through the link with the hash. You can also clear the hash field after use. In that way, you will be able to specifically inform the user if the link has expired, or invalid, or if no pending request has been made at all.

Upvotes: 0

Related Questions