I tend to have a User or Member controller for this sort of thing. I also implement it with two actions:
request_password_reset -
Requests the user’s email address and a captcha code. A VerificationCode model generates a unique code (say 20 chars). Then send an email to the user with a link to the reset_password action and the user ID, verification code record ID and the code itself as query parameters.
reset_password -
Checks that the user ID and the ID and code of the verification code record match the database and prompts the user to enter and reenter their new password.
I have a VerificationCode model for the purposes of generating these codes and deactivating them once they’ve been used.
Here’s an example of the sort of URL that’s generated:
I use a separate table called VerificationCode, which allows you to generate codes for different purposes, such as email verification at registration and password reset requests.
The VerificationCode model takes care of generating new records with associated expiry times, and then verifying and deactivating the codes.
The schema I use is:
CREATE TABLE `VerificationCode` (
`Id` int(11) NOT NULL AUTO_INCREMENT,
`ForeignTableId` varchar(20) COLLATE utf8_unicode_ci NOT NULL,
`Type` enum('account_activation','password_reset') COLLATE utf8_unicode_ci NOT NULL,
`UserId` int(11) NOT NULL,
`VerificationCode` varchar(20) CHARACTER SET utf8 COLLATE utf8_bin NOT NULL,
`Created` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,
`Expires` timestamp NOT NULL DEFAULT '0000-00-00 00:00:00',
`Active` tinyint(1) NOT NULL DEFAULT '1',
PRIMARY KEY (`Id`),
KEY `ForeignTableId` (`ForeignTableId`,`Type`,`UserId`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci;
There are lots of ways to handle this sort of functionality though, so use whatever works best for your app. I think it’s fairly common to have a verification code column in the user table and just use that.
public static function checkCode($type, $verificationId, $verificationCode, $foreignTableId = null, $userId = null)
{
$record = VerificationCode::model()->findByPk($verificationId);
if ($record === null)
throw new Exception('Verification record not found.');
if ($type !== $record->Type)
throw new Exception('Invalid credentials.');
if ($verificationCode !== $record->VerificationCode)
throw new Exception('Invalid credentials.');
if ($userId !== null && $userId !== $record->UserId)
throw new Exception('Invalid credentials.');
if ($foreignTableId !== null && $foreignTableId !== $record->ForeignTableId)
throw new Exception('Invalid credentials.');
if (!$record->Active || $record->expired)
throw new Exception('This verification code has expired. Please request a new one.');
}
Hi guys, first of all, thank you a lot for this discussion. But I still have a question that how can we make a record of VerificationCode table to be deactive when the expired_date < current_date ? Is it possible for check it inside Mysql (when the expired_date is <= current_date, automatically change the active field) ?