feat(auth): add TOTP 2FA setup and login verification
Some checks failed
CI / test (push) Failing after 21s
Some checks failed
CI / test (push) Failing after 21s
- add user twofa fields and migration - add 2FA setup/enable/disable endpoints - enforce OTP on login when 2FA enabled - add web login OTP field and settings UI
This commit is contained in:
@@ -14,9 +14,13 @@ from app.auth.schemas import (
|
||||
ResetPasswordRequest,
|
||||
TokenResponse,
|
||||
SessionRead,
|
||||
TwoFactorCodeRequest,
|
||||
TwoFactorSetupRead,
|
||||
VerifyEmailRequest,
|
||||
)
|
||||
from app.auth.service import (
|
||||
disable_twofa,
|
||||
enable_twofa,
|
||||
get_current_user,
|
||||
get_email_sender,
|
||||
get_request_metadata,
|
||||
@@ -29,6 +33,7 @@ from app.auth.service import (
|
||||
request_password_reset,
|
||||
resend_verification_email,
|
||||
reset_password,
|
||||
setup_twofa,
|
||||
verify_email,
|
||||
)
|
||||
from app.database.session import get_db
|
||||
@@ -155,3 +160,32 @@ async def revoke_session(jti: str, current_user: User = Depends(get_current_user
|
||||
@router.delete("/sessions", status_code=status.HTTP_204_NO_CONTENT)
|
||||
async def revoke_all_sessions(current_user: User = Depends(get_current_user)) -> None:
|
||||
await revoke_all_user_sessions(user_id=current_user.id)
|
||||
|
||||
|
||||
@router.post("/2fa/setup", response_model=TwoFactorSetupRead)
|
||||
async def setup_2fa(
|
||||
db: AsyncSession = Depends(get_db),
|
||||
current_user: User = Depends(get_current_user),
|
||||
) -> TwoFactorSetupRead:
|
||||
secret, otpauth_url = await setup_twofa(db, current_user)
|
||||
return TwoFactorSetupRead(secret=secret, otpauth_url=otpauth_url)
|
||||
|
||||
|
||||
@router.post("/2fa/enable", response_model=MessageResponse)
|
||||
async def enable_2fa(
|
||||
payload: TwoFactorCodeRequest,
|
||||
db: AsyncSession = Depends(get_db),
|
||||
current_user: User = Depends(get_current_user),
|
||||
) -> MessageResponse:
|
||||
await enable_twofa(db, current_user, code=payload.code)
|
||||
return MessageResponse(message="2FA enabled")
|
||||
|
||||
|
||||
@router.post("/2fa/disable", response_model=MessageResponse)
|
||||
async def disable_2fa(
|
||||
payload: TwoFactorCodeRequest,
|
||||
db: AsyncSession = Depends(get_db),
|
||||
current_user: User = Depends(get_current_user),
|
||||
) -> MessageResponse:
|
||||
await disable_twofa(db, current_user, code=payload.code)
|
||||
return MessageResponse(message="2FA disabled")
|
||||
|
||||
Reference in New Issue
Block a user