Implementing TOTP-based Two Factor Authentication
Here is the story of how to implement a 2FA Authentication.
Firstly, What is Two Factor Authentication?
Two-factor authentication (2FA) is an identity and access management security method that requires two forms of identification to access resources and data. (Microsoft)
There are different types of 2FA authentications.
Some of them are;
- Email-based 2FA
- SMS-based 2FA
- Voice-based 2FA
- Software token/TOTP-based 2FA
- Bio-metrics based 2FA
- As a Push Notification
- Hardware Token-based 2FA
In this article, I implement TOTP-based 2FA.
The TOTP algorithm
The algorithm uses a form of symmetric key cryptography: the same key is used by both parties to generate and validate the token.
The TOTP algorithm follows an open standard documented in RFC 6238. The inputs include a shared secret key and the system time.
How to implement it?
I used some open-source packages for this purpose.
This package (Google2FA) is a PHP implementation of the Google Two-Factor Authentication Module, supporting the HMAC-Based One-time Password (HOTP) algorithm specified in RFC 4226 and the Time-based One-time Password (TOTP) algorithm specified in RFC 6238.
This package provides us to;
- Generate a secret key
- Generate the QR to enable 2FA.
- Verify Code
Generate Secret Key
We need to set each user a secret key. With this secret key, we will generate a QR and the Authenticator Apps will easily scan them. After that, with this secret key, we will be able to validate generated codes from the device.
Example for generating a secret;
$google2fa = new Google2FA();
$secret = $google2fa->generateSecretKey();
// you can give length as first parameter for specify key length
Generating the QR Code
So we need to generate a QR code for scanning with any device. For doing this, basically, we need this code block.
public function createInlineQRCode($email, $secret)
{
$google2fa = new Google2FA();
$inlineUrl = $google2fa->getQRCodeInline(
'APP_NAME',
$email,
$secret
);
return $inlineUrl;
}
This will create an inline QR code. You need to echo this. If any page you can easily use these headers and echo a QR code.
header('Content-Type: image/svg+xml');
header('Cache-Control: no-cache, no-store, must-revalidate');
header('Pragma: no-cache');
header('Expires: 0');
echo $this->createInlineQRCode($user->email, $user->secret);
Verify Codes
In the last step, we need to verify generated code. With this library, you need to use this code block for verifying entered code.
public function verify($secret, $code)
{
$google2fa = new Google2FA();
return $google2fa->verifyKey($secret, $code);
}
Basically, you need to pass the secret and code (entered code from the user) to verifyKey method.
What is Flow?
I said basic things about the technical part. Let’s deep dive into real implementation with the flow.
This is the main flow of my implementation. Of course, there are different types and different flows but for this implementation, it is enough.
2FA Endpoints
I created basic 2 endpoints to implement this flow in the backend part.
$app->router->get('/2fa/qr-code', 'TwoFAController@printQRCode');
$app->router->post('/2fa/verify', 'TwoFAController@verify2FA');
QR-code endpoint basically prints a QR code image with proper HTPP headers, and I use it to scan from any device.
Verify endpoint is for verifying the given code from the user with a related security key. This endpoint just gets the code parameter, we store the secrets in the database and do not show them to the client.
Where do we store secrets?
We need to store secret keys, and we can store them in users tables or the user_settings table if you have them. It is totally up to you and your design.
If you decide to store it in the users table your SQL query is like this;
ALTER TABLE users ADD COLUMN secret varchar(32) DEFAULT NULL;
Note: To be compatible with Google Authenticator we need to store Base32 strings.
Enabling & Disabling 2FA
It is important to give you the option to disable or enable 2FA for your users. You can store a boolean in your DB for this option. Like named two_factor_enabled.
- You must check the code before enabling this option. The user scans the QR and just asks to enter the code before enabling this 2FA.
- If the user disables 2FA, be sure to delete the user's related secret. This is for revoking generated new keys from old devices.
This is before enabling 2FA. Users need to scan QR and must enter the code to enable 2FA.
QR code generated by 2fa/qr-code endpoint on the fly.
After enabling this 2FA, it is asking to enter 6 digits verification code.