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