PHP class to generate and verify Google Authenticator 2-factor authentication

Overview

Google Authenticator PHP class

Build Status

This PHP class can be used to interact with the Google Authenticator mobile app for 2-factor-authentication. This class can generate secrets, generate codes, validate codes and present a QR-Code for scanning the secret. It implements TOTP according to RFC6238

For a secure installation you have to make sure that used codes cannot be reused (replay-attack). You also need to limit the number of verifications, to fight against brute-force attacks. For example you could limit the amount of verifications to 10 tries within 10 minutes for one IP address (or IPv6 block). It depends on your environment.

Usage:

See following example:

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

$ga = new PHPGangsta_GoogleAuthenticator();
$secret = $ga->createSecret();
echo "Secret is: ".$secret."\n\n";

$qrCodeUrl = $ga->getQRCodeGoogleUrl('Blog', $secret);
echo "Google Charts URL for the QR-Code: ".$qrCodeUrl."\n\n";

$oneCode = $ga->getCode($secret);
echo "Checking Code '$oneCode' and Secret '$secret':\n";

$checkResult = $ga->verifyCode($secret, $oneCode, 2);    // 2 = 2*30sec clock tolerance
if ($checkResult) {
    echo 'OK';
} else {
    echo 'FAILED';
}

Running the script provides the following output:

Secret is: OQB6ZZGYHCPSX4AK

Google Charts URL for the QR-Code: https://www.google.com/chart?chs=200x200&chld=M|0&cht=qr&chl=otpauth://totp/infoATphpgangsta.de%3Fsecret%3DOQB6ZZGYHCPSX4AK

Checking Code '848634' and Secret 'OQB6ZZGYHCPSX4AK':
OK

Installation:

  • Use Composer to install the package

  • From project root directory execute following

composer install

  • Composer will take care of autoloading the library. Just include the following at the top of your file

    require_once __DIR__ . '/../vendor/autoload.php';

Run Tests:

  • All tests are inside tests folder.
  • Execute composer install and then run the tests from project root directory
  • Run as phpunit tests from the project root directory

ToDo:

  • ??? What do you need?

Notes:

If you like this script or have some features to add: contact me, visit my blog, fork this project, send pull requests, you know how it works.

