Nobody has started this yet — be first.
Business impact
TOTP_DIGITS (default 6, the RFC 6238 / Google Authenticator default) already exists in config, and provisioning_uri() already reads it correctly when telling an authenticator app how many digits to generate. But verify_totp_code() never reads it, so any deployment that changes TOTP_DIGITS away from 6 gets a 2FA flow where the provisioning side and the verification side disagree about the protocol they are speaking.
Problem
verify_totp_code() builds its own pyotp.TOTP(secret) with no digits argument at all, silently defaulting to 6 regardless of what TOTP_DIGITS actually says, even though provisioning_uri() two functions away correctly reads current_app.config.get("TOTP_DIGITS", 6).
Current behavior
With TOTP_DIGITS=8 set, an 8-digit code generated exactly as the provisioning URI instructed is rejected by verify_totp_code() as an "invalid code."
Expected behavior
Whatever TOTP_DIGITS is set to, provisioning_uri() and verify_totp_code() agree on it. Setting TOTP_DIGITS=8 (or any other supported length) results in a working 2FA flow end to end: the provisioning URI tells the authenticator app to generate N-digit codes, and the server checks against N-digit codes.
Steps to reproduce
Why this matters
This is a cross-component protocol mismatch scoped to a single codebase: the code path that advertises a protocol parameter (digit count) to an external party (the user's authenticator app, which has no other source of truth) and the code path that enforces it are two different functions, edited at two different times. The bug is invisible with the default config (both sides happen to agree by coincidence: 6 is both the hardcoded pyotp default and the configured default), which is exactly what makes this kind of mismatch dangerous -- it passes every test until the day someone actually changes the config value, at which point 2FA silently locks out 100% of users on that deployment.
Suggested approach
Compare the two functions side by side. provisioning_uri() already shows the correct pattern for reading TOTP_DIGITS from current_app.config. Also consider: does changing the digit count change anything else about how verify_totp_code()'s manual step-checking loop needs to work, or is it purely a constructor argument?
Acceptance criteria
Verification
pytest practice_tickets/tests/test_ticket_14.py -v
Hints (0/2)
Try it without hints first — the reading is the exercise.
Working on this ticket
Work on a branch named for the ticket — that's what you'll submit.
Branch off your fork
Sign in to create it automatically, or do it yourself:
$git checkout -b feat/totp-digits-mismatchFix it and commit
Meet every acceptance criterion, and add a test that would have caught this.
Push the branch
$git push -u origin feat/totp-digits-mismatchSubmit it below
Paste your fork URL and the branch name, with a short write-up of the root cause.
Questions
Ask about anything unclear in the ticket — the maintainer and anyone who has solved it can answer. Please don't post full solutions.
Sign in to ask a question or reply.
Sign inhttp://localhost:5000make seedpyotpmake docker-prod-upYou do not need Docker running at all to work the tickets -- the test suite uses an in-memory SQLite database (via StaticPool, an isolated DB per test function) and fakeredis (TestingConfig.USE_FAKE_REDIS), so everything runs against a plain virtualenv:
python -m venv .venv && source .venv/bin/activate
pip install -r requirements.txt
pytest practice_tickets/tests/test_ticket_01.py -v # a single ticket
./practice_tickets/run_tickets.sh # all 15, clean pass/fail summary
All 15 tests fail at the start; as you fix each ticket in app/, its test (and only its test) should flip to passing. make test (pytest -q) runs the project's own main suite -- as of writing it shows 9 pre-existing failures caused by ticket bugs 02, 03, 07, 11, and 15; fixing those tickets fixes those failures too, not a separate task. Do not look at git diff / version control history to find the bugs if this repo is ever put under source control -- the point is to find them the way you would find a real bug: from a failing test and a description of broken behavior, not a diff.
Level 2
Implement a feature
Extend the system within its own patterns.