PHP Classes

How to Use a PHP OTP Verification Class to Generate a Token that Is Only Valid One Time Using the Package OTP: Generate token to be verified during a time window

Recommend this page to a friend!
  Info   Documentation   View files Files   Install with Composer Install with Composer   Download Download   Reputation   Support forum   Blog    
Last Updated Ratings Unique User Downloads Download Rankings
2026-06-15 (20 days ago) RSS 2.0 feedNot yet rated by the usersTotal: Not yet counted Not yet ranked
Version License PHP version Categories
otp 5.02.2MIT/X Consortium ...8.4Cryptography, Security, PHP 8
Description 

Author

This package can generate a token to be verified during a time window.

It provides a class that can generate a secret and a token that can be verified later.

The class allows configuring several details about the generation and verification of the token, like the following:

- The time range that the token can be considered valid

- Storage to determine where the token verification information is stored

- An identifier to be associated with the generated token

Picture of A. B. M. Mahmudul Hasan
  Performance   Level  
Name: A. B. M. Mahmudul Hasan <contact>
Classes: 5 packages by
Country: Bangladesh Bangladesh
Innovation award
Innovation award
Nominee: 2x

Instructions

Please read this document to learn how to generate a one-time password (OTP) token and verify if it is valid.

Documentation

OTP

Security & Standards Packagist Downloads License: MIT Packagist Version Packagist PHP Version GitHub Code Size Documentation

Standalone OTP and MFA primitives for PHP.

Supports:

  • Generic OTP with PSR-6 storage
  • TOTP (RFC6238)
  • HOTP (RFC4226)
  • OCRA (RFC6287)
  • Recovery / backup codes
  • `otpauth://` generation and parsing
  • Replay-protection contracts and in-memory stores

Requirements

  • PHP 8.4+

Version Compatibility

| OTP Library Line | PHP Requirement | | --- | --- | | 5.x | >=8.4 | | 4.x | >=8.2 | | 3.x | >=8.2 | | 2.x | >=8.0 | | 1.x | >=7.1 |

Use a matching major line when your runtime is pinned to an older PHP version.

Installation

composer require infocyph/otp

Highlights

  • Base32 secret generation, normalization, and validation
  • Safer provisioning URI and label handling
  • SVG QR rendering plus raw payload/URI access
  • Rich verification results where needed, simple bool APIs where preferred
  • Configurable TOTP drift windows
  • HOTP look-ahead resynchronization
  • Replay protection contracts for TOTP, HOTP, and OCRA
  • One-time recovery codes with hashed storage

Quick Start

TOTP

<?php
use Infocyph\OTP\TOTP;

$secret = TOTP::generateSecret();

$totp = (new TOTP($secret))
    ->setAlgorithm('sha256');

$otp = $totp->getOTP();

$isValid = $totp->verify($otp);

Advanced verification with drift windows:

<?php
use Infocyph\OTP\Stores\InMemoryReplayStore;
use Infocyph\OTP\ValueObjects\VerificationWindow;

$store = new InMemoryReplayStore();

$result = $totp->verifyWithWindow(
    $otp,
    timestamp: time(),
    window: new VerificationWindow(past: 1, future: 1),
    replayStore: $store,
    binding: 'user-42',
);

$result->matched;
$result->matchedTimestep;
$result->driftOffset;
$result->isExact();
$result->isDrifted();
$result->replayDetected;

Useful helpers:

<?php
$totp->getCurrentTimeStep();
$totp->getRemainingSeconds();
$totp->getTimeStepFromTimestamp(1716532624);

HOTP

<?php
use Infocyph\OTP\HOTP;

$secret = HOTP::generateSecret();
$hotp = (new HOTP($secret))
    ->setCounter(3)
    ->setAlgorithm('sha256');

$otp = $hotp->getOTP(346);

$isValid = $hotp->verify($otp, 346);

Look-ahead verification with matched-counter result:

<?php
use Infocyph\OTP\Stores\InMemoryReplayStore;

$result = $hotp->verifyWithResult(
    $otp,
    counter: 340,
    lookAhead: 10,
    replayStore: new InMemoryReplayStore(),
    binding: 'device-1',
);

$result->matched;
$result->matchedCounter;
$result->driftOffset;

Generic OTP

Generic OTP is now string-based and uses a caller-provided PSR-6 cache pool.

<?php
use Infocyph\OTP\OTP;
use Psr\Cache\CacheItemPoolInterface;

