Implementing TOTP Google Authenticator with PHP

By | November 2, 2017

Today I decided to write an article on how to implement the TOTP Google Authenticator into your website’s login authentication system using PHP for any purpose (software, mobile app, website). It does not matter whether you want to use it for a website or software you have created or a mobile phone app, the Google Authenticator does not have to be necessarily used with PHP and either way you can use PHP for your authentication interface anywhere.

What is TOTP?
TOTP is a short form for Time-based One-time Password (usually called Token) which is password that can only be used once and is only valid to be used in a defined time range. Usually TOTP generators generate new passwords every defined number of seconds or minutes.

What is Google Authenticator?
Google Authenticator is a TOTP/HOTP generator you can freely use for your software, app or website authentication.

How does it work?
A simplified explanation would be, both google authenticator app and your authentication program know the same secret key and can compute the same token for a certain range in time based on this secret key.
A more technical explanation can be read here:

Getting Started
To get started you will have to download the Google Authenticator app to your smartphone or tablet etc.
In this implementation example I will be using a PHP Framework by Michael Kliewe (PHPGangsta). From my point of view the framework is lightweight and just perfect for this example. I will not be changing the author’s initial code.
The author has stored the Framework on his github repo. Feel free to download in order to follow along.

The github link already provides all the code you need to link your Google Authenticator app.
The following code (based on the example code created by the author) will generate a Secret Code and use it to generate a QR CODE Image. Run this code on your server and scane the QR CODE with your device in order to link your authenticator and start generating your tokens.

require_once 'PHPGangsta/GoogleAuthenticator.php';

$websiteTitle = 'MyWebsite';

$ga = new PHPGangsta_GoogleAuthenticator();

$secret = $ga->createSecret();
echo 'Secret is: '.$secret.'<br />';

$qrCodeUrl = $ga->getQRCodeGoogleUrl($websiteTitle, $secret);
echo 'Google Charts URL QR-Code:<br /><img src=”'.$qrCodeUrl.'” />';

$myCode = $ga->getCode($secret);
echo 'Verifying Code '.$myCode.'<br />';

//third parameter of verifyCode is a multiplicator for 30 seconds clock tolerance 
$result = $ga->verifyCode($secret, $oneCode, 1);
if ($result) {
   echo 'Verified';
} else {
   echo 'Not verified';

Creating the authentication page
In my example I will be using a login.php page where I have a textbox and a button. I will be entering the tokens into the textbox and use Ajax to verify the token. For the Ajax I will be using the jquery framework.
The complete login.php page will look like as follows.

<!DOCTYPE html>
    <div id="loginstatus">Not logged in</div>
    <div id="loginform">
        Code: <input type="text" id="googlecode" />
        <input type="submit" id="submit-googlecode" value="Submit" />
    <script src="" type="text/javascript"></script>
    <script src="js/event.js" type="text/javascript></script>

I have decided to have my JavaScript code in a seperate file I named event.js. However, if you like you can put it directly into the HTML.

$('input#submit-googlecode').on('click', function() {
    var googlecode = $('input#googlecode').val();
        if ($.trim(googlecode) != '') {
            $.post('ajax/check.php', {code: googlecode}, function(data) {
                    if (data == 1) {
                        $('div#loginstatus').text('Logged in');

Now, here is what I see when visiting the login page on my webserver.

In order to explain this a little bit without going into detail with Ajax, the script will execute an anonymous function when my submit button is clicked. It will grab the input googlecode text from the textbox check whether there is content. If there is it will execute a PHP file with the google code as a POST parameter. We will get to the php file in a moment. If the PHP returns a 1 then the authentication was successful and the javascript will replace ‘Not logged in’ with ‘Logged in’ and hide the authentication textbox and submit button. If it does not, nothing will happen.
In my PHP file check.php located in an ajax directory I will have to import the GA Framework.
The authenticating PHP requires knowing the Secret Code in order to validate the token. In my example I hardcode the Secret Code into the PHP file because in my case it is a very dry example.
The PHP program receives the token in a POST parameter named ‘code’. If this POST parameter is set then it will execute the GA Authentication method for verifying the code. If the result is equal to 1 it will return it, else it will return ‘Login failed’.

    require_once '../PHPGangsta/GoogleAuthenticator.php';

    if (isset($_POST['code'])) {
           $code = $_POST['code'];

            $ga = new PHPGangsta_GoogleAuthenticator();
            $result = $ga->verifyCode($secret, $code);

            if ($result == 1) {
                    echo $result;
            } else {
                    echo 'Login failed';

Now I can simply try entering my current “One-time” password. Once I click submit, the input mask will hide and show that I logged in.

To retain this as a ‘On-time’ Password Authentication you would want to store all used codes in a database together with your user.

You can see that the implementation is actually really simple and does not require many resources or any sort of real time connection between your service and the authentication app. All that happens is that both, the authenticator and google’s app are able to generate the current time-based valid token using your secret key and because the token expires and becomes invalid after a short amount of time, it is almost 100% safe against current bruteforcing methods. The only way someone could possibly generate the same code is by either knowing your secret key or guessing with sheer luck.

Leave a Reply

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