To generate one-time passwords, each class needs at least the following parameters:
- A secret encoded in Base32
- A digest algorithm
- A number of digits
Depending on the type of OTP, you will need the following additional parameters:
- For TOTP: a period (and optionally an epoch)
- For HOTP: a counter
By default, a 512 bits secret is generated. If you need, you can use your own secret:
<?php
use OTPHP\TOTP;
use ParagonIE\ConstantTime\Base32;
$mySecret = trim(Base32::encodeUpper(random_bytes(128)), '='); // We generate our own 1024 bits secret
$otp = TOTP::create($mySecret);
Please note that the trailing =
are automatically removed by the library.
By default, the period for a TOTP is 30 seconds and the counter for a HOTP is 0.
<?php
use OTPHP\TOTP;
use OTPHP\HOTP;
$otp = TOTP::create(
null, // Let the secret be defined by the class
10 // The period is now 10 seconds
);
$otp = HOTP::create(
null, // Let the secret be defined by the class
1000 // The counter is now 1000. We recommend you start at `0`, but you can set any value (at least 0)
);
By default the digest algorithm is sha1
.
You can use any algorithm listed by hash_algos()
.
Note that most applications only support md5
, sha1
, sha256
and sha512
.
You must verify that the algorithm you want to use is supported by the application your clients might be using.
SHA-2
algorithms are recommended.
<?php
use OTPHP\TOTP;
$totp = TOTP::create(
null, // Let the secret be defined by the class
30, // The period (30 seconds)
'ripemd160' // The digest algorithm
);
By default the number of digits is 6. You can decide to use more (or less) digits. More than 10 may be difficult to use by the owner.
<?php
use OTPHP\TOTP;
$totp = TOTP::create(
null, // Let the secret be defined by the class
30, // The period (30 seconds)
'sha1', // The digest algorithm
8 // The output will generate 8 digits
);
By default, the epoch for a TOTP is 0
.
The epoch is equivalent to the T0
parameter in RFC 6238.
This parameter basically determines at which timestamp (epoch) to start counting. It is useful in scenarios where
you need an exact period to verify passwords in. The epoch can be shared by client and server to specify the exact
timestamp at which the password was created so that you can reuse it for exact verification.
CAUTION: If you follow this approach and share the epoch as password creation timestamp, you should use dynamic secrets that are different each time, otherwise you will most likely always produce the same passwords. You could for example encode the timestamp in the secret to make it different each time.
<?php
use OTPHP\TOTP;
// Without epoch
$otp = TOTP::create(
null, // Let the secret be defined by the class
5, // The period (5 seconds)
'sha1', // The digest algorithm
6 // The output will generate 6 digits
);
$password = $otp->at(1519401289); // Current period is: 1519401285 - 1519401289
$otp->verify($password, 1519401289); // Second 1: true
$otp->verify($password, 1519401290); // Second 2: false
// With epoch
$otp = TOTP::create(
null, // Let the secret be defined by the class
5, // The period (5 seconds)
'sha1', // The digest algorithm
6, // The output will generate 6 digits
1519401289 // The epoch is now 02/23/2018 @ 3:54:49pm (UTC)
);
$password = $otp->at(1519401289); // Current period is: 1519401289 - 1519401293
$otp->verify($password, 1519401289); // Second 1: true
$otp->verify($password, 1519401290); // Second 2: true
$otp->verify($password, 1519401291); // Second 3: true
$otp->verify($password, 1519401292); // Second 4: true
$otp->verify($password, 1519401293); // Second 5: true
$otp->verify($password, 1519401294); // Second 6: false
OTP objects are able to support custom parameters.
These parameters are available in the provisioning URI or from the method getParameter
.
<?php
use OTPHP\TOTP;
$totp = TOTP::create('JBSWY3DPEHPK3PXP'); // New TOTP
$totp->setLabel('[email protected]'); // The label
$totp->setParameter('foo', 'bar');
$totp->getProvisioningUri(); // Will return otpauth://totp/alice%40google.com?secret=JBSWY3DPEHPK3PXP&foo=bar
As a user may have multiple OTP using the same label (e.g. the user email), it is useful to set the issuer parameter to identify the service that provided the OTP.
<?php
use OTPHP\TOTP;
$totp = TOTP::create('JBSWY3DPEHPK3PXP'); // New TOTP with custom secret
$totp->setLabel('[email protected]'); // The label (string)
$totp->setIssuer('My Service');
By default and to be compatible with Google Authenticator, the issuer is set in the query parameters and as the label prefix.
<?php
echo $totp->getProvisioningUri(); // Will return otpauth://totp/My%20Service%3Aalice%40google.com?issuer=My%20Service&secret=JBSWY3DPEHPK3PXP
If you do not want to get the issuer as a query parameter, you can remove it by using the method setIssuerIncludedAsParameter(bool)
.
<?php
$totp->setIssuerIncludedAsParameter(false);
echo $totp->getProvisioningUri(); // Will return otpauth://totp/My%20Service%3Aalice%40google.com?secret=JBSWY3DPEHPK3PXP
Some applications such as FreeOTP can load images from an URI (image
parameter).
Please note that at the moment, we cannot list applications that support this parameter.
<?php
use OTPHP\TOTP;
$totp = TOTP::create('JBSWY3DPEHPK3PXP'); // New TOTP with custom secret
$totp->setLabel('[email protected]'); // The label (string)
$totp->setParameter('image', 'https://foo.bar/otp.png');
$totp->getProvisioningUri(); // Will return otpauth://totp/alice%40google.com?secret=JBSWY3DPEHPK3PXP&image=https%3A%2F%2Ffoo.bar%2Fotp.png
When you load a QR Code using this input data, a compatible application will try to load the image at https://foo.bar/otp.png
.