Two-step verification with php and Google Authenticator

A security feature in system authentication that has become quite popular recently is 2-step verification. The main banking and brokerage applications use their own apps to generate tokens that are dynamic (they are constantly updated in low cycles of time), and used as a 2nd factor for system access (the 1st factor is the traditional username/mail and password ).

However, if you do not want to implement your own token generator, you can use Google Authenticator, an application with the mentioned features (available for Android and IOS) and that can be easily integrated into your application or web system. In this post, I’m going to show you how to integrate your php application with Google Authenticator.

Basically, the user of the app / system needs to associate his account with the Google Authenticator application installed on his smartphone. This association is made by reading a QR Code, or entering the code manually (in both cases, the code must be generated from the system itself). In the image below, you can view the main interface of the Google Authenticator app. Each set of 6 digits refers to a specific system.

Image 1: Google Authenticator main interface
Image 1: Google Authenticator main interface

To integrate a php application with Google Authenticator, we will use the sonata-project/GoogleAuthenticator (https://github.com/sonata-project/GoogleAuthenticator) library. . Let’s do it through composer.

composer require sonata-project/google-authenticator

In our example, we will have 2 files:

  • genqrcode.php – responsible for generating the QR Code, so that the user can scan
  • check.php – validates the 6-digit code (generated by Google Authenticator) and then allows or denies access to the system

In the genqrcode.php file we will insert the following code:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
<?php 
include_once 'vendor/sonata-project/google-authenticator/src/FixedBitNotation.php';
include_once 'vendor/sonata-project/google-authenticator/src/GoogleAuthenticatorInterface.php';
include_once 'vendor/sonata-project/google-authenticator/src/GoogleAuthenticator.php';
include_once 'vendor/sonata-project/google-authenticator/src/GoogleQrUrl.php';
 
$g = new \Google\Authenticator\GoogleAuthenticator();
$secret = 'XVQ2UIGO75XRUKJO';
//Optionally, you can use $g->generateSecret() to generate your secret
//$secret = $g->generateSecret();
 
//the "getUrl" method takes as a parameter: "username", "host" and the key "secret"
echo '<img src="'.$g->getURL('rafaelwendel', 'rafaelwendel.com', $secret).'" />';
 
?>

In line 8 I created a variable called “$secret”. This variable will be responsible for storing my application’s key. I recommend that you use a good key. The better, the safer. This key must be used when generating the QR Code, and later we must use the same key to be able to validate an authentication code.

Line 10 generates an image (QR Code) for the user to scan from the Google Authenticator app on their cell phone.

Finally, let’s implement the check.php file

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
<?php 
 
include_once 'vendor/sonata-project/google-authenticator/src/FixedBitNotation.php';
include_once 'vendor/sonata-project/google-authenticator/src/GoogleAuthenticatorInterface.php';
include_once 'vendor/sonata-project/google-authenticator/src/GoogleAuthenticator.php';
include_once 'vendor/sonata-project/google-authenticator/src/GoogleQrUrl.php';
 
$g = new \Google\Authenticator\GoogleAuthenticator();
$secret = 'XVQ2UIGO75XRUKJO';
 
$code = '010989'; //6-digit code generated by Google Authenticator app
 
if($g->checkCode($secret, $code)){
	echo 'Authorized!';
}
else{
	echo 'Incorrect or expired code!';
}

In line 11 I enter the code generated by the Google Authenticator app. Obviously, you will have a form for this code to be filled in by the user, submitted and retrieved. Remember that this code is dynamic and its life cycle is extremely short (just a few seconds). Also notice that in line 9 I use the “$secret” key, and it must be EXACTLY the same as the one we defined there in genqrcode.php

To carry out the verification and then release access, the code generated by the app and the “$secret” key will be analyzed using the “checkCode” method.

I hope you like the content and any questions leave your comment.

See you!

Holds a university degree in Information Systems, a postgraduate degree in Database Systems and a master's degree in Education with a focus on Sociocommunity Technologies. He works as a professor of technical and technological education at the Federal Institute of Education, Science and Technology of São Paulo, teaching subjects in the areas of programming, database, project development and software engineering.

Posts relacionados

Comentários

  1. Hi Rafael

    I’ve just been testing this one out and have used exactly the code above (except changing the $code that is passed back by the google authenticator app to the one it currently displays), however while it generates the QR code and authenticator accepts it, when i run the check.php file with the appropriate code in the $code field i always receive the ‘incorrect or expired code’ error.

    Code i’ve used is exactly as posted above, although have also tried using different secrets, usernames and hosts but all are doing the same.

    Has google updated/changed something in the last couple months which has stopped it from working, or is there some kind of dependency not mentioned above? (No other errors show either on screen or in the php log files, so im not sure where to go from here

    1. Hi James,

      I don’t think there has been any change from Google in recent months.

      I just rerun my code and everything is working fine.

      Please verify:

      1. If the $secret used in both files is the same
      2. If your smartphone’s date and time settings are correct (See this video: https://www.youtube.com/watch?v=Dc-0FFPmjY4)
      3. I also have a more complete video tutorial, but unfortunately only in PT-BR (https://youtu.be/fqy6FYV_jAA)

      See you!

  2. Hi Rafael,
    I created a random string as secret code but it didn’t worked .
    So after a good research I found that we can use genrateSecret function like
    $g->genrateSecret() and it worked perfectly fine for my code .
    Hope it helps.

Leave a Reply

Your email address will not be published. Required fields are marked *