Repository navigation
CCCT-2918: Lock the backup code instead of deactivating the account - #316
Open
OrangeAndGreen wants to merge 3 commits into
Open
OrangeAndGreen wants to merge 3 commits into
OrangeAndGreen wants to merge 3 commits into
Conversation
Clients before 3.0 expect a locked account to be refused when configuring, so they keep refusing any locked user. All versions keep the existing check on inactive locked accounts. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
The backup code check now refuses a locked user before looking at the code, and the legacy recover_account and confirm_recovery_pin endpoints refuse locked users too. Completing recovery clears the lock. Locked users are still inactive today, so none of this changes behaviour yet. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
The third wrong backup code now sets is_locked only. The account stays active, so the owner's existing device keeps working, and a 3.0 client can reach email recovery after the backup code is locked. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
|
Important
This repository does not receive automatic reviews because it has fewer than 10 stars. ⚙️ Run configuration
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Technical Summary
CCCT-2918
Corresponding mobile changes: CCCT-2912
Design doc
After three wrong backup codes we deactivate the account, so the user is refused before the Name page and never reaches email recovery. Now "locked" means only the backup code is disabled: the account stays active, any backup code is refused with LOCKED_ACCOUNT, and the app (dimagi/commcare-android#3962) moves the user to email recovery, which clears the lock.
Best reviewed commit by commit. The first two change nothing on their own, because today every locked account is also inactive:
recover_accountandconfirm_recovery_pinalso refuse locked users, since an inactive account no longer shields them. Completing recovery clears the lock.is_lockedonly.Worth a look: the version check sits in
authenticate(request), becauseauthenticate_credentialsnever sees the request.test_version_comes_from_accept_headerchecks that DRF has setrequest.versionby then.Logging and monitoring
The BigQuery user export (
users/tasks.py) will show newly locked users asis_active=True. Reports that count locked users as inactive will change meaning.Safety Assurance
Safety story
Accounts locked before this change stay inactive and are refused on every version exactly as now. A 2.0 app sees the same LOCKED_ACCOUNT at session start. The one change it sees is that its owner's existing device keeps working after someone else gets the code wrong three times. A non-matching name can't register over a locked account, because
complete_profilealready refuses when the number has an active account.Automated test coverage
test_auth.pycovers the session check for each version, with inactive and active locked users, plus one request through the Accept header.test_views.pycovers a locked user refused before the code is checked (counter unchanged, session not marked verified) on both backup code endpoints, email recovery clearing the lock, and refusal on both legacy endpoints. The new view tests fail without the change.QA Plan
QA together with the 2.65 build that includes #3962: with and without an email on the account, lock the backup code, retry recovery and check email recovery clears the lock. Then repeat on a pre-3.0 app build to check it is still refused.
Deployment
This has to be deployed before the 2.65 QA build that sends API 3.0, since the server returns 406 for a version it doesn't know.
Out of scope, in the backlog: reactivating accounts locked under the old scheme, and making the admin unlock page find active locked accounts. Until then, a developer can unlock one with
unlock_and_generate_backup_code --inactive_user_id.Labels & Review
🤖 Generated with Claude Code