ProvenPin Install

pyotp TOTP and HOTP codes tested at frozen time: working versions (tested 2026-10-16)

Generate RFC-compatible TOTP and HOTP codes, build otpauth provisioning URIs and tolerate one-step clock drift, all pinned to deterministic time with freezegun.

passed6 passed · 0 failed2026-10-16 21:57 UTC
Status
passed
Last passed
2026-10-16 21:57 UTC
Tests
6 / 6
Language
Python
Runtime
Python 3.13.16
pinned versions3
pyotp@2.10.0freezegun@1.5.5pytest@9.1.1
run it
uv venv -q .venv && uv pip install -q --python .venv/bin/python -r requirements.txt
.venv/bin/python -m pytest -q
test environment
Python 3.13.16
linux 6.17.0-1022-azure
x64
runner: github-actions 38972656245

The code

Exactly the files that passed. Your agent can pull them with get_recipe("pyotp-totp-freezegun") (install), or see them on GitHub.

requirements.txt
pyotp==2.10.0
freezegun==1.5.5
pytest==9.1.1
otp.py
import datetime

import pyotp

# RFC 4226/6238 test secret: base32 of the ASCII bytes of 12345678901234567890
SECRET = "GEZDGNBVGY3TQOJQGEZDGNBVGY3TQOJQ"


def make_totp(
    secret: str = SECRET,
    issuer: str = "ProvenPin",
    account: str = "[email protected]",
) -> pyotp.TOTP:
    return pyotp.TOTP(secret, issuer=issuer, name=account)


def make_hotp(secret: str = SECRET) -> pyotp.HOTP:
    return pyotp.HOTP(secret)


def instant(timestamp: int) -> datetime.datetime:
    # TOTP.now() feeds datetime.now() through time.mktime (local time), while
    # freeze_time() pins the local wall clock. Building the instant with
    # fromtimestamp() makes the frozen Unix timestamp match on any host TZ.
    return datetime.datetime.fromtimestamp(timestamp)
tests/test_otp.py
import time
from urllib.parse import parse_qs, unquote, urlparse

import pytest
from freezegun import freeze_time

from otp import SECRET, instant, make_hotp, make_totp


def test_totp_matches_rfc6238_vectors():
    otp = make_totp()
    assert otp.at(59) == "287082"
    assert otp.at(1111111109) == "081804"
    assert otp.at(1234567890) == "005924"


@freeze_time(instant(1111111109))
def test_frozen_now_matches_rfc6238_vector():
    assert make_totp().now() == "081804"


def test_frozen_now_is_timezone_independent(monkeypatch):
    if not hasattr(time, "tzset"):
        pytest.skip("tzset is POSIX only")
    monkeypatch.setenv("TZ", "Asia/Kolkata")
    time.tzset()
    try:
        with freeze_time(instant(59)):
            assert make_totp().now() == "287082"
    finally:
        monkeypatch.undo()
        time.tzset()


def test_valid_window_accepts_one_step_of_drift():
    otp = make_totp()
    code = otp.at(1111111109)
    with freeze_time(instant(1111111109 + 30)):
        assert otp.verify(code, valid_window=1) is True
        assert otp.verify(code, valid_window=0) is False


def test_hotp_counter_advances_rfc4226_vectors():
    hotp = make_hotp()
    assert hotp.at(0) == "755224"
    assert hotp.verify("287082", 1) is True
    assert hotp.verify("755224", 1) is False


def test_provisioning_uri_encodes_issuer_and_account():
    uri = make_totp(issuer="ProvenPin", account="[email protected]").provisioning_uri()
    parsed = urlparse(uri)
    query = parse_qs(parsed.query)
    assert parsed.scheme == "otpauth" and parsed.netloc == "totp"
    assert query["secret"] == [SECRET]
    assert query["issuer"] == ["ProvenPin"]
    assert unquote(parsed.path.lstrip("/")) == "ProvenPin:[email protected]"
pytest.ini
[pytest]
pythonpath = .

Connect to ProvenPin

Add the free MCP server so your agent can pull this recipe and others like it. Same setup as the docs.

terminal
claude mcp add --transport http provenpin https://mcp.provenpin.com/mcp

Any other MCP client: Streamable HTTP at https://mcp.provenpin.com/mcp. No API key, no signup.