Repository navigation
Make Steam.provisioning_uri() round-trip through parse_uri() - #193
dylanpulver wants to merge 1 commit into
Conversation
Steam inherits TOTP.provisioning_uri(), which emits digits=self.digits.
Steam sets self.digits = 10 (the Steam code is derived from a 10-digit HOTP
value), so the URI carries digits=10 and no encoder=steam marker. parse_uri()
rejects digits outside {6, 7, 8} unless encoder=steam is present, so the URI
pyotp generates for a Steam instance cannot be parsed back by pyotp:
>>> pyotp.parse_uri(pyotp.contrib.Steam(secret).provisioning_uri())
ValueError: Digits may only be 6, 7, or 8
Override provisioning_uri() on Steam to emit encoder=steam and omit the
internal digits value. Adds the provisioning_uri -> parse_uri -> provisioning_uri
round-trip assertion that the suite already makes for HOTP and TOTP.
This comment was marked as low quality.
This comment was marked as low quality.
|
Hmm if this was not tested with a real client, what good is this change? Can anyone verify that this actually works with any authenticator app? |
|
Fair question. No authenticator app can produce Steam codes from any So a real-client test is not a bar this change can meet, and the current behaviour does not meet it either: Neither URI gets a Steam code out of a generic app, and What the change fixes is pyotp parsing its own output, which the suite asserts for HOTP and TOTP in 10 places and never for Steam. If you would rather |
pyotpcannot parse the provisioning URI it generates for aSteaminstance:Reproduces on released 2.10.0 and at HEAD (cc15683).
Mechanism.
Steamsetsself.digits = 10(contrib/steam.py:25) and doesn't overrideprovisioning_uri, so it inheritsTOTP.provisioning_uri(totp.py:117), which passesdigits=self.digitstobuild_uri;utils.py:46emits anydigits != 6. The URI thereforecarries
digits=10and noencoder=steam, andparse_uri(__init__.py:100-102) rejectsdigitsoutside {6,7,8} unlessencoder=steamis present.Both halves are wrong. The Key Uri Format spec both docstrings cite says "the
digitsparametermay have the values 6 or 8". And without
encoder=steamthe URI doesn't identify itself asSteam, so a consumer tolerating
digits=10yields ordinary TOTP codes (929991vsC5V56at t=0).
Fix. Override
provisioning_urionSteamto emitencoder=steamand omit the internaldigits.parse_uriis untouched; nothing previously rejected is now accepted.Verification.
make test/make lint/ruff format --checkon 3.14: 28 passed before, 29after; mypy clean. Adds the
provisioning_uri→parse_uri→provisioning_uriround-tripassertion the suite already makes for HOTP and TOTP in 10 places but never for Steam. Mutants:
reverting the fix fails the test; emitting
encoder=steamanddigits=10fails only thequery-parameter assertion (it survives the round-trip check, as
parse_uriskips the digitcheck when
encoder=steam); droppingdigitswithoutencoder=steamreturns aTOTP.Not tested. Only 3.14 locally, not the 3.10–3.14 matrix; no real Steam client scanned the
URI.
Steam.__init__'sinterval/digitsargs are separately ignored (steam.py:24isoverwritten by
TOTP.__init__) — left alone.Written with AI assistance (Claude).