How would you implement 2FA? #1389
Replies: 1 comment 2 replies
-
|
Extending Rewriting the login method in the auth backend has a similar issue as above, but can work a little better because you can add for example a def get_database_strategy(
access_token_db: AccessTokenDatabase[AccessToken] = Depends(get_access_token_db), totp_header: Header
) -> DatabaseStrategy:
return MyDatabaseStrategy(
access_token_db, lifetime_seconds=get_config().ACCESS_TOKEN_EXPIRE_SECONDS, totp=totp_header
)Then you could access it from inside the And finally, both of these options require that you need to somehow send the Here's what I did:
async def authenticate(self, *args, **kwargs) -> Optional[User]:
user = await super().authenticate(*args, **kwargs)
if user and user.is_2fa_enabled:
raise HTTPException(
status_code=status.HTTP_401_UNAUTHORIZED,
detail={
"error": "MFA_REQUIRED",
"mfa_token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...", # TODO
},
)
return userRaising
async def authenticate_mfa(
self, username: str, mfa_token: str, totp_code: str
) -> Optional[User]:
try:
user = await self.get_by_email(username)
except fastapi_users_exceptions.UserNotExists:
return None
if (
user.is_2fa_enabled
and mfa_token == "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9..." # TODO
and totp_code == "123456" # TODO
):
return user
return None
class MFALogin(BaseModel):
username: str
mfa_token: str
totp_code: str
@router.post(
"/login_mfa",
)
async def login_mfa(
request: Request,
user_manager: DBBoundUserManager,
mfa_login: MFALogin,
strategy=Depends(auth_backend.get_strategy),
):
user = await user_manager.authenticate_mfa(
username=mfa_login.username, mfa_token=mfa_login.mfa_token, totp_code=mfa_login.totp_code
)
if user is None or not user.is_active:
raise HTTPException(
status_code=status.HTTP_400_BAD_REQUEST,
detail=ErrorCode.LOGIN_BAD_CREDENTIALS,
)
if not user.is_verified:
raise HTTPException(
status_code=status.HTTP_400_BAD_REQUEST,
detail=ErrorCode.LOGIN_USER_NOT_VERIFIED,
)
response = await auth_backend.login(strategy, user)
await user_manager.on_after_login(user, request, response)
return responseThe above is a replica of the login router found in https://github.com/fastapi-users/fastapi-users/blob/master/fastapi_users/router/auth.py#L44-L69 It seems to be working for me, but I'd love to hear opinions on a better method or if there's some issue with my approach. Ideally, I think fastapi-users should let one override the credentials type (instead of having just |
Beta Was this translation helpful? Give feedback.
Uh oh!
There was an error while loading. Please reload this page.
Uh oh!
There was an error while loading. Please reload this page.
-
Hey all!
One question, if one would try to extend fastapi users for 2FA, how would you do it?
Thanks in advance!
Beta Was this translation helpful? Give feedback.
All reactions