/ @var CacheItemPoolInterface $cachePool */
$otp = new OTP(
    digitCount: 6,
    validUpto: 60,
    retry: 3,
    hashAlgorithm: 'xxh128',
    cacheAdapter: $cachePool,
);

$code = $otp->generate('signup:alice@example.com');
$otp->verify('signup:alice@example.com', $code);
$otp->delete('signup:alice@example.com');
$otp->flush();

Notes:

  • Codes are strings, not integers
  • Leading zeroes are preserved
  • Digit count must be between `4` and `10`

OCRA

<?php
use Infocyph\OTP\OCRA;

$ocra = new OCRA('OCRA-1:HOTP-SHA256-8:C-QN08-PSHA1', '12345678901234567890123456789012');

$ocra->setPin('1234');

$code = $ocra->generate('12345678', 0);
$isValid = $ocra->verify($code, '12345678', 0);

Replay-aware verification:

<?php
use Infocyph\OTP\Stores\InMemoryReplayStore;

$result = $ocra->verifyWithResult(
    $code,
    challenge: '12345678',
    counter: 0,
    replayStore: new InMemoryReplayStore(),
    binding: 'user-42',
);

Provisioning

Generate otpauth:// URIs

<?php
$uri = $totp->getProvisioningUri('alice@example.com', 'Example App');

Render SVG QR

<?php
$svg = $totp->getProvisioningUriQR('alice@example.com', 'Example App');

Get enrollment payload

<?php
$payload = $totp->getEnrollmentPayload(
    'alice@example.com',
    'Example App',
    withQrSvg: true,
);

$payload->secret;
$payload->uri;
$payload->qrPayload;
$payload->issuer;
$payload->label;
$payload->qrSvg;

Parse existing otpauth:// URIs

<?php
use Infocyph\OTP\TOTP;

$parsed = TOTP::parseProvisioningUri($uri);

$parsed->type;
$parsed->secret;
$parsed->label;
$parsed->issuer;
$parsed->algorithm;
$parsed->digits;
$parsed->period;
$parsed->counter;
$parsed->ocraSuite;

Replay Protection

The package ships with contracts plus an in-memory store for testing and lightweight use:

  • `Infocyph\OTP\Contracts\ReplayStoreInterface`
  • `Infocyph\OTP\Stores\InMemoryReplayStore`

Recommended usage:

  • TOTP: store accepted timesteps per user/device binding
  • HOTP: store last accepted counter
  • OCRA: store used challenge/counter combinations where required

Recovery Codes

<?php
use Infocyph\OTP\RecoveryCodes;
use Infocyph\OTP\Stores\InMemoryRecoveryCodeStore;

$codes = new RecoveryCodes(new InMemoryRecoveryCodeStore());

$generated = $codes->generate(
    binding: 'user-42',
    count: 10,
    length: 10,
    groupSize: 4,
);

$generated->plainCodes;
$generated->totalGenerated;
$generated->remainingCount;

Consume a code:

<?php
$result = $codes->consume('user-42', $generated->plainCodes[0]);

$result->consumed;
$result->reason;
$result->remainingCount;
$result->totalGenerated;
$result->lastUsedAt;

Notes:

  • Recovery codes are stored hashed
  • Generating a new set replaces the old set
  • Display formatting is separate from storage hashing

Secret Utilities

Base32 helpers live in Infocyph\OTP\Support\SecretUtility.

<?php
use Infocyph\OTP\Support\SecretUtility;

$secret = SecretUtility::generate(64);
$normalized = SecretUtility::normalizeBase32('ab cd ef 234===');
$isValid = SecretUtility::isValidBase32($normalized);

Result Objects

For richer flows, use the advanced APIs and inspect:

  • `Infocyph\OTP\Result\VerificationResult`
  • `Infocyph\OTP\Result\RecoveryCodeGenerationResult`
  • `Infocyph\OTP\Result\RecoveryCodeConsumptionResult`

VerificationResult exposes:

  • `matched`
  • `reason`
  • `matchedTimestep`
  • `matchedCounter`
  • `driftOffset`
  • `replayDetected`
  • `verifiedAt`

Additional Helpers

  • `Infocyph\OTP\Support\StepUp`
  • `Infocyph\OTP\ValueObjects\DeviceEnrollment`

Example:

<?php
use Infocyph\OTP\Support\StepUp;

$requiresFreshOtp = StepUp::requiresFreshOtp($verifiedAt, 300);

