chillerlan / php-authenticator
A generator for counter- and time based 2-factor authentication codes (Google Authenticator). PHP 8.2+
                                    Fund package maintenance!
                                                                            
                                                                                                                                        Ko-Fi
                                                                                    
                                                                
Installs: 99 842
Dependents: 2
Suggesters: 4
Security: 0
Stars: 54
Watchers: 2
Forks: 2
Open Issues: 0
pkg:composer/chillerlan/php-authenticator
Requires
- php: ^8.2
- chillerlan/php-settings-container: ^3.2.1
- paragonie/constant_time_encoding: ^3.0
Requires (Dev)
- ext-curl: *
- ext-json: *
- ext-sodium: *
- phpmd/phpmd: ^2.15
- phpstan/phpstan: ^1.11
- phpstan/phpstan-deprecation-rules: ^1.2
- phpunit/phpunit: ^11.2
- squizlabs/php_codesniffer: ^3.10
Suggests
- chillerlan/php-qrcode: Create QR Codes for use with an authenticator app.
README
A generator for counter based (RFC 4226) and time based (RFC 6238) one time passwords (OTP). (a.k.a. Yet Another Google Authenticator Implementation!)
Documentation
Requirements
- PHP 8.2+
- ext-curlfor Steam Guard server time synchronization
- ext-sodiumfor constant time implementations of base64 encode/decode and hex2bin/bin2hex (- paragonie/constant_time_encodingis used as fallback)
 
Installation
requires composer
via terminal: composer require chillerlan/php-authenticator
composer.json
{
	"require": {
		"php": "^8.2",
		"chillerlan/php-authenticator": "dev-main"
	}
}
Note: replace dev-main with a version constraint, e.g. ^5.0 - see releases for valid versions
Profit!
Usage
Create a secret
The secret is usually being created once during the activation process in a user control panel. So all you need to do there is to display it to the user in a convenient way - as a text string and QR code for example - and save it somewhere with the user data.
use chillerlan\Authenticator\{Authenticator, AuthenticatorOptions}; $options = new AuthenticatorOptions; $options->secret_length = 32; $authenticator = new Authenticator($options); // create a secret (stored somewhere in a *safe* place on the server. safe... hahaha jk) $secret = $authenticator->createSecret(); // you can also specify the length of the secret key, which overrides the options setting $secret = $authenticator->createSecret(20); // set an existing secret $authenticator->setSecret($secret);
A secret created with Authenticator::createSecret() will also be stored internally,
so that you don't need to provide the secret you just created on follow-up operations with the current instance.
Verify a one time code
Now during the login process - after the user has successfully entered their credentials - you would ask them for a one time code to check it against the secret from your user database.
// verify the code if($authenticator->verify($otp)){ // that's it - 2FA has never been easier! :D }
time based (TOTP)
Verify adjacent codes
// try the first adjacent $authenticator->verify($otp, time() - $options->period); // -> true // try the second adjacent, default is 1 $authenticator->verify($otp, time() + 2 * $options->period); // -> false // allow 2 adjacent codes $options->adjacent = 2; $authenticator->verify($otp, time() + 2 * $options->period); // -> true
counter based (HOTP)
// switch mode to HOTP $options->mode = AuthenticatorInterface::HOTP; // user sends the OTP for code #42, which is equivalent to $otp = $authenticator->code(42); // -> 123456 // verify [123456, 42] $authenticator->verify($otp, $counterValueFromUserDatabase) // -> true
URI creation
In order to display a QR code for a mobile authenticator you'll need an otpauth:// URI, which can be created using the following method.
- $labelshould be something that identifies the account to which the secret belongs
- $issueris the name of your website or company for example, so that the user is able to identify multiple accounts.
$uri = $authenticator->getUri($label, $issuer); // -> otpauth://totp/my%20label?secret=NKSOQG7UKKID4IXW&issuer=chillerlan.net&digits=6&period=30&algorithm=SHA1
Notes
Keep in mind that several URI settings are not (yet) recognized by all authenticators. Check the Google Authenticator wiki for more info.
// code length, currently 6 or 8 $options->digits = 8; // valid period between 15 and 60 seconds $options->period = 45; // set the HMAC hash algorithm $options->algorithm = AuthenticatorInterface::ALGO_SHA512;
API
Authenticator
| method | return | description | 
|---|---|---|
| __construct(SettingsContainerInterface $options = null, string $secret = null) | - | |
| setOptions(SettingsContainerInterface $options) | Authenticator | called internally by __construct() | 
| setSecret(string $secret) | Authenticator | called internally by __construct() | 
| getSecret() | string | |
| createSecret(int $length = null) | string | $lengthoverridesAuthenticatorOptionssetting | 
| code(int $data = null) | string | $datamay be a UNIX timestamp (TOTP) or a counter value (HOTP) | 
| verify(string $otp, int $data = null) | bool | for $dataseeAuthenticator::code() | 
| getUri(string $label, string $issuer, int $hotpCounter = null, bool $omitSettings = null) | string | 
AuthenticatorOptions
Properties
| property | type | default | allowed | description | 
|---|---|---|---|---|
| $digits | int | 6 | 6 or 8 | auth code length | 
| $period | int | 30 | 15 - 60 | validation period (seconds) | 
| $secret_length | int | 20 | >= 16 | length of the secret phrase (bytes, unencoded binary) | 
| $algorithm | string | SHA1 | SHA1,SHA256orSHA512 | HMAC hash algorithm, see AuthenticatorInterface::HASH_ALGOS | 
| $mode | string | totp | totp,hotp,battlenetorsteam | authenticator mode: time- or counter based, see AuthenticatorInterface::MODES | 
| $adjacent | int | 1 | >= 0 | number of allowed adjacent codes | 
| $time_offset | int | 0 | * | fixed time offset that will be added to the current time value | 
| $useLocalTime | bool | true | * | whether to use local time or request server time | 
| $forceTimeRefresh | bool | false | * | whether to force refreshing server time on each call | 
AuthenticatorInterface
Methods
| method | return | description | 
|---|---|---|
| setOptions(SettingsContainerInterface $options) | AuthenticatorInterface | |
| setSecret(string $encodedSecret) | AuthenticatorInterface | |
| getSecret() | string | |
| createSecret(int $length = null) | string | |
| getServertime() | int | |
| getCounter(int $data = null) | int | internal | 
| getHMAC(int $counter) | string | internal | 
| getCode(string $hmac) | int | internal | 
| getOTP(int $code) | string | internal | 
| code(int $data = null) | string | |
| verify(string $otp, int $data = null) | bool | 
Constants
| constant | type | description | 
|---|---|---|
| TOTP | string | |
| HOTP | string | |
| STEAM_GUARD | string | |
| ALGO_SHA1 | string | |
| ALGO_SHA256 | string | |
| ALGO_SHA512 | string | |
| MODES | array | map of mode -> classname | 
| HASH_ALGOS | array | list of available hash algorithms | 
