1- from typing import Any , Union , Dict
1+ from typing import Any , Union
22
33from fastapi import APIRouter , Body , Depends , HTTPException
44from fastapi .security import OAuth2PasswordRequestForm
5- from sqlalchemy . orm import Session
5+ from motor . core import AgnosticDatabase
66
77from app import crud , models , schemas
88from app .api import deps
2424 - The user ID or password was incorrect.
2525 - The account does not exist.
2626 - The account is locked or disabled.
27- - Code should go through the same process, no matter what, allowing the application to return in approximately
27+ - Code should go through the same process, no matter what, allowing the application to return in approximately
2828 the same response time.
2929 - In the words of George Orwell, break these rules sooner than do something truly barbaric.
3030
3333
3434
3535@router .post ("/magic/{email}" , response_model = schemas .WebToken )
36- def login_with_magic_link (* , db : Session = Depends (deps .get_db ), email : str ) -> Any :
36+ async def login_with_magic_link (* , db : AgnosticDatabase = Depends (deps .get_db ), email : str ) -> Any :
3737 """
3838 First step of a 'magic link' login. Check if the user exists and generate a magic link. Generates two short-duration
3939 jwt tokens, one for validation, one for email. Creates user if not exist.
4040 """
41- user = crud .user .get_by_email (db , email = email )
41+ user = await crud .user .get_by_email (db , email = email )
4242 if not user :
4343 user_in = schemas .UserCreate (** {"email" : email })
44- user = crud .user .create (db , obj_in = user_in )
44+ user = await crud .user .create (db , obj_in = user_in )
4545 if not crud .user .is_active (user ):
4646 # Still permits a timed-attack, but does create ambiguity.
4747 raise HTTPException (status_code = 400 , detail = "A link to activate your account has been emailed." )
@@ -53,9 +53,9 @@ def login_with_magic_link(*, db: Session = Depends(deps.get_db), email: str) ->
5353
5454
5555@router .post ("/claim" , response_model = schemas .Token )
56- def validate_magic_link (
56+ async def validate_magic_link (
5757 * ,
58- db : Session = Depends (deps .get_db ),
58+ db : AgnosticDatabase = Depends (deps .get_db ),
5959 obj_in : schemas .WebToken ,
6060 magic_in : bool = Depends (deps .get_magic_token ),
6161) -> Any :
@@ -64,7 +64,7 @@ def validate_magic_link(
6464 """
6565 claim_in = deps .get_magic_token (token = obj_in .claim )
6666 # Get the user
67- user = crud .user .get (db , id = magic_in .sub )
67+ user = await crud .user .get (db , id = magic_in .sub )
6868 # Test the claims
6969 if (
7070 (claim_in .sub == magic_in .sub )
@@ -75,15 +75,15 @@ def validate_magic_link(
7575 raise HTTPException (status_code = 400 , detail = "Login failed; invalid claim." )
7676 # Validate that the email is the user's
7777 if not user .email_validated :
78- crud .user .validate_email (db = db , db_obj = user )
78+ await crud .user .validate_email (db = db , db_obj = user )
7979 # Check if totp active
8080 refresh_token = None
8181 force_totp = True
8282 if not user .totp_secret :
8383 # No TOTP, so this concludes the login validation
8484 force_totp = False
8585 refresh_token = security .create_refresh_token (subject = user .id )
86- crud .token .create (db = db , obj_in = refresh_token , user_obj = user )
86+ await crud .token .create (db = db , obj_in = refresh_token , user_obj = user )
8787 return {
8888 "access_token" : security .create_access_token (subject = user .id , force_totp = force_totp ),
8989 "refresh_token" : refresh_token ,
@@ -92,11 +92,13 @@ def validate_magic_link(
9292
9393
9494@router .post ("/oauth" , response_model = schemas .Token )
95- def login_with_oauth2 (db : Session = Depends (deps .get_db ), form_data : OAuth2PasswordRequestForm = Depends ()) -> Any :
95+ async def login_with_oauth2 (
96+ db : AgnosticDatabase = Depends (deps .get_db ), form_data : OAuth2PasswordRequestForm = Depends ()
97+ ) -> Any :
9698 """
9799 First step with OAuth2 compatible token login, get an access token for future requests.
98100 """
99- user = crud .user .authenticate (db , email = form_data .username , password = form_data .password )
101+ user = await crud .user .authenticate (db , email = form_data .username , password = form_data .password )
100102 if not form_data .password or not user or not crud .user .is_active (user ):
101103 raise HTTPException (status_code = 400 , detail = "Login failed; incorrect email or password" )
102104 # Check if totp active
@@ -106,7 +108,7 @@ def login_with_oauth2(db: Session = Depends(deps.get_db), form_data: OAuth2Passw
106108 # No TOTP, so this concludes the login validation
107109 force_totp = False
108110 refresh_token = security .create_refresh_token (subject = user .id )
109- crud .token .create (db = db , obj_in = refresh_token , user_obj = user )
111+ await crud .token .create (db = db , obj_in = refresh_token , user_obj = user )
110112 return {
111113 "access_token" : security .create_access_token (subject = user .id , force_totp = force_totp ),
112114 "refresh_token" : refresh_token ,
@@ -115,9 +117,9 @@ def login_with_oauth2(db: Session = Depends(deps.get_db), form_data: OAuth2Passw
115117
116118
117119@router .post ("/totp" , response_model = schemas .Token )
118- def login_with_totp (
120+ async def login_with_totp (
119121 * ,
120- db : Session = Depends (deps .get_db ),
122+ db : AgnosticDatabase = Depends (deps .get_db ),
121123 totp_data : schemas .WebToken ,
122124 current_user : models .User = Depends (deps .get_totp_user ),
123125) -> Any :
@@ -130,9 +132,9 @@ def login_with_totp(
130132 if not new_counter :
131133 raise HTTPException (status_code = 400 , detail = "Login failed; unable to verify TOTP." )
132134 # Save the new counter to prevent reuse
133- current_user = crud .user .update_totp_counter (db = db , db_obj = current_user , new_counter = new_counter )
135+ current_user = await crud .user .update_totp_counter (db = db , db_obj = current_user , new_counter = new_counter )
134136 refresh_token = security .create_refresh_token (subject = current_user .id )
135- crud .token .create (db = db , obj_in = refresh_token , user_obj = current_user )
137+ await crud .token .create (db = db , obj_in = refresh_token , user_obj = current_user )
136138 return {
137139 "access_token" : security .create_access_token (subject = current_user .id ),
138140 "refresh_token" : refresh_token ,
@@ -141,17 +143,17 @@ def login_with_totp(
141143
142144
143145@router .put ("/totp" , response_model = schemas .Msg )
144- def enable_totp_authentication (
146+ async def enable_totp_authentication (
145147 * ,
146- db : Session = Depends (deps .get_db ),
148+ db : AgnosticDatabase = Depends (deps .get_db ),
147149 data_in : schemas .EnableTOTP ,
148150 current_user : models .User = Depends (deps .get_current_active_user ),
149151) -> Any :
150152 """
151153 For validation of token before enabling TOTP.
152154 """
153155 if current_user .hashed_password :
154- user = crud .user .authenticate (db , email = current_user .email , password = data_in .password )
156+ user = await crud .user .authenticate (db , email = current_user .email , password = data_in .password )
155157 if not data_in .password or not user :
156158 raise HTTPException (status_code = 400 , detail = "Unable to authenticate or activate TOTP." )
157159 totp_in = security .create_new_totp (label = current_user .email , uri = data_in .uri )
@@ -161,39 +163,39 @@ def enable_totp_authentication(
161163 if not new_counter :
162164 raise HTTPException (status_code = 400 , detail = "Unable to authenticate or activate TOTP." )
163165 # Enable TOTP and save the new counter to prevent reuse
164- current_user = crud .user .activate_totp (db = db , db_obj = current_user , totp_in = totp_in )
165- current_user = crud .user .update_totp_counter (db = db , db_obj = current_user , new_counter = new_counter )
166+ current_user = await crud .user .activate_totp (db = db , db_obj = current_user , totp_in = totp_in )
167+ current_user = await crud .user .update_totp_counter (db = db , db_obj = current_user , new_counter = new_counter )
166168 return {"msg" : "TOTP enabled. Do not lose your recovery code." }
167169
168170
169171@router .delete ("/totp" , response_model = schemas .Msg )
170- def disable_totp_authentication (
172+ async def disable_totp_authentication (
171173 * ,
172- db : Session = Depends (deps .get_db ),
174+ db : AgnosticDatabase = Depends (deps .get_db ),
173175 data_in : schemas .UserUpdate ,
174176 current_user : models .User = Depends (deps .get_current_active_user ),
175177) -> Any :
176178 """
177179 Disable TOTP.
178180 """
179181 if current_user .hashed_password :
180- user = crud .user .authenticate (db , email = current_user .email , password = data_in .original )
182+ user = await crud .user .authenticate (db , email = current_user .email , password = data_in .original )
181183 if not data_in .original or not user :
182184 raise HTTPException (status_code = 400 , detail = "Unable to authenticate or deactivate TOTP." )
183- crud .user .deactivate_totp (db = db , db_obj = current_user )
185+ await crud .user .deactivate_totp (db = db , db_obj = current_user )
184186 return {"msg" : "TOTP disabled." }
185187
186188
187189@router .post ("/refresh" , response_model = schemas .Token )
188- def refresh_token (
189- db : Session = Depends (deps .get_db ),
190+ async def refresh_token (
191+ db : AgnosticDatabase = Depends (deps .get_db ),
190192 current_user : models .User = Depends (deps .get_refresh_user ),
191193) -> Any :
192194 """
193195 Refresh tokens for future requests
194196 """
195197 refresh_token = security .create_refresh_token (subject = current_user .id )
196- crud .token .create (db = db , obj_in = refresh_token , user_obj = current_user )
198+ await crud .token .create (db = db , obj_in = refresh_token , user_obj = current_user )
197199 return {
198200 "access_token" : security .create_access_token (subject = current_user .id ),
199201 "refresh_token" : refresh_token ,
@@ -202,8 +204,8 @@ def refresh_token(
202204
203205
204206@router .post ("/revoke" , response_model = schemas .Msg )
205- def revoke_token (
206- db : Session = Depends (deps .get_db ),
207+ async def revoke_token (
208+ db : AgnosticDatabase = Depends (deps .get_db ),
207209 current_user : models .User = Depends (deps .get_refresh_user ),
208210) -> Any :
209211 """
@@ -213,11 +215,11 @@ def revoke_token(
213215
214216
215217@router .post ("/recover/{email}" , response_model = Union [schemas .WebToken , schemas .Msg ])
216- def recover_password (email : str , db : Session = Depends (deps .get_db )) -> Any :
218+ async def recover_password (email : str , db : AgnosticDatabase = Depends (deps .get_db )) -> Any :
217219 """
218220 Password Recovery
219221 """
220- user = crud .user .get_by_email (db , email = email )
222+ user = await crud .user .get_by_email (db , email = email )
221223 if user and crud .user .is_active (user ):
222224 tokens = security .create_magic_tokens (subject = user .id )
223225 if settings .EMAILS_ENABLED :
@@ -227,9 +229,9 @@ def recover_password(email: str, db: Session = Depends(deps.get_db)) -> Any:
227229
228230
229231@router .post ("/reset" , response_model = schemas .Msg )
230- def reset_password (
232+ async def reset_password (
231233 * ,
232- db : Session = Depends (deps .get_db ),
234+ db : AgnosticDatabase = Depends (deps .get_db ),
233235 new_password : str = Body (...),
234236 claim : str = Body (...),
235237 magic_in : bool = Depends (deps .get_magic_token ),
@@ -239,7 +241,7 @@ def reset_password(
239241 """
240242 claim_in = deps .get_magic_token (token = claim )
241243 # Get the user
242- user = crud .user .get (db , id = magic_in .sub )
244+ user = await crud .user .get (db , id = magic_in .sub )
243245 # Test the claims
244246 if (
245247 (claim_in .sub == magic_in .sub )
@@ -251,6 +253,5 @@ def reset_password(
251253 # Update the password
252254 hashed_password = security .get_password_hash (new_password )
253255 user .hashed_password = hashed_password
254- db .add (user )
255- db .commit ()
256+ await user .save ()
256257 return {"msg" : "Password updated successfully." }
0 commit comments