PHPackages                             phpdot/totp - PHPackages - PHPackages  [Skip to content](#main-content)[PHPackages](/)[Directory](/)[Categories](/categories)[Trending](/trending)[Leaderboard](/leaderboard)[Changelog](/changelog)[Analyze](/analyze)[Collections](/collections)[Log in](/login)[Sign up](/register)

1. [Directory](/)
2. /
3. phpdot/totp

ActiveLibrary

phpdot/totp
===========

Coroutine-safe, zero-dependency HOTP/TOTP (RFC 4226 / RFC 6238) with provisioning URIs for the PHPdot ecosystem.

v0.1.0(1mo ago)00MITPHPPHP &gt;=8.5

Since Jul 18Pushed 1w agoCompare

[ Source](https://github.com/phpdot/totp)[ Packagist](https://packagist.org/packages/phpdot/totp)[ RSS](/packages/phpdot-totp/feed)WikiDiscussions main Synced 1w ago

READMEChangelogDependencies (7)Versions (2)Used By (0)

phpdot/totp
===========

[](#phpdottotp)

Zero-dependency HOTP/TOTP for the PHPdot ecosystem — RFC 4226 and RFC 6238, built from scratch and verified against the official RFC test vectors. Generate authenticator secrets, compute and verify codes, emit `otpauth://` provisioning URIs, and (optionally) render the enrollment QR via `phpdot/qrcode`. Time is injected as a PSR-20 clock, so codes are testable and read fresh under a long-lived Swoole worker; the only runtime dependency is the `psr/clock` interface.

Table of Contents
-----------------

[](#table-of-contents)

- [Requirements](#requirements)
- [Installation](#installation)
- [Usage](#usage)
- [Architecture](#architecture)
- [Testing](#testing)
- [License](#license)

Requirements
------------

[](#requirements)

RequirementConstraintPHP`>= 8.5``psr/clock``^1.0``phpdot/qrcode` (`^0.2`) is an optional suggestion — install it only if you want `QrCodeBridge` to render the enrollment QR code; the core (secrets, codes, provisioning URIs) needs nothing but `psr/clock`.

Installation
------------

[](#installation)

```
composer require phpdot/totp
```

Usage
-----

[](#usage)

### Quick start

[](#quick-start)

Inject `OtpFactory`, make a secret at enrollment, and verify the typed code at login:

```
use PHPdot\Totp\OtpFactory;

final class TwoFactor
{
    public function __construct(private readonly OtpFactory $otp) {}

    public function enroll(string $account): string
    {
        $secret = $this->otp->generateSecret();                 // CSPRNG, 160-bit
        // persist $secret->toBase32() against the user — ENCRYPTED (see Security)
        return $this->otp->totp($secret)->provisioningUri($account, 'phpdot');
    }

    public function check(string $secretBase32, string $code): bool
    {
        $secret = \PHPdot\Totp\Secret\Secret::fromBase32($secretBase32);
        return $this->otp->totp($secret)->verify($code)->passed;
    }
}
```

`Totp` exposes `current()`/`previous()`/`next()` and `at($timestamp)` for display, `window($steps)` for a run of codes around now, and `verify()`/`verifyAt()` for validation. `Hotp` is the counter-based variant. Defaults are **SHA-1 / 6 digits / 30s** — what every authenticator app expects; SHA-256/512 and 7–8 digits are available via `Algorithm` but not all scanners honour non-default parameters.

### Security

[](#security)

The cryptography here is small and RFC-verified; the real risks live **around** it, in your application. This package deliberately does not implement the following — they cannot live in a stateless library:

- **Encrypt the secret at rest.** A TOTP secret is a symmetric credential — it cannot be hashed, since verification needs the original bytes. Encrypt it before persisting (app key / KMS / libsodium) and decrypt only to verify. `Secret` marks its raw input `#[\SensitiveParameter]` to keep it out of stack traces, but storage is on you.
- **Rate-limit the verify endpoint.** A 6-digit code is one of a million values; without throttling and lockout an attacker can brute-force it. That is an HTTP/middleware concern the library has no context for.
- **Block replay with `after`.** A code stays valid for its whole step (plus any drift window). `verify()`returns the matched `timestep` — persist it and pass it back as `after` to reject any step at or before the last one used:

```
$result = $otp->totp($secret)->verify($userInput, after: $user->lastTotpStep);

if ($result->passed) {
    $user->lastTotpStep = $result->timestep; // that code can never be replayed
}
```

### QR enrollment (optional)

[](#qr-enrollment-optional)

With `phpdot/qrcode` installed, inject `QrCodeBridge` to render the provisioning URI straight to an image (it disables ECI, since an `otpauth://` URI is pure ASCII):

```
use PHPdot\Totp\Qr\QrCodeBridge;

$svg = $bridge->svg($otp->totp($secret), 'alice@example.com', 'phpdot');
```

Architecture
------------

[](#architecture)

`OtpFactory` is the injected `#[Singleton]` entry point; it builds `Totp` (RFC 6238) and `Hotp` (RFC 4226) over a shared abstract `Otp` core (HMAC + dynamic truncation) and an injected PSR-20 clock. A `Secret`holds the raw key and its Base32 codec; `ProvisioningUri` builds the `otpauth://` URI; `Verification` and `OtpWindow` are the immutable results. `QrCodeBridge` is an optional caller that renders a provisioning URI through `phpdot/qrcode`.

 ```
graph TD
    FACTORY["OtpFactory#[Singleton] — inject this"]
    CLOCK["ClockInterface (PSR-20)SystemClock by default"]
    OTP["Otp (abstract)RFC 4226 HMAC + truncation"]
    HOTP["Hotpcounter-based"]
    TOTP["Totptime-based"]
    SECRET["Secret + Base32"]
    URI["ProvisioningUriotpauth:// builder"]
    BRIDGE["QrCodeBridgeoptional → phpdot/qrcode"]

    FACTORY --> CLOCK
    FACTORY --> HOTP
    FACTORY --> TOTP
    HOTP --> OTP
    TOTP --> OTP
    FACTORY --> SECRET
    TOTP --> URI
    URI --> BRIDGE
```

      Loading Testing
-------

[](#testing)

```
composer install
composer test        # PHPUnit — includes the RFC 4226 / RFC 6238 vectors
composer analyse     # PHPStan, level max + strict rules
composer cs-check    # PHP-CS-Fixer
composer check       # All three
```

The full RFC 4226 (Appendix D) and RFC 6238 (Appendix B, all three algorithms) test vectors run as tests, including the per-algorithm seed lengths most implementations get wrong.

License
-------

[](#license)

MIT — see [LICENSE](LICENSE).

This repository is a **read-only mirror**. The canonical source lives in [phpdot/monorepo](https://github.com/phpdot/monorepo); pull requests and issues are handled there: [pulls](https://github.com/phpdot/monorepo/pulls) · [issues](https://github.com/phpdot/monorepo/issues).

###  Health Score

37

—

LowBetter than 81% of packages

Maintenance94

Actively maintained with recent releases

Popularity0

Limited adoption so far

Community6

Small or concentrated contributor base

Maturity41

Maturing project, gaining track record

 Bus Factor1

Top contributor holds 100% of commits — single point of failure

How is this calculated?**Maintenance (25%)** — Last commit recency, latest release date, and issue-to-star ratio. Uses a 2-year decay window.

**Popularity (30%)** — Total and monthly downloads, GitHub stars, and forks. Logarithmic scaling prevents top-heavy scores.

**Community (15%)** — Contributors, dependents, forks, watchers, and maintainers. Measures real ecosystem engagement.

**Maturity (30%)** — Project age, version count, PHP version support, and release stability.

###  Release Activity

Cadence

Unknown

Total

1

Last Release

45d ago

### Community

Maintainers

![](https://www.gravatar.com/avatar/62e82421bda4b5d6ba9a47ba6d88caca060dcd0d1a2862f351f3a97657385db0?d=identicon)[phpdot](/maintainers/phpdot)

---

Top Contributors

[![o3AM](https://avatars.githubusercontent.com/u/252500?v=4)](https://github.com/o3AM "o3AM (3 commits)")

---

Tags

otphotptotp2faMFArfc4226rfc6238phpdot

###  Code Quality

TestsPHPUnit

Static AnalysisPHPStan

Code StylePHP CS Fixer

Type Coverage Yes

### Embed Badge

![Health badge](/badges/phpdot-totp/health.svg)

```
[![Health](https://phpackages.com/badges/phpdot-totp/health.svg)](https://phpackages.com/packages/phpdot-totp)
```

###  Alternatives

[pragmarx/google2fa

A One Time Password Authentication package, compatible with Google Authenticator.

2.0k111.2M302](/packages/pragmarx-google2fa)[spomky-labs/otphp

A PHP library for generating one time passwords according to RFC 4226 (HOTP Algorithm) and the RFC 6238 (TOTP Algorithm) and compatible with Google Authenticator

1.5k53.4M205](/packages/spomky-labs-otphp)[chillerlan/php-authenticator

A generator for counter- and time based 2-factor authentication codes (Google Authenticator). PHP 8.2+

58145.3k4](/packages/chillerlan-php-authenticator)[christian-riesen/otp

One Time Passwords, hotp and totp according to RFC4226 and RFC6238

925.8M6](/packages/christian-riesen-otp)[tempest/framework

The PHP framework that gets out of your way.

2.3k42.4k21](/packages/tempest-framework)[rych/otp

PHP implementation of the OATH one-time password standards

36286.9k5](/packages/rych-otp)

PHPackages © 2026

[Directory](/)[Categories](/categories)[Trending](/trending)[Changelog](/changelog)[Analyze](/analyze)
