feat(auth): two-factor (TOTP) login seam + enterprise-gated UI (trinity-enterprise#5)#1247
Open
dolho wants to merge 1 commit into
Open
feat(auth): two-factor (TOTP) login seam + enterprise-gated UI (trinity-enterprise#5)#1247dolho wants to merge 1 commit into
dolho wants to merge 1 commit into
Conversation
OSS side of two-factor auth (enterprise issue #5). All IP (TOTP verify, secret store, recovery codes, policy) lives in the private trinity-enterprise submodule; this PR adds only the edition-agnostic seam + the entitlement-gated Vue surface, consistent with the open-core pattern (#847). Backend seam: - services/mfa_gate.py — single hook the enterprise provider registers into. No provider (OSS-only build) → gate_login() returns None → login unchanged. Fail-open on provider error (never lock everyone out of login). - dependencies.py — create_mfa_challenge_token / decode_mfa_challenge; a challenge-scoped token is rejected as a session token in get_current_user and decode_token. - routers/auth.py — /token and /api/auth/email/verify consult mfa_gate after the first factor; when a second factor is required they return a short-lived challenge token instead of an access token (audited as mfa_challenge_issued). - models.py — Token gains optional mfa_required / challenge_token fields. - Dockerfile — pyotp (used by the enterprise module running in this image). Frontend (gated by `2fa` in GET /api/settings/feature-flags): - Settings → Security tab (TwoFactorPanel): self-service enroll/confirm/ disable/recovery; admin-only org policy. - Login.vue: second-factor step (verify + forced-enroll) after the password/ email factor; QrCode.vue renders the otpauth URI (graceful manual-key fallback). auth store carries the challenge through to the real token. OSS-only builds: no Security tab, no login step, /api/enterprise/2fa/* → 404. Related to Abilityai/trinity-enterprise#5 Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
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.
What
OSS side of two-factor authentication (TOTP) — enterprise issue Abilityai/trinity-enterprise#5.
Per the open-core pattern (#847), all algorithmic IP (TOTP verify, AES secret store, recovery codes, per-role policy) lives in the private
trinity-enterprisesubmodule (separate PR). This PR adds only the edition-agnostic login seam + the entitlement-gated Vue surface.Backend seam
services/mfa_gate.py— the single hook the enterprise provider registers into. No provider (OSS-only build) →gate_login()returnsNone→ login is byte-for-byte unchanged. Fail-open on provider error.dependencies.py—create_mfa_challenge_token/decode_mfa_challenge; a challenge-scoped token is rejected as a session token inget_current_user+decode_token.routers/auth.py—/tokenand/api/auth/email/verifyconsultmfa_gateafter the first factor; if a second factor is required they return a short-lived challenge token instead of an access token (auditedmfa_challenge_issued).models.py—Tokengains optionalmfa_required/challenge_tokenfields.docker/backend/Dockerfile—pyotp(the enterprise module runs in this image).Frontend (gated by
2fainGET /api/settings/feature-flags)TwoFactorPanel.vue): self-service enroll / confirm / disable / recovery codes; admin-only org policy.QrCode.vuerenders theotpauthURI with a graceful manual-key fallback.Edition behavior
/api/enterprise/2fa/*Testing
Verified end-to-end on a live stack (enroll → confirm → gated re-login →
login/verify→ working token; replay rejected; recovery-code login; challenge-token-as-session rejected). Enterprise unit + real-DB pytest 20/20.Related to Abilityai/trinity-enterprise#5
🤖 Generated with Claude Code