mirror of
https://github.com/maziggy/bambuddy.git
synced 2026-05-09 05:35:30 +02:00
[PR #933] [MERGED] feat: Two-Factor Authentication (TOTP, Email OTP) and OIDC/SSO – full implementation with admin UI #1133
Labels
No labels
A1
automated
automated
bug
bug
Closed due to inactivity
contrib
dependencies
dependencies
duplicate
enhancement
feedback
hold
invalid
Notes
P1S
pull-request
security
security
ThumbsUp
user-report
wontfix
No milestone
No project
No assignees
1 participant
Notifications
Due date
No due date set.
Dependencies
No dependencies set.
Reference
starred/bambuddy#1133
Loading…
Add table
Add a link
Reference in a new issue
No description provided.
Delete branch "%!s()"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
📋 Pull Request Information
Original PR: https://github.com/maziggy/bambuddy/pull/933
Author: @netscout2001
Created: 4/10/2026
Status: ✅ Merged
Merged: 4/13/2026
Merged by: @maziggy
Base:
dev← Head:feature/2fa-oidc-authentication📝 Commits (10+)
8020889feat: add Two-Factor Authentication (TOTP, Email OTP) and OIDC/SSO4663cabfeat: add 2FA and OIDC settings UI with full translationsbd07bedtest: add unit and integration tests for 2FA and OIDCbba2495fix(security): address all critical and important issues from maintainer review9203794fix(frontend): update UI for email OTP two-step enable, password-gated disable, and OIDC 2FA7c313e0test: add missing security tests and update existing tests for API changesf7e570efix(frontend): remove unused Toggle import in TwoFactorSettingseb8ca2btest: add email OTP confirm endpoint tests and fix disable test80dd277fix(security): TOTP replay protection, rate limiting on disable/regenerate, migration cleanup7edab65fix(security): bind pre_auth token to HttpOnly cookie, encrypt secrets at rest📊 Changes
44 files changed (+10473 additions, -290 deletions)
View changed files
📝
backend/app/api/routes/archives.py(+4 -4)📝
backend/app/api/routes/auth.py(+428 -57)📝
backend/app/api/routes/camera.py(+1 -1)📝
backend/app/api/routes/library.py(+2 -2)➕
backend/app/api/routes/mfa.py(+1690 -0)📝
backend/app/api/routes/users.py(+43 -7)📝
backend/app/core/auth.py(+256 -64)📝
backend/app/core/database.py(+44 -0)➕
backend/app/core/encryption.py(+88 -0)📝
backend/app/main.py(+158 -4)📝
backend/app/models/__init__.py(+10 -0)➕
backend/app/models/auth_ephemeral.py(+199 -0)➕
backend/app/models/oidc_provider.py(+93 -0)📝
backend/app/models/user.py(+5 -1)➕
backend/app/models/user_otp_code.py(+55 -0)➕
backend/app/models/user_totp.py(+84 -0)📝
backend/app/schemas/auth.py(+344 -16)📝
backend/app/services/email_service.py(+67 -4)📝
backend/tests/conftest.py(+4 -0)📝
backend/tests/integration/test_advanced_auth_api.py(+111 -27)...and 24 more files
📄 Description
Description
Adds a complete multi-factor authentication and single sign-on system to BamBuddy, accompanied by an extensive security hardening pass (~50 findings across four review rounds).
Related Issue
Features https://github.com/maziggy/bambuddy/issues/902
Documentation
Companion docs PRs (delete lines that don't apply):
Pick one:
Type of Change
Two-Factor Authentication
OIDC / Single Sign-On
Security Hardening
Input validation (pbkdf2 / pyotp DoS guard)
max_lengthon string fields — unbounded input reached pbkdf2 or pyotpmax_lengthenforced on all user-facing fields before any hashing or OTP call:LoginRequest,SetupRequest,UserCreate,UserUpdate,ChangePasswordRequest,ForgotPasswordRequest,TOTPSetupRequest,EmailOTPEnableConfirmRequest,OIDCProviderCreate/Updatecodenow validated as exactly 6 digits before reaching any verification logicTwoFAVerifyRequest.codeaccepted arbitrary strings up to 128 chars^[A-Za-z0-9]{6,8}$added; field capped atmax_length=8JWT & Session management
jtiblocklist; logout revokes the token immediatelyiatclaim added to all JWTs;password_changed_attimestamp on User invalidates all prior sessions/auth/meandauth_middlewareskipped freshness check_is_token_fresh()now enforced in all 8 JWT validation pathsAuthentication hardening
client.host(proxy-blind)_get_client_ip()withTRUSTED_PROXY_IPSenv var; rightmost non-proxy IP fromX-Forwarded-Forcheck_rate_limit+record_failed_attemptaddedsetup_totprequires a valid current TOTP code before overwritingPOST /forgot-passwordonly rate-limited by email addresssend_email()moved to FastAPIBackgroundTasks; response time now constantPre-auth & token handling
DELETE … RETURNINGlast_totp_countertracked per TOTP record; replay rejectedAuthEphemeralTokenwith TTL and single-use enforcementnonce(resource key) added to theDELETE … WHEREclause; wrong-resource calls cannot consume the tokenget_api_key(webhook path) scanned all keys with pbkdf2key_prefixpre-filter added (same O(1) fix as login API key validation)OIDC / SSO
email_verifiedclaim not checkedcode,state,errorquery paramstoken_endpoint/jwks_uricould use non-HTTP schemeshttps://orhttp://only); SSRF viafile://,gopher://etc. preventedauto_link_existing_accountsdefaulted toTrueFalse; operators must explicitly opt in to prevent silent account hijack by an attacker-controlled IdPUserOIDCLinkrows for the target user are queried; any existing link blocks auto-link to prevent a second IdP from taking over the accountauth_source="local"auth_source="oidc"; OIDC users are blocked from the password-reset flowInformation leakage
reset_user_passwordincludedstr(e)break); constant O(n) timingInfrastructure
ws:/wss:for MQTT,data:/blob:for thumbnails)Testing
1608+ passed
Branch based on current
dev. Frontend build files not included.Screenshots
Login page — SSO button

Settings → Authentication → Two-Factor Auth

Settings → Authentication → SSO / OIDC (admin)

🔄 This issue represents a GitHub Pull Request. It cannot be merged through Gitea due to API limitations.