Small OTP library
Find a file
2025-06-16 16:48:59 +02:00
.forgejo/workflows various code cleanups, add CI, update psaml.xml.dist 2025-01-27 13:17:03 +01:00
example source formatting, require PHP >= 8.2 2025-04-02 08:22:43 +02:00
src require PHP >= 8.3, enable vimeo/psalm for unit tests 2025-06-16 16:48:59 +02:00
tests require PHP >= 8.3, enable vimeo/psalm for unit tests 2025-06-16 16:48:59 +02:00
.gitignore pull fkooman/otp-verifier in the 21st century 2022-01-27 17:23:38 +01:00
.php-cs-fixer.dist.php source formatting, require PHP >= 8.2 2025-04-02 08:22:43 +02:00
CHANGES.md prepare for release 2025-04-02 12:27:31 +02:00
composer.json require PHP >= 8.3, enable vimeo/psalm for unit tests 2025-06-16 16:48:59 +02:00
LICENSE initial commit 2018-07-09 20:02:54 +02:00
Makefile various code cleanups, add CI, update psaml.xml.dist 2025-01-27 13:17:03 +01:00
phpstan.neon fix phpstan.neon 2025-04-02 09:55:59 +02:00
phpunit.xml.dist pull fkooman/otp-verifier in the 21st century 2022-01-27 17:23:38 +01:00
psalm.xml.dist require PHP >= 8.3, enable vimeo/psalm for unit tests 2025-06-16 16:48:59 +02:00
README.md update README 2025-04-02 12:25:13 +02:00

Summary: Small OTP verification library

Description: PHP library to verify OTP codes and protect against replay and brute force attacks.

License: MIT

Introduction

PHP library to verify OTP codes and protect against replay and brute force attacks with support for PHP >= 8.2.

What

This is a library that includes TOTP verification and protection against replay and brute force attacks.

Why

High quality OTP verification libraries exist, e.g. christian-riesen/otp and spomky-labs/otphp which are popular and work well, however, they lack built-in support for protection against replay attacks and brute force attempts.

Features

  • Supports PHP >= 8.2;
  • Verifies TOTP codes;
  • Protects againts replay attacks by storing the (used) OTP keys;
  • Protects against brute force attacks by limiting the number of attempts in a certain time frame.

Database

A database is needed to store OTP secrets and used OTP keys. Currently only SQLite is tested, but others may work.

<?php 

$storage = new fkooman\OTP\DBStorage(new PDO('sqlite:/path/to/db.sqlite'));

You can call init() on the Storage object to initialize the database, do this only once, during application installation:

<?php

$storage->init();

API

Registration

A TOTP application, e.g. running on a mobile device will need to be configured with the TOTP secret and generate a valid OTP key before registration can succeed. Your application can for example generate a TOTP secret, generate a QR code and allow the user to import that in their OTP application.

Most (T)OTP applications can handle a QR code for enrollment, making it much easier for users to configure their application. This library can generate a Key-URI to make this easier with all the right parameters set:

<?php

$userId = 'foo';
$otpInfo = fkooman\OTP\OTPInfo::create();
$keyUri = KeyURI::generate($userId, 'My Service', $otpInfo);

This will return something like this:

otpauth://totp/My%20Service:foo?secret=RMO6LYUJPDMRZTCA5JKP4TOLRYKHBF2H&algorithm=SHA1&digits=6&period=30&issuer=My%2520Service

Use this URI in a QR code and show it to the user. Once you get back a valid OTP key from the user, e.g. entered in a form, you can complete the registration:

$otp = new OTP($storage);
$otpKey = '123456';
$otp->register($userId, $otpInfo, $otpKey);

Validation

$userId = 'foo';
$otpKey = '234567';
try {
    $otp->validate($userId, $otpKey);
    echo 'VALID';
} catch (fkooman\OTP\Exception\OTPException $e) {
    echo 'ERROR: '.$e::class.PHP_EOL;
}

You can catch the specific OTP Exception for a particular case, e.g. you can catch:

Exception When?
InvalidOTPException The user provided an invalid OTP code
ReplayOTPException An OTP code was reused, i.e. "replayed"
TooManyAttemptsOTPException Too many (invalid) attempts were made

"Too many" in this case means, 10 invalid OTP codes in a row.

NOTE: you can not reuse the OTP key used for registration for verification afterwards. You have to wait for the next window, i.e. up to 30 seconds by default.

Inspiration

This library was inspired by other software, both in idea and sometimes code. A list:

License

MIT.