Storage Guidance

  • OTP secrets are reversible secrets. If your application needs to generate OTPs later, hashing alone is not enough.
  • Recovery codes should usually be stored hashed.
  • Replay state may live in cache or a database depending on durability needs.
  • Generic OTP requires a PSR-6 cache pool implementation from the caller.

OCRA Suite Notes

Example suite:

OCRA-1:HOTP-SHA1-6:C-QN08-PSHA1

Supported suite parts include:

  • HMAC algorithms: `SHA1`, `SHA256`, `SHA512`
  • Digits: `0`, `4`-`10`
  • Challenge formats: numeric (`QNxx`), alphanumeric (`QAxx`), hexadecimal (`QHxx`)
  • Optional counter, PIN, session, and time components

References

  • HOTP (RFC4226): https://tools.ietf.org/html/rfc4226
  • TOTP (RFC6238): https://tools.ietf.org/html/rfc6238
  • OCRA (RFC6287): https://tools.ietf.org/html/rfc6287

Security

Protected by PHPForge ? an automated quality and security gate for PHP projects.

<div align="center"> <sub><strong>Made with ?? for the PHP community</strong></sub><br /> <sub><a href="LICENSE">MIT Licensed</a></sub><br /> <a href="https://docs.infocyph.com/projects/OTP">Documentation</a> ? <a href="SECURITY.md">Security</a> ? <a href="CODE_OF_CONDUCT.md">Code of Conduct</a> ? <a href="CONTRIBUTING.md">Contributing</a> ? <a href="https://github.com/infocyph/OTP/issues">Report | Request | Suggest</a> </div>


  Files folder image Files (82)  
File Role Description
Files folder image.github (3 files, 2 directories)
Files folder imagebenchmarks (1 file)
Files folder imagedocs (3 files, 4 directories)
Files folder imagesrc (6 files, 6 directories)
Files folder imagetests (7 files, 1 directory)
Accessible without login Plain text file .editorconfig Data Auxiliary data
Accessible without login Plain text file .readthedocs.yaml Data Auxiliary data
Accessible without login Plain text file captainhook.json Data Auxiliary data
Accessible without login Plain text file CODE_OF_CONDUCT.md Data Auxiliary data
Accessible without login Plain text file composer.json Data Auxiliary data
Accessible without login Plain text file CONTRIBUTING.md Data Auxiliary data
Accessible without login Plain text file LICENSE Lic. License text
Accessible without login Plain text file README.md Doc. Read me
Accessible without login Plain text file SECURITY.md Data Auxiliary data

  Files folder image Files (82)  /  .github  
File Role Description
Files folder imageISSUE_TEMPLATE (7 files)
Files folder imageworkflows (1 file)
  Accessible without login Plain text file CODEOWNERS Data Auxiliary data
  Accessible without login Plain text file dependabot.yml Data Auxiliary data
  Accessible without login Plain text file PULL_REQUEST_TEMPLATE.md Data Auxiliary data

  Files folder image Files (82)  /  .github  /  ISSUE_TEMPLATE  
File Role Description
  Accessible without login Plain text file bug_report.yml Data Auxiliary data
  Accessible without login Plain text file ci_failure.yml Data Auxiliary data
  Accessible without login Plain text file config.yml Data Auxiliary data
  Accessible without login Plain text file docs_improvement.yml Data Auxiliary data
  Accessible without login Plain text file feature_request.yml Data Auxiliary data
  Accessible without login Plain text file question.yml Data Auxiliary data
  Accessible without login Plain text file regression_report.yml Data Auxiliary data

  Files folder image Files (82)  /  .github  /  workflows  
File Role Description
  Accessible without login Plain text file security-standards.yml Data Auxiliary data

  Files folder image Files (82)  /  benchmarks  
File Role Description
  Plain text file OtpBench.php Class Class source

  Files folder image Files (82)  /  docs  
File Role Description
Files folder imageapi (3 files)
Files folder imagegetting-started (3 files)
Files folder imageguides (13 files)
Files folder image_static (1 file)
  Accessible without login Plain text file conf.py Data Auxiliary data
  Accessible without login Plain text file index.rst Data Auxiliary data
  Accessible without login Plain text file requirements.txt Doc. Documentation

  Files folder image Files (82)  /  docs  /  api  
File Role Description
  Accessible without login Plain text file contracts.rst Data Auxiliary data
  Accessible without login Plain text file results.rst Data Auxiliary data
  Accessible without login Plain text file support.rst Data Auxiliary data

  Files folder image Files (82)  /  docs  /  getting-started  
