feat(2fa): implement step-up authentication - #3215
Open
FrankApiyo wants to merge 1 commit into
Open
FrankApiyo wants to merge 1 commit into
FrankApiyo wants to merge 1 commit into
Conversation
FrankApiyo
force-pushed
the
feat/step-up-authentication
branch
4 times, most recently
from
August 17, 2026 12:05
997ea08 to
617dffa
Compare
2 tasks
FrankApiyo
force-pushed
the
feat/step-up-authentication
branch
3 times, most recently
from
August 19, 2026 07:50
afbbc6a to
ecc8ee3
Compare
FrankApiyo
force-pushed
the
feat/django-two-factor-auth
branch
from
August 19, 2026 09:07
d3797e6 to
19d2b1c
Compare
FrankApiyo
force-pushed
the
feat/step-up-authentication
branch
2 times, most recently
from
August 19, 2026 10:15
5799b9f to
af118e4
Compare
FrankApiyo
force-pushed
the
feat/django-two-factor-auth
branch
from
August 19, 2026 10:19
6d33f67 to
0d13046
Compare
FrankApiyo
force-pushed
the
feat/step-up-authentication
branch
from
August 19, 2026 10:21
af118e4 to
1bd7149
Compare
FrankApiyo
force-pushed
the
feat/django-two-factor-auth
branch
from
August 19, 2026 11:10
7532dd9 to
d29ef14
Compare
FrankApiyo
force-pushed
the
feat/step-up-authentication
branch
from
August 19, 2026 12:35
1bd7149 to
e3c5c58
Compare
FrankApiyo
force-pushed
the
feat/django-two-factor-auth
branch
from
August 19, 2026 13:03
00d9c16 to
5a1adfb
Compare
FrankApiyo
force-pushed
the
feat/step-up-authentication
branch
from
August 19, 2026 13:04
d0c2148 to
0638997
Compare
FrankApiyo
force-pushed
the
feat/django-two-factor-auth
branch
from
August 19, 2026 13:11
5a1adfb to
4042f52
Compare
FrankApiyo
force-pushed
the
feat/step-up-authentication
branch
2 times, most recently
from
August 19, 2026 13:28
35e5214 to
11c8d60
Compare
FrankApiyo
force-pushed
the
feat/django-two-factor-auth
branch
from
August 19, 2026 13:38
b90ed42 to
3f6f571
Compare
FrankApiyo
force-pushed
the
feat/step-up-authentication
branch
from
August 19, 2026 13:39
11c8d60 to
5fe1dc3
Compare
FrankApiyo
force-pushed
the
feat/step-up-authentication
branch
from
August 19, 2026 13:53
5fe1dc3 to
c6281e3
Compare
FrankApiyo
force-pushed
the
feat/django-two-factor-auth
branch
from
August 20, 2026 10:00
9cd079d to
7a0b620
Compare
FrankApiyo
force-pushed
the
feat/step-up-authentication
branch
from
August 20, 2026 10:06
68df7e3 to
3e692d9
Compare
FrankApiyo
force-pushed
the
feat/django-two-factor-auth
branch
from
August 20, 2026 12:22
7a0b620 to
46c4cba
Compare
FrankApiyo
force-pushed
the
feat/step-up-authentication
branch
2 times, most recently
from
August 20, 2026 12:46
1cbeb48 to
2518c4b
Compare
FrankApiyo
force-pushed
the
feat/step-up-authentication
branch
from
August 23, 2026 13:07
8ebd0e6 to
4ff7399
Compare
2 tasks
FrankApiyo
force-pushed
the
feat/step-up-authentication
branch
from
August 27, 2026 08:35
d76deac to
4ff7399
Compare
FrankApiyo
force-pushed
the
feat/django-two-factor-auth
branch
from
August 27, 2026 09:16
3aaa21f to
f72ae01
Compare
FrankApiyo
force-pushed
the
feat/step-up-authentication
branch
from
August 27, 2026 09:20
08156f5 to
ab4c589
Compare
FrankApiyo
force-pushed
the
feat/django-two-factor-auth
branch
4 times, most recently
from
August 31, 2026 13:26
6432a7e to
335865f
Compare
FrankApiyo
force-pushed
the
feat/step-up-authentication
branch
from
August 31, 2026 13:46
e402046 to
2aa0911
Compare
FrankApiyo
force-pushed
the
feat/django-two-factor-auth
branch
from
August 31, 2026 14:15
335865f to
c98d3d5
Compare
FrankApiyo
force-pushed
the
feat/step-up-authentication
branch
2 times, most recently
from
August 31, 2026 16:38
fc589f2 to
db5fd2f
Compare
FrankApiyo
force-pushed
the
feat/django-two-factor-auth
branch
from
September 1, 2026 07:40
c98d3d5 to
2f4b5e8
Compare
FrankApiyo
force-pushed
the
feat/step-up-authentication
branch
from
September 1, 2026 07:40
db5fd2f to
cfb3411
Compare
FrankApiyo
force-pushed
the
feat/django-two-factor-auth
branch
from
September 1, 2026 08:04
2f4b5e8 to
d735885
Compare
FrankApiyo
force-pushed
the
feat/step-up-authentication
branch
5 times, most recently
from
September 1, 2026 10:10
c13af42 to
2625413
Compare
FrankApiyo
force-pushed
the
feat/django-two-factor-auth
branch
4 times, most recently
from
September 7, 2026 10:57
2567db3 to
faa3d1d
Compare
FrankApiyo
force-pushed
the
feat/step-up-authentication
branch
2 times, most recently
from
September 17, 2026 13:03
abb7e79 to
255262a
Compare
FrankApiyo
force-pushed
the
feat/step-up-authentication
branch
from
September 17, 2026 13:20
255262a to
3250246
Compare
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.
Changes / Features implemented
Builds on #3207 (merged). Depends on onaio/ona-oidc#139.
#3207 puts sign-in behind a second factor; this gates individual account actions. A gated action is refused until the caller presents a single-use grant earned for that action. Each is opt-in via
STEP_UP["ACTIONS"]: API and ODK token regeneration, email change, password change, the require-auth toggle and privacy consent. Profile gates fire only on an actual change, and the server-rendered settings and password-change pages are gated too, so they are not a way around the API.Two modes via
STEP_UP["MODE"]: inlocal, onadata owns the factor and the caller proves a code at/api/v1/totp/verify; infederated, an identity provider does —/api/v1/stepup/startreturns the authorization URL and/api/v1/stepup/callbackmints the grant./api/v1/user/capabilitiesreports what the deployment can manage. A grant travels in the body or theX-Step-Up-Grantheader, whichCORS_ALLOW_HEADERSnow allows.Grants live in ona-oidc's
stepup_grants; the audience whitelist stays here and its default covers every gated audience. Deploy checks catch misconfiguration:stepup.W001(a gate bypassable underskip_gate),W002(a gated audience/totp/verifycannot mint),W003(an unrecognisedMODE),W004(local gating withENABLE_TWO_FACTORoff). Documented indocs/two_factor.rst.Steps taken to verify this change does what is intended
Tests cover policy, challenge dialects, capabilities, the federated round trip, the 409s, the deploy checks, and each gate's refused and grant-accepted paths — including the server-rendered pages and the CORS preflight. Codes come from a real HOTP implementation, and the single-use grant is exercised end to end. Verified against a live Keycloak: sign-in takes the password only; a gated action prompts for the factor and returns
acr: goldwith a freshauth_time.Worth a close read:
skip_gatewould wave every gated action through; federated mode always challenges instead.PUTcannot drop the record ungated.partial_updatedelegates toupdateand both run the gate.Side effects of implementing this change
TWO_FACTOR_ENROLMENT_REQUIRES_PASSWORDfrom feat(2fa): two-factor authentication with secrets encrypted at rest #3207 is removed: a first enrolment always needs the account password./totp/statusgainsmanagedByandcapabilities; in federated mode the/totpmanagement endpoints answer 409.requirementspin ona-oidc at the use common settings as default to prevent local_settings from being overidden by default_settings #139 branch head. Re-pin to amainSHA once that merges.Before submitting this PR for review, please make sure you have:
Closes #