Comments
  • question

    question

    hi i set the $secret to a string typed by me, and then i typed the same thing in the 2 fact app provided by google but they won't generate the same key any help?

    opened by darkpowerxo 7
  • Can't verify code from device

    Can't verify code from device

    Hello. I has been using this library for many months on my website and it worked alright for a long time, but we changed hosting-provider and we faced the problem. Users reported that they can't log in with 2fa or can't set 2fa on their accounts.

    I started researching. Codes generated on my device (Iphone 8 with latest Google Authenticator App from AppStore) and codes on server are different in ~80-90% of my tries, at that script returns successful results pretty chaotically. For example, i can get 20 false results after calling verifyCode() and then get 1-3 true results in a row or get only 1 true result after 30 tries.

    Everywhere on the web i can see advice to sync time in GA app, but, first of all, there is no such settings on iOS GA app no more (i guess that app does it authomaticly). Secondly, i checked time on my server with command date and it's normal UTC time that fully equal with UTC time i checked with Google.

    I added var_dump($calculatedCode) inside forloop in method verifyCode(), and i get a strange result after calling verifyCode method with code from my device:

    string(6) "695201" string(6) "781292" string(6) "000000" string(6) "419982" string(6) "774585" bool(false)

    It's 5 codes (with discrepancy = 2) generated by php script, and sometimes it prints one or couple 000000 codes, but sometimes all 5 codes contain numbers.

    Also, i see that error in backend log:

    PHP Warning: unpack(): Type N: not enough input, need 4, have 0 in /html/system/libs/ga.class.php on line 83

    It appears every time when method getCode() returns 000000 code and i guess it's because of some wrong data put in unpack() func:

    In getCode(): // Unpak binary value $value = unpack('N', $hashpart);

    What's the reason of that? How can i solve that problem?

    I tried to use another lib https://github.com/Dolondro/google-authenticator but i faced absolutly same problem with same errors in backend log.

    opened by karabone 6
  • Sync with GA app at the mobile timing and script

    Sync with GA app at the mobile timing and script

    Hello.

    How I can sync GA app timing at the mobile with PHP script?

    In the example I see:

    $checkResult = $ga->verifyCode($secret, $oneCode, 2);    // 2 = 2*30sec clock tolerance
    if ($checkResult) {
        echo 'OK';
    } else {
        echo 'FAILED';
    }
    

    But in the GA app another timing ... And when I put code from GA app its valid. But when time is over in the GA app code still valid.

    Thanks.

    opened by tcolonel 5
  • Avoid TOTP code replay attacks

    Avoid TOTP code replay attacks

    As far as I can tell, GoogleAuthenticator library doesn't prevent a replay attack.

    For this to be relevant we have to assume that either the victim is using an insecure network or has malware on their computer - both of which are not ideal, but happen more often than we'd like.

    Would it be possible for GoogleAuthenticator library itself to prevent a replay? My guess is that the library doesn't have any way to persist data so it can't really prevent this kind of attack, but maybe you have ideas on that.

    If it's not possible to solve in the library, then I suggest adding a note to the README to encourage people who use the library to add a feature to their code to prevent replay.

    opened by greggles 5
  • Google Charts API is deprecated and not available since March 19, 2019

    Google Charts API is deprecated and not available since March 19, 2019

    Google Charts API has a nice warning about that:

    Warning: This API is deprecated and is scheduled to be turned off on March 14, 2019. Please use the actively maintained Google Charts API instead.

    And returns 502 instead of qr-code: 2019-03-19_09-19

    opened by luxemate 4
  • Generated Code in GA-App wrong

    Generated Code in GA-App wrong

    Hey,

    i've got the problem, that when i use the secret timebased in my GA-App the code the App generates is different than the code that would be right. I've not changed the secret etc.

    My Code: [<?php require_once 'PHPGangsta/GoogleAuthenticator.php';

    $ga = new PHPGangsta_GoogleAuthenticator(); $secret = $ga->createSecret(); echo "Secret is: ".$secret."\n\n";

    $qrCodeUrl = $ga->getQRCodeGoogleUrl('Blog', $secret); echo "Google Charts URL for the QR-Code: ".$qrCodeUrl."\n\n";

    echo '';

    $oneCode = $ga->getCode($secret); echo "
    "; echo "Checking Code '$oneCode' and Secret '$secret':\n";

    $oneCode = "MY_CODE_FROM_THE_GA_APP";

    $checkResult = $ga->verifyCode($secret, $oneCode, 2); // 2 = 2*30sec clock tolerance if ($checkResult) { echo 'OK'; } else { echo 'FAILED'; }]

    At first I scan the QR Code in my GA-App or i type the secret in there (Both tried). And everytime before I launch the script, I insert the code the GA-App gives me at the "MY_CODE_FROM_THE_GA_APP" place. But the Code given me by the App is always a different Code than "$oneCode" and so the verification fails. I also syncronized the time within the settings of the App but it didn't solve the problem. I also tried both ways to import the secret to the App (manually typing in the secret and via QR-Code)

    Now I've got no more clues where the problem might be.

    Thanks in advance m-schoeffel.

    opened by mschoeffel 4
  • Fixed bug that malformes QR URL

    Fixed bug that malformes QR URL

    Hi,

    I've just spent 2 hours trying to figure out why my QR wouldn't show. If I visited the Google URL, it said it was malformed.

    The problem the person is having here: http://stackoverflow.com/questions/11074191/google-2-factor-authentication-qr-code-issues is about the same problem I was having.

    I eventually fixed this problem my applying a proper urlencode (yours was partially encoded already, but you forgot the user defined variables) and secondly by changing the URL from google.com to chart.googleapis.com. Don't ask me why but the latter seems to be working better.

    Good luck and thanks for making this plugin!

    opened by edwardmp 4
  • Avoid timing attacks and fix #24

    Avoid timing attacks and fix #24

    Time attacks can be used to improve one Brute Force attack More info can be found here: http://blog.ircmaxell.com/2014/11/its-all-about-time.html http://sakurity.com/blog/2015/07/18/2fa.html

    This comparison will also avoid the problem of leading zeros (#24) :)

    opened by leandro-lugaresi 3
  • Codes are not syncing up

    Codes are not syncing up

    I have been having a huge problem. I have not been able to figure out how to sync up the codes, when I use the $oneCode it gives me a number but its not the same as the one on my phone. Both are set to the correct time so I do not know what to do.

    Please lend me any suggestions you have.

    opened by jfm-so 3
  • Several bugs / comments

    Several bugs / comments

    verifyCode discrepancy bug

    verifyCode()'s 3rd argument, $discrepancy is specified in units of 30s (so a discrepancy of 1 calculates codes for -1, 0, 1 timeslices e.g. your device's time and the server can be off +/- about 45 seconds in both directions on average or 1m30s worst-case). A $discrepancy of 2 should calculate for -2, -1, 0, 1, 2. However, the loop iterator $i is added by increments of 1. The code in the verifyCode() method is currently:

    $calculatedCode = $this->getCode($secret, $currentTimeSlice + $i);
    

    It should read:

    $calculatedCode = $this->getCode($secret, $currentTimeSlice + ($i * 30));
    

    getQRCodeGoogleUrl url encoding bug

    The method getQRCodeGoogleUrl() encodes the string incorrectly:

    $urlencoded = urlencode('otpauth://totp/'.$name.'?secret='.$secret.'');
    return 'https://chart.googleapis.com/chart?chs=200x200&chld=M|0&cht=qr&chl='.$urlencoded.'';
    

    Should read:

    $url = 'otpauth://totp/'.urlencode($name).'?secret='.urlencode($secret);
    return 'https://chart.googleapis.com/chart?chs=200x200&chld=M|0&cht=qr&chl='.urlencode($url);
    

    See KeyUriFormat for what should be encoded how. (Also: an Issuer value is "STRONGLY RECOMMENDED"' this should be easy to add/implement. I would also add support for the Algorithm, Digits, Counter and Period values which also should be easy to add/implement (even though the current Google Authenticator implementations still ignore some of them).

    createSecret uses non-cryptographically secure RNG

    Not exactly a bug but why not use a cryptographically secure RNG?

    public function createSecret($secretLength = 16)
    {
        $validChars = $this->_getBase32LookupTable();
        unset($validChars[32]);
    
        $secret = '';
        for ($i = 0; $i < $secretLength; $i++) {
            $secret .= $validChars[array_rand($validChars)];
        }
        return $secret;
    }
    

    Should/could be:

    public function CreateSecret($length = 16) {
        $validChars = $this->_getBase32LookupTable();
        $secret = '';
        $rnd = openssl_random_pseudo_bytes($length);
        for ($i = 0; $i < $length; $i++)
            $secret .= $validChars[ord($rnd[$i]) & 31];  //Mask out left 3 bits for 0-31 values
        return $secret;
    }
    

    The left 3 bits are masked out resulting in values ranging from 0-31. Because we require a nice even 5 bits we can simply mask them out. I'm no security expert but should we need values in a range of 0-100 we can't use a nice 'whole' number of bits which usually results in people using a modulo 100 operation intruducing a bias towards some numbers. This should not be the case in this code since we require an 'exact' amount of 5 bits and simply discard the rest.

    Also; there's no more need to unset the last element of the array (the padding char) since it is simply never referred to.

    Boobies!

    The getCode() method mentions a nipple in the comments; this should probably read nibble ;-)

    Others

    • I haven't gotten around to the _base32Decode() method but that looks overly complex too (which is just a gut-feel, I won't know until I have had time to examine it more closely).
    • The _base32Encode() method is never used and should be removed.
    • Furthermore I'd suggest a new method getQRCodeImage($name, $secret, ...) that returns a data-uri so can easily embed an image in a page:
    echo '<img src="' . $ga->getQRCodeImage('Test', $secret) . '">';
    

    This method would return a string like: data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAG8AAABvAQMAAADYCwwjAAAABlBMVEX///8AAABVwtN+AAABHklEQVQ4jdXTu7GGIBAF4OMQkEkDztAGmS1pA1dtQFsiow1mbEAzAse9i859RCzpz5h8BLB7ZIFPXIZoS7F1iiiKbKG2ZE9SMyrY72vfTWnnE2q4BXs4VckZGFBHqFXT/yIL5H5Xt/P3236BvBofTfgLs0DjadbdFDC+RxXZajSJbmcpyTTe3jo2nKSX2Xig70b+rUEmYGdHi+9+GiqRO81hgu4+igTUAa7fnl6mCZ3hJD0tQSa0PYNdCIOGyNblZBav3qrKBL+r/hkHVBDXFDhJzh8ijedbroaI3o6KzDvJHuCWZfKLnR0GnjXIzPPLd1FsdQXz/PKw013HJcUvdzXPOIjMSQZ19FEmV5UfwL5qmXl+OXkfx+eiMj9vfQNOb/aNopC1ZAAAAABJRU5ErkJggg==

    Example

    You could use PHPQRCode for this. The method would look something like this: (Refer to this post for more info)

    public function getQRCodeImage($name, $secret, $size = 3, $margin = 4, $level = 'L') {
        require_once 'phpqrcode.php';
    
        ob_start();
        QRCode::png($this->getQRText($name, $secret), null, constant('QR_ECLEVEL_'.$level), $size, $margin);
        $result = base64_encode(ob_get_contents());
        ob_end_clean();
        return 'data:image/png;base64,'.$result;
    }
    
    

    This method refers to getQRText() which I simple refactored out of getQRCodeGoogleUrl to it's own method so both getQRCodeGoogleUrl and getQRText can use it.

    private function getQRText($name, $secret) {
        return 'otpauth://totp/'.urlencode($name).'?secret='.urlencode($secret);
    }
    

    When the getQRCodeImage() would be implemented/added you'll gain the following benefits:

    • Solves Possible security problem
    • No more reliant on 3rd parties (granted, Google isn't down often but it CAN be; also see previous point: proxies/caches/whatevers could cause problems or even be an extra attack vector (MITM))
    • Saves at least one more HTTP request (the page is a bit bigger in terms of bytes but a roundtrip to a 3rd party is saved)
    • The image is harder to 'capture' for MITM's (not impossible) and not saved in browser caches, url histories etc.

    Hope this helps.

    opened by RobThree 3
  • Possible security problem

    Possible security problem

    While it's easier to use Google's chart service to render the QR code, it's easy to capture this URL and replay it (I think you mentioned in the Read Me). Is there not a more secure way to handle the QR code without firing off to Google?

    opened by NoodlesNZ 3
  • Can not verify the code generated by Google Authenticator app after deploying the code on the server

    Can not verify the code generated by Google Authenticator app after deploying the code on the server

    Hi,

    I have used this library to enable the 2FA in our app. It is working absolutely working fine on my local environment but when we deployed the code on staging it is not able to validate the verification code generated by Google Authenticator app. Both the environments are using Php 7.3. and according to the the issue https://github.com/PHPGangsta/GoogleAuthenticator/issues/61, the mbstring.func_overload value is already set to 0. The only deference I can see the default timezone. On server it is Europe/London where as in local it is UTC. I have also changed in my local system to Europe/London even it is working fine. Attached is the configuration in php.ini of server for your reference.

    Screen Shot 2021-11-01 at 10 50 07 PM

    I have also made the suggested changes in getCode function as mentioned in the https://github.com/PHPGangsta/GoogleAuthenticator/issues/61.

    // last arg: When set to TRUE, outputs raw binary data. FALSE outputs lowercase hexits.
    // hex string would be more stable in PHP I guess.
    $hm = hash_hmac('SHA1', $time, $secretkey, false);
    
    // last nibble is the last hex symbol now. Just turn it to decimal.
    $offset = hexdec(substr($hm, -1));
    
    // as each byte is 2 hex symbols, multiply 'substr' args by 2
    // turn resulting hex into binary for compliance with further code
    $hashpart = hex2bin(substr($hm, $offset * 2, 8));
    
    

    But still no luck. It would be a great help if anyone can help on this to resolve.

    Thanks and Regards Ashish

    opened by ashish-devclever 0
  • https://accounts.google.com/signin/v2/identifier?flowName=GlifWebSignIn&flowEntry=ServiceLogin

    https://accounts.google.com/signin/v2/identifier?flowName=GlifWebSignIn&flowEntry=ServiceLogin

    I'm just starting out but what got me even into this was because I lost my Google authenticator keys. I was told you could retrieve them somehow or over ride them. Can you help me out? If it's possible.

    https://accounts.google.com/signin/v2/challenge/totp?flowName=GlifWebSignIn&flowEntry=ServiceLogin&cid=4&navigationDirection=forward&TL=AM3QAYabIWSP8C_DV0huYZoWSS_cXrbfrMV9W_R0BIWcshVvlITBjRaoBsvko2Er

    opened by Gluda4649 2
  • Still maintained? Or forked with more pull requests/features?

    Still maintained? Or forked with more pull requests/features?

    Just wondering: I see the latest updates have been a while, and also several pull requests were made and issues raised.

    Is this still actively maintained? The author's Twitter account, website don't seem to be very recent either?

    opened by jbostoen 0
Releases(v1.0.1)
Rinvex Authy is a simple wrapper for @Authy TOTP API, the best rated Two-Factor Authentication service for consumers, simplest 2fa Rest API for developers and a strong authentication platform for the enterprise.

Rinvex Authy Rinvex Authy is a simple wrapper for Authy TOTP API, the best rated Two-Factor Authentication service for consumers, simplest 2fa Rest AP

Rinvex 34 Feb 14, 2022
Google Two-Factor Authentication Package for Laravel

Google2FA for Laravel Google Two-Factor Authentication Package for Laravel Google2FA is a PHP implementation of the Google Two-Factor Authentication M

Antonio Carlos Ribeiro 785 Dec 31, 2022
JWT Authenticator for symfony

HalloVerdenJwtAuthenticatorBundle This bundle provides a JWT authenticator for Symfony applications. It's using PHP JWT Framework for parsing and vali

Hallo Verden 0 Jul 8, 2022
PHP library for Two Factor Authentication (TFA / 2FA)

PHP library for Two Factor Authentication PHP library for two-factor (or multi-factor) authentication using TOTP and QR-codes. Inspired by, based on b

Rob Janssen 896 Dec 30, 2022
PHP library for Two Factor Authentication (TFA / 2FA)

PHP library for Two Factor Authentication PHP library for two-factor (or multi-factor) authentication using TOTP and QR-codes. Inspired by, based on b

Rob Janssen 896 Dec 30, 2022
PHP library to verify and validate Apple IdentityToken and authenticate a user with Apple ID.

Sign-in with Apple SDK Installation Recommended and easiest way to installing library is through Composer. composer require azimolabs/apple-sign-in-ph

Azimo Labs 79 Nov 8, 2022
Vendor-Agnostic Two-Factor Authentication

Multi-Factor Designed to be a vendor-agnostic implementation of various Two-Factor Authentication solutions. Developed by Paragon Initiative Enterpris

Paragon Initiative Enterprises 139 Dec 21, 2022
API stubs for developing a plugin that provides a 2FA authentication factor in JobRouter®.

Authentication Factor API JobRouter® is a scalable digitisation platform which links processes, data and documents. Starting with JobRouter® 5.2, a se

JobRouter 4 Nov 4, 2021
This repository includes a sample project to illustrate the usage of the JobRouter® Authentication Factor API.

JR 2FA Example Plugin This repository includes a sample project to illustrate the usage of the JobRouter® Authentication Factor API. It can be used as

JobRouter 4 Sep 10, 2021
Redirects any user which hasn't setup two factor authentication yet to /2fa/

force-two-factor Redirects any user which hasn't setup two factor authentication yet to /2fa/. Use together with the forked two-factor plugin at https

Aiwos 0 Dec 24, 2021
Secure WordPress login with two factor authentication

This plugin allows you to secure your WordPress login with two factor authentication. The users will have to enter a one time password every time they log in.

Volodymyr Kolesnykov 6 Nov 2, 2022
A simple two factor authentication for laravel applications

Laravel 2fa A simple two factor authentication for laravel applications. Installation Require via composer Update database Replace authentication trai

Rezkonline 1 Feb 9, 2022
Laravel Two-Factor Authentication

This package allow you to enable two-factor authentication in your Laravel applications very easily, without the need to add middleware or any modification to your routes. It stores tokens in your database in a distinct table, so you don't need to alter your users table. Notify users about their token via mail, SMS or any custom channel.

null 7 Jun 24, 2022
Multi-factor Authentication using a Public PGP key for web based applications

PGPmfa() a PHP Class for PGP Multi-factor Authentication using a Public PGP key for web based applications Multi-factor Authentication with PGP Second

null 2 Nov 27, 2022
Two-Factor Authentication for all your users out-of-the-box.

Two Factor On-premises Two-Factor Authentication for all your users out of the box. use Illuminate\Support\Facades\Auth; use Laragear\TwoFactor\TwoFac

Laragear 105 Dec 22, 2022
It's a Laravel 8 authentication markdown that will help you to understand and grasp all the underlying functionality for Session and API Authentication

About Auth Starter It's a Laravel 8 authentication markdown that will help you to understand and grasp all the underlying functionality for Session an

Sami Alateya 10 Aug 3, 2022
Social OAuth Authentication for Laravel 5. drivers: facebook, github, google, linkedin, weibo, qq, wechat and douban

Social OAuth Authentication for Laravel 5. drivers: facebook, github, google, linkedin, weibo, qq, wechat and douban

安正超 330 Nov 14, 2022
phpCAS is an authentication library that allows PHP applications to easily authenticate users via a Central Authentication Service (CAS) server.

phpCAS is an authentication library that allows PHP applications to easily authenticate users via a Central Authentication Service (CAS) server.

Apereo Foundation 780 Dec 24, 2022
Simple PHP Google Authentication Template

php-google-auth A php google authentication page project View Demo · Report Problems About The Project This is a small and easy project that I made to

Antonio 4 Nov 21, 2021