1![image](http://lingtalfi.com/services/pngtext?color=cc0000&size=50&text=WARNING) 2 3<p style="font-color: red;"> 4 Google API for QR generator is turned off. 5</p> 6 7All version of that package prior to 5.0.0 are deprecated. Please, make composer update and check documentation regarding [QRCode generation](https://github.com/antonioribeiro/google2fa#generating-qrcodes). 8 9# Google2FA 10 11<p align="center"> 12 <a href="https://packagist.org/packages/pragmarx/google2fa"><img alt="Latest Stable Version" src="https://img.shields.io/packagist/v/pragmarx/google2fa.svg?style=flat-square"></a> 13 <a href="LICENSE.md"><img alt="License" src="https://img.shields.io/badge/license-MIT-brightgreen.svg?style=flat-square"></a> 14 <a href="https://scrutinizer-ci.com/g/antonioribeiro/google2fa/?branch=master"><img alt="Code Quality" src="https://img.shields.io/scrutinizer/g/antonioribeiro/google2fa.svg?style=flat-square"></a> 15 <a href="https://travis-ci.org/antonioribeiro/google2fa"><img alt="Build" src="https://img.shields.io/travis/antonioribeiro/google2fa.svg?style=flat-square"></a> 16</p> 17<p align="center"> 18 <a href="https://packagist.org/packages/pragmarx/google2fa"><img alt="Downloads" src="https://img.shields.io/packagist/dt/pragmarx/google2fa.svg?style=flat-square"></a> 19 <a href="https://scrutinizer-ci.com/g/antonioribeiro/google2fa/?branch=master"><img alt="Coverage" src="https://img.shields.io/scrutinizer/coverage/g/antonioribeiro/google2fa.svg?style=flat-square"></a> 20 <a href="https://styleci.io/repos/24296182"><img alt="StyleCI" src="https://styleci.io/repos/24296182/shield"></a> 21 <a href="https://travis-ci.org/antonioribeiro/google2fa"><img alt="PHP" src="https://img.shields.io/badge/PHP-5.4%20--%207.2-brightgreen.svg?style=flat-square"></a> 22</p> 23 24### Google Two-Factor Authentication for PHP Package 25 26Google2FA is a PHP implementation of the Google Two-Factor Authentication Module, supporting the HMAC-Based One-time Password (HOTP) algorithm specified in [RFC 4226](https://tools.ietf.org/html/rfc4226) and the Time-based One-time Password (TOTP) algorithm specified in [RFC 6238](https://tools.ietf.org/html/rfc6238). 27 28This package is agnostic, but there's a [Laravel bridge](https://github.com/antonioribeiro/google2fa-laravel). 29 30## Demos, Example & Playground 31 32Please check the [Google2FA Package Playground](http://pragmarx.com/playground/google2fa). 33 34![playground](docs/playground.jpg) 35 36Here's an demo app showing how to use Google2FA: [google2fa-example](https://github.com/antonioribeiro/google2fa-example). 37 38You can scan the QR code on [this (old) demo page](https://antoniocarlosribeiro.com/technology/google2fa) with a Google Authenticator app and view the code changing (almost) in real time. 39 40## Requirements 41 42- PHP 5.4+ 43 44## Installing 45 46Use Composer to install it: 47 48 composer require pragmarx/google2fa 49 50To generate inline QRCodes, you'll need to install a QR code generator, e.g. [BaconQrCode](https://github.com/Bacon/BaconQrCode): 51 52 composer require bacon/bacon-qr-code 53 54## Using It 55 56### Instantiate it directly 57 58```php 59use PragmaRX\Google2FA\Google2FA; 60 61$google2fa = new Google2FA(); 62 63return $google2fa->generateSecretKey(); 64``` 65 66## How To Generate And Use Two Factor Authentication 67 68Generate a secret key for your user and save it: 69 70```php 71$user->google2fa_secret = $google2fa->generateSecretKey(); 72``` 73 74## Generating QRCodes 75 76The securer way of creating QRCode is to do it yourself or using a library. First you have to install a QR code generator e.g. BaconQrCode, as stated above, then you just have to generate the QR code url using: 77 78```php 79$qrCodeUrl = $google2fa->getQRCodeUrl( 80 $companyName, 81 $companyEmail, 82 $secretKey 83); 84``` 85 86Once you have the QR code url, you can feed it to your preferred QR code generator. 87 88```php 89// Use your own QR Code generator to generate a data URL: 90google2fa_url = custom_generate_qrcode_url($qrCodeUrl); 91 92/// and in your view: 93 94<img src="{{ $google2fa_url }}" alt=""> 95``` 96 97And to verify, you just have to: 98 99```php 100$secret = $request->input('secret'); 101 102$valid = $google2fa->verifyKey($user->google2fa_secret, $secret); 103``` 104 105## QR Code Packages 106 107This package suggests the use of [Bacon/QRCode](https://github.com/Bacon/BaconQrCode) because it is known as a good QR Code package, but you can use it with any other package, for instance [Simple QrCode](https://www.simplesoftware.io/docs/simple-qrcode) or [Endroid QR Code](https://github.com/endroid/qr-code), which both use [Bacon/QRCode](https://github.com/Bacon/BaconQrCode) to produce QR Codes. 108 109Usually you'll need a 2FA URL, so you just have to use the URL generator: 110 111```php 112$google2fa->getQRCodeUrl($companyName, $companyEmail, $secretKey) 113``` 114 115#### Here's an example using Simple QrCode: 116 117```php 118<div class="visible-print text-center"> 119 {!! QrCode::size(100)->generate($google2fa->getQRCodeUrl($companyName, $companyEmail, $secretKey)); !!} 120 <p>Scan me to return to the original page.</p> 121</div> 122``` 123 124#### Endroid QR Code Generator 125 126Generate the data URL 127 128```php 129 130$qrCode = new \Endroid\QrCode\QrCode($value); 131$qrCode->setSize(100); 132$google2fa_url = $qrCode->writeDataUri(); 133``` 134 135And in your view 136 137```php 138<div class="visible-print text-center"> 139 {!! $google2fa_url !!} 140 <p>Scan me to return to the original page.</p> 141</div> 142``` 143 144#### BaconQRCode directly 145 146```php 147<?php 148 149use PragmaRX\Google2FA\Google2FA; 150use BaconQrCode\Renderer\ImageRenderer; 151use BaconQrCode\Renderer\Image\ImagickImageBackEnd; 152use BaconQrCode\Renderer\RendererStyle\RendererStyle; 153use BaconQrCode\Writer; 154 155$google2fa = app(Google2FA::class); 156 157$g2faUrl = $google2fa->getQRCodeUrl( 158 'pragmarx', 159 'google2fa@pragmarx.com', 160 $google2fa->generateSecretKey() 161); 162 163$writer = new Writer( 164 new ImageRenderer( 165 new RendererStyle(400), 166 new ImagickImageBackEnd() 167 ) 168); 169 170$qrcode_image = base64_encode($writer->writeString($g2faUrl)); 171``` 172 173And show it as an image: 174 175```php 176<img src="data:image/png;base64, <?php echo $qrcode_image; ?> "/> 177``` 178 179## Server Time 180 181It's really important that you keep your server time in sync with some NTP server, on Ubuntu you can add this to the crontab: 182 183```bash 184sudo service ntp stop 185sudo ntpd -gq 186sudo service ntp start 187``` 188 189## Validation Window 190 191To avoid problems with clocks that are slightly out of sync, we do not check against the current key only but also consider `$window` keys each from the past and future. You can pass `$window` as optional third parameter to `verifyKey`, it defaults to `4`. A new key is generated every 30 seconds, so this window includes keys from the previous two and next two minutes. 192 193```php 194$secret = $request->input('secret'); 195 196$window = 8; // 8 keys (respectively 4 minutes) past and future 197 198$valid = $google2fa->verifyKey($user->google2fa_secret, $secret, $window); 199``` 200 201An attacker might be able to watch the user entering his credentials and one time key. 202Without further precautions, the key remains valid until it is no longer within the window of the server time. In order to prevent usage of a one time key that has already been used, you can utilize the `verifyKeyNewer` function. 203 204```php 205$secret = $request->input('secret'); 206 207$timestamp = $google2fa->verifyKeyNewer($user->google2fa_secret, $secret, $user->google2fa_ts); 208 209if ($timestamp !== false) { 210 $user->update(['google2fa_ts' => $timestamp]); 211 // successful 212} else { 213 // failed 214} 215``` 216 217Note that `$timestamp` either `false` (if the key is invalid or has been used before) or the provided key's unix timestamp divided by the key regeneration period of 30 seconds. 218 219## Using a Bigger and Prefixing the Secret Key 220 221Although the probability of collision of a 16 bytes (128 bits) random string is very low, you can harden it by: 222 223#### Use a bigger key 224 225```php 226$secretKey = $google2fa->generateSecretKey(32); // defaults to 16 bytes 227``` 228 229#### You can prefix your secret keys 230 231You may prefix your secret keys, but you have to understand that, as your secret key must have length in power of 2, your prefix will have to have a complementary size. So if your key is 16 bytes long, if you add a prefix it must be also 16 bytes long, but as your prefixes will be converted to base 32, the max length of your prefix is 10 bytes. So, those are the sizes you can use in your prefixes: 232 233``` 2341, 2, 5, 10, 20, 40, 80... 235``` 236 237And it can be used like so: 238 239```php 240$prefix = strpad($userId, 10, 'X'); 241 242$secretKey = $google2fa->generateSecretKey(16, $prefix); 243``` 244 245#### Window 246 247The Window property defines how long a OTP will work, or how many cycles it will last. A key has a 30 seconds cycle, setting the window to 0 will make the key lasts for those 30 seconds, setting it to 2 will make it last for 120 seconds. This is how you set the window: 248 249```php 250$secretKey = $google2fa->setWindow(4); 251``` 252 253But you can also set the window while checking the key. If you need to set a window of 4 during key verification, this is how you do: 254 255```php 256$isValid = $google2fa->verifyKey($seed, $key, 4); 257``` 258 259#### Key Regeneration Interval 260 261You can change key regeneration interval, which defaults to 30 seconds, but remember that this is a default value on most authentication apps, like Google Authenticator, which will, basically, make your app out of sync with them. 262 263```php 264$google2fa->setKeyRegeneration(40); 265``` 266 267## Google Authenticator secret key compatibility 268 269To be compatible with Google Authenticator, your (converted to base 32) secret key length must be at least 8 chars and be a power of 2: 8, 16, 32, 64... 270 271So, to prevent errors, you can do something like this while generating it: 272 273```php 274$secretKey = '123456789'; 275 276$secretKey = str_pad($secretKey, pow(2,ceil(log(strlen($secretKey),2))), 'X'); 277``` 278 279And it will generate 280 281``` 282123456789XXXXXXX 283``` 284 285By default, this package will enforce compatibility, but, if Google Authenticator is not a target, you can disable it by doing 286 287```php 288$google2fa->setEnforceGoogleAuthenticatorCompatibility(false); 289``` 290 291## Google Authenticator Apps: 292 293To use the two factor authentication, your user will have to install a Google Authenticator compatible app, those are some of the currently available: 294 295* [Authy for iOS, Android, Chrome, OS X](https://www.authy.com/) 296* [FreeOTP for iOS, Android and Pebble](https://apps.getpebble.com/en_US/application/52f1a4c3c4117252f9000bb8) 297* [Google Authenticator for iOS](https://itunes.apple.com/us/app/google-authenticator/id388497605?mt=8) 298* [Google Authenticator for Android](https://play.google.com/store/apps/details?id=com.google.android.apps.authenticator2) 299* [Google Authenticator (port) on Windows Store](https://www.microsoft.com/en-us/store/p/google-authenticator/9wzdncrdnkrf) 300* [Microsoft Authenticator for Windows Phone](https://www.microsoft.com/en-us/store/apps/authenticator/9wzdncrfj3rj) 301* [LastPass Authenticator for iOS, Android, OS X, Windows](https://lastpass.com/auth/) 302* [1Password for iOS, Android, OS X, Windows](https://1password.com) 303 304## Tests 305 306The package tests were written with [phpspec](http://www.phpspec.net/en/latest/). 307 308## Authors 309 310- [Antonio Carlos Ribeiro](http://twitter.com/iantonioribeiro) 311- [Phil (Orginal author of this class)](https://www.idontplaydarts.com/static/ga.php_.txt) 312- [All Contributors](https://github.com/antonioribeiro/google2fa/graphs/contributors) 313 314## License 315 316Google2FA is licensed under the MIT License - see the [LICENSE](LICENSE.md) file for details. 317 318## Contributing 319 320Pull requests and issues are more than welcome. 321