HOTP Guide
==========

RFC context
-----------

HOTP is defined in RFC4226. Unlike TOTP, it is driven by a moving counter rather than wall-clock time.

That means:

- both sides must agree on a counter
- every successful authentication normally advances the server-side counter state
- resynchronization is a practical concern in real deployments

Basic usage
-----------

Generating a secret
-------------------

You can generate a new Base32 secret directly from the HOTP class:

.. code-block:: php

   <?php
   use Infocyph\OTP\HOTP;

   $secret = HOTP::generateSecret();

Creating an HOTP instance
-------------------------

.. code-block:: php

   <?php
   use Infocyph\OTP\HOTP;

   $hotp = (new HOTP($secret))
       ->setCounter(3)
       ->setAlgorithm('sha1');

   $otp = $hotp->getOTP(10);
   $hotp->verify($otp, 10);

Generating OTPs
---------------

.. code-block:: php

   <?php
   $counter = 346;
   $otp = $hotp->getOTP($counter);

Basic verification
------------------

.. code-block:: php

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

Look-ahead verification
-----------------------

HOTP often needs controlled counter resynchronization.

.. code-block:: php

   <?php
   $hotp->verify($otp, counter: 10, lookAhead: 5);

This means the verifier will try the provided counter and then probe forward up to the configured look-ahead window.

Rich verification result
------------------------

.. code-block:: php

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

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

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

Typical server pattern
----------------------

In a stateful HOTP deployment, a common flow is:

1. load the user or device counter
2. verify using a small look-ahead window
3. if matched, persist the returned ``matchedCounter``
4. reject any already-used counter value

Example:

.. code-block:: php

   <?php
   $storedCounter = 100;

   $result = $hotp->verifyWithResult(
       $submittedOtp,
       counter: $storedCounter,
       lookAhead: 5,
       replayStore: $store,
       binding: 'device-1',
   );

   if ($result->matched) {
       $nextCounter = $result->matchedCounter;
       // persist the new server-side counter state
   }

Provisioning
------------

HOTP is also commonly shared via ``otpauth://`` URIs, especially when interoperating with authenticator clients that support event-based counters.

.. code-block:: php

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

QR example:

.. code-block:: php

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

See :doc:`provisioning` for additional provisioning and QR examples.