File Role Description
  Accessible without login Plain text file installation.rst Data Auxiliary data
  Accessible without login Plain text file migration.rst Data Auxiliary data
  Accessible without login Plain text file quickstart.rst Example Example script

  Files folder image Files (82)  /  docs  /  guides  
File Role Description
  Accessible without login Plain text file authenticator-apps.rst Example Example script
  Plain text file custom-stores.rst Class Class source
  Accessible without login Plain text file device-enrollment.rst Example Example script
  Accessible without login Plain text file generic-otp.rst Example Example script
  Accessible without login Plain text file hotp.rst Example Example script
  Accessible without login Plain text file ocra.rst Example Example script
  Accessible without login Plain text file provisioning.rst Example Example script
  Accessible without login Plain text file recovery-codes.rst Example Example script
  Accessible without login Plain text file replay-protection.rst Example Example script
  Accessible without login Plain text file secret-rotation.rst Example Example script
  Accessible without login Plain text file step-up-auth.rst Example Example script
  Accessible without login Plain text file storage.rst Data Auxiliary data
  Accessible without login Plain text file totp.rst Example Example script

  Files folder image Files (82)  /  docs  /  _static  
File Role Description
  Accessible without login Plain text file theme.css Data Auxiliary data

  Files folder image Files (82)  /  src  
File Role Description
Files folder imageContracts (3 files)
Files folder imageExceptions (1 file)
Files folder imageResult (4 files)
Files folder imageStores (2 files)
Files folder imageSupport (8 files)
Files folder imageValueObjects (6 files)
  Plain text file AbstractOtpAuthenticator.php Class Class source
  Plain text file HOTP.php Class Class source
  Plain text file OCRA.php Class Class source
  Plain text file OTP.php Class Class source
  Plain text file RecoveryCodes.php Class Class source
  Plain text file TOTP.php Class Class source

  Files folder image Files (82)  /  src  /  Contracts  
File Role Description
  Plain text file RecoveryCodeStoreInterface.php Class Class source
  Plain text file ReplayStoreInterface.php Class Class source
  Plain text file SecretStoreInterface.php Class Class source

  Files folder image Files (82)  /  src  /  Exceptions  
File Role Description
  Plain text file OCRAException.php Class Class source

  Files folder image Files (82)  /  src  /  Result  
File Role Description
  Plain text file RecoveryCodeConsumptionResult.php Class Class source
  Plain text file RecoveryCodeGenerationResult.php Class Class source
  Plain text file StepUpResult.php Class Class source
  Plain text file VerificationResult.php Class Class source

  Files folder image Files (82)  /  src  /  Stores  
File Role Description
  Plain text file InMemoryRecoveryCodeStore.php Class Class source
  Plain text file InMemoryReplayStore.php Class Class source

  Files folder image Files (82)  /  src  /  Support  
File Role Description
  Plain text file AlgorithmValidator.php Class Class source
  Plain text file LabelHelper.php Class Class source
  Plain text file OtpMath.php Class Class source
  Plain text file ProvisioningUriBuilder.php Class Class source
  Plain text file ProvisioningUriParser.php Class Class source
  Plain text file SecretUtility.php Class Class source
  Plain text file StepUp.php Class Class source
  Plain text file SvgQrRenderer.php Class Class source

  Files folder image Files (82)  /  src  /  ValueObjects  
File Role Description
  Plain text file DeviceEnrollment.php Class Class source
  Plain text file EnrollmentPayload.php Class Class source
  Plain text file OcraSuite.php Class Class source
  Plain text file ParsedOtpAuthUri.php Class Class source
  Plain text file SecretRotation.php Class Class source
  Plain text file VerificationWindow.php Class Class source

  Files folder image Files (82)  /  tests  
File Role Description
Files folder imageSupport (1 file)
  Accessible without login Plain text file AdvancedOTPTest.php Example Example script
  Accessible without login Plain text file ArchTest.php Example Example script
  Accessible without login Plain text file GenericOTPTest.php Example Example script
  Accessible without login Plain text file HOTPTest.php Example Example script
  Plain text file OCRATest.php Class Class source
  Accessible without login Plain text file TOTPTest.php Example Example script
  Accessible without login Plain text file WorkflowHelpersTest.php Example Example script

  Files folder image Files (82)  /  tests  /  Support  
File Role Description
  Plain text file InMemoryCacheItemPool.php Class Class source

The PHP Classes site has supported package installation using the Composer tool since 2013, as you may verify by reading this instructions page.
Install with Composer Install with Composer
 Version Control Unique User Downloads  
 100%
Total:0
This week:0