Skip to content

Latest commit

 

History

History
86 lines (58 loc) · 2.37 KB

QA.md

File metadata and controls

86 lines (58 loc) · 2.37 KB

Questions and Answers

What should I store in my server database?

The Situation

To verify the OTP you received, you have to create an OTP object with the mandatory parameters that are namely:

  • The secret,
  • The digest,
  • The digits,
  • The period (TOTP only) or the current counter (HOTP only).

In general, the digest and digits are the same for all account and might not change. Same goes for the period. The secret, the current counter and the optional label depends on the user account.

<?php

use OTPHP\TOTP;

$digits = 6;
$digest = 'sha1';
$period = 30;

$totp = TOTP::createFromSecret($user->getOtpSecret());
$totp->setPeriod($period);
$totp->setDigest($digest);
$totp->setDigits($digits);
$totp->setLabel($user->getEmail());

$totp->verify($_POST['otp']);

The Problem

If you use TOTP, storing the secret should be enough. However if you store only that information, you will have troubles if your application security policy evolves:

  • You decide to increase the number of digits from 6 to 8.
  • You decide to change the hashing algorithm from 'sha1' to 'sha256'.
  • You decide to change the period from 30 to 20.
  • You need to use custom parameters you set during the application configuration.

None of your end-user will be able to use the OTP generated by their application.

The Solution

Another approach could be to store the entire provisioning Uri. As the provisioning Uri contains all parameters including the custom ones, then only the new provisioning will be affected by your new security policy. Old provisioning Uris may be updated step by step (e.g. when the end-user is logged in).

<php

use OTPHP\Factory;

$otp = Factory::loadFromProvisioningUri(
    $user->getProvisioningUri()
);

$otp->verify($_POST['otp']);

Note: if your OTP is an HOTP, then you have to store the provisioning Uri again as the current counter changed.

Sometimes my TOTP lifetime is not exactly 30 seconds (period I defined)

A TOTP ALWAYS lives for the period you defined.

If you try the following code lines, you may see 2 different OTPs.

<?php
use OTPHP\TOTP;

$totp = TOTP::generate(10); // TOTP with an 10 seconds period

for ($i = 0; $i < 10; $i++) {
    echo 'Current OTP is: '. $totp->now();
    sleep(1);
}

The reason is that the OTP lifetime does not start on your first request ($totp->now()) but may have started few seconds ago.