Programster's Blog

Tutorials focusing on Linux, programming, and open-source

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.

This tutorial will focus on just adding support for 2FA in PHP. This could be for either a website, or a CLI utility. It makes no difference.

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:

References

Last updated: 9th August 2021
First published: 23rd March 2021