Setting Up Google 2FA on PHP
There are actually quite a few packages for implementing two-factor-authentication in PHP but I am going to be using pragmarx/google2fa for this tutorial, simply because it has 6.26 million installs at the time of writing this tutorial. That doesn't necessarily mean its great, but it's a good rule of thumb.
Steps
First we need to install the package, which is as easy as:
composer require pragmarx/google2fa
Then we can make use of it to generate a secret for our user(s).
<?php
# Include packages
require_once(__DIR__ . '/vendor/autoload.php');
# Create the 2FA class
$google2fa = new PragmaRX\Google2FA\Google2FA();
# Print a user secret for user to enter into their phone.
# The application needs to persist this somewhere safely where other users can't get it.
$userSecret = $google2fa->generateSecretKey();
print "Please enter the following secret into your phone:" . PHP_EOL . $userSecret . PHP_EOL;
Now you have a secret for our user(s), they can feed it into their Google authenticator application.
Now when that user wishes to authenticate, the application needs to do the following:
<?php
# Include packages
require_once(__DIR__ . '/vendor/autoload.php');
# Create the 2FA class
$google2fa = new PragmaRX\Google2FA\Google2FA();
# Get the 2FA code from the user. If this is a website, you would fetch from a posted field instead.
print "Please enter your 2FA code:" . PHP_EOL;
$code = readline();
# Fetch/load the user secret in whatever way you do.
$userSecret = fetchUserSecretFromPersistenceStore();
# Verify the code is correct against our persisted user secret.
# This returns true if correct, false if not.
$valid = $google2fa->verifyKey($userSecret, $code);
print ($valid) ? "Authenication PASSED!": "Authentication FAILED!";
print PHP_EOL;
Taking It Further With QR Codes
The biggest problem with the solution above is that $google2fa->generateSecretKey()
generates a long string that is tedious to enter into a phone manually.
To get around this, we can expose a QR code to the user to make it easier to add to the authenticator application.
Install Package
First we need to install our QR code generator package.
composer require bacon/bacon-qr-code
Generate QR Code
<?php
# Include packages
require_once(__DIR__ . '/vendor/autoload.php');
# Create the 2FA class
$google2fa = new PragmaRX\Google2FA\Google2FA();
# Create data that will go into the QR code
# The title will show up first in the authenticator app,
# followed by the username wrapped in `()`
$title = "blog.programster.org";
$usernameOrEmail = "admin@programster.org";
$userSecret = fetchUserSecretFromPersistenceStore();
$qrCodeData = $google2fa->getQRCodeUrl(
$title,
$usernameOrEmail,
$userSecret
);
print "QR code url is: $qrCodeUrl" . PHP_EOL;
# Now create the QR code image from the URL.
$renderer = new \BaconQrCode\Renderer\ImageRenderer(
new \BaconQrCode\Renderer\RendererStyle\RendererStyle(400),
new \BaconQrCode\Renderer\Image\ImagickImageBackEnd()
);
$writer = new BaconQrCode\Writer($renderer);
$writer->writeFile($qrCodeData, 'qrcode.png');
print "Please open qrcode.png and scan it with your google authenticator app." . PHP_EOL;
Debugging
If you get the message:
PHP Fatal error: Uncaught BaconQrCode\Exception\RuntimeException: You need to install the imagick extension to use this back end
Then you just need to make sure to install the php8.0-imagick
package.
Conclusion
You should now have enough to get started with setting up 2FA in your application. This tutorial was aimed at getting you started quickly, but you may wish to read up on how you can take it further by:
- changing the default hashing algorithm from the default of SHA1 to SHA256 or SHA512.
- Change the validation window so that tokens last for a different amount of time, and or work for x number of cycles.
- Allow tokens can only be used once (using
verifyKeyNewer
)
References
First published: 23rd March 2021