2fa library
This library allows developers to implement Time Based One Time Passwords (TOTP) for the PHP implementation of the 2factor Authentication (2FA), supporting both the HMAC-based one-time password (HOTP) and the time-based one-time passwords (TOTP).
Getting Started
The preferred way to install this extension is through composer.
Either run
php composer.phar require 2amigos/2fa-library:^2.0
or add
"2amigos/2fa-library": "^2.0"
Usage
How to generate a secret key
The secret key requires to be saved to later check with the input key from the user to implement the two-factor authentication (2FA).
This is how you create a secret key:
use Da\TwoFA\Manager;
$secret = (new Manager())->generateSecretKey();
How to use two-factor authentication
The first thing you need to do is to generate the secret key for your user before is saved.
use Da\TwoFA\Manager;
$manager = new Manager();
$user->twofa_secret = $manager->generateSecretKey();
Then, when user chooses to include two-factor authentication as login, you should present a QR Code to the user to scan using one of the two-factor authentication apps. There are couple of ways to display it. For example, with data-uri:
<?php
use Da\TwoFA\Service\TOTPSecretKeyUriGeneratorService;
use Da\TwoFA\Service\QrCodeDataUriGeneratorService;
// first we need to create our time-based one time password secret uri
$totpUri = (new TOTPSecretKeyUriGeneratorService('your-company', $user->email, $user->twofa_secret))->run();
$uri = (new QrCodeDataUriGeneratorService($totpUri))->run();
?>
<!-- display it -->
<img src="<?= $uri ?>" alt="" />
With google uri:
<?php
use Da\TwoFA\Service\TOTPSecretKeyUriGeneratorService;
use Da\TwoFA\Service\GoogleQrCodeUrlGeneratorService;
// first we need to create our time-based one time password secret uri
$totpUri = (new TOTPSecretKeyUriGeneratorService('your-company', $user->email, $user->twofa_secret))->run();
$googleUri = (new GoogleQrCodeUrlGeneratorService($totpUri))->run();
?>
<!-- display it -->
<img src="<?= $googleUri ?>" alt="" />
Or by pointing the src
attribute of the img
tag to a script (or action if you use a framework) that will create the
Qr Code.
When the Qr Code is presented, the user should scan it with their favorite 2FA Authenticator mobile app. Then, to verify, after user has logged in with their regular credentials you should present a new screen where user can enter the code given by its 2FA Authenticator mobile app:
use Da\TwoFA\Manager;
$manager = new Manager();
$valid = $manager->verify($_POST['key'], $user->twofa_secret);
Preventing out of sync clocks
In order to avoid sync issues between servers, we have the $cycles
attribute. By default, each cycle of key generation
is about 30 seconds. We can change that value by modifying the $counter
attribute. If $cycles
is a positive number it
will increase verification times, past and future, from current time. That is, if $cycles
is set to 1
(value by
default), it will increase 1 more time the seconds set in $counter
. If $counter
is set to 30
then it will be 60
.
Set this attribute to 0
if you don't care about this sync issue.
use Da\TwoFA\Manager;
$manager = new Manager();
$valid = $manager
->setCycles(2) // 120 seconds (60 seconds past and future respectively)
->verify($_POST['key'], $user->twofa_secret);
Remember that you can also modify the $counter
, which defaults to 30
seconds, by the Manager::setCounter
method.
Preventing insertion of a previous code
To prevent the usage of a key code more than once (in case someone has seen your code) you can include the latest time
he was validated. If null
is provided, the method verify
will return a boolean value, but if we pass a timestamp
value, it will return a timestamp
value that we can safe for future verifications.
use Da\TwoFA\Manager;
$manager = new Manager();
$previousTs = $user->twofa_timestamp ? : $manager->getTimestamp();
$timestamp = $manager->verify($_POST['key'], $user->twofa_secret, $previousTs);
if($timestamp) {
// ... success, update user timestamp
$user->twofa_timestamp = $timestamp;
$user->save();
} else {
// ... error
}
Bigger keys & prefixing secret keys
You can harden the collision probabilities of the random string by incrementing the length of the secret keys:
use Da\TwoFA\Manager;
$manager = new Manager();
$user->twofa_secret = $manager->generateSecretKey(32);
By prefixing keys, we need to remember that all keys must have length in power of 2. So, if the key is 16 bytes long,
your prefix string must be also 16 bytes long. But as the prefixes will be converted to Base32
, the maximum length of
that prefix is 10 bytes. Therefore, the sizes of your prefixes would be: 1, 2, 5, 10, 20, 40, 80 ...
, and it can be
used like:
use Da\TwoFA\Manager;
$manager = new Manager();
$prefix = strpad($id, 10, 'X');
$secretKey = $manager->generateSecretKey(16, $prefix);
Google Authenticator compatibility
To be compatible with Google Authenticator, your (converted to base 32) secret key length must be at least 8 chars and
be a power of 2: 8, 16, 32, 64... This is why the library forces you to create the secret key throughout its
Manager::generateSecretKey()
or Encoder::generateBase32RandomKey()
methods. You should only be concerned about the
prefix as stated previously.
By default, the library enforces google compatibility, but if Google Authenticator is not a target, you can disable it like this:
<?php
use Da\TwoFA\Manager;
use Da\TwoFA\Validator\SecretKeyValidator;
$validator = new SecretKeyValidator(false);
$manager = new Manager(false);
?>
or:
<?php
use Da\TwoFA\Manager;
$manager = (new Manager())->disableGoogleAuthenticatorCompatibility();
?>
IMPORTANT: Server Time
It's very important to keep the server time in sync with an NTP server.
Links
- How to sync your linux server time with network time servers (NTP)
- Use NTP to sync time
- Sync linux server time with network time protocol (NTP) servers
- Configure Window Server 2008/2012 to sync with Internet time servers
Credits
This library is highly inspired by the excellent work of Antonio Ribeiro. This library is a different approach of what he did, and therefore deserves all the credit of this one. The reason to create our very own version, was no other that to have full control of updates and fixes and provide a different version of what he did, as we are going to implement it on our other library yii-usuario.
If you wish to check his library, please visit:
© 2amigos 2013-2020