1- from fastapi import APIRouter , HTTPException , status , Depends
1+ from datetime import timedelta
2+
23from app .auth .schemas import (
3- EmailSignupRequest , EmailLoginRequest , GoogleLoginRequest ,
4- RefreshTokenRequest , PasswordResetRequest , PasswordResetConfirm ,
5- TokenVerifyRequest , AuthResponse , TokenResponse , SuccessResponse ,
6- UserResponse , ErrorResponse
4+ AuthResponse ,
5+ EmailLoginRequest ,
6+ EmailSignupRequest ,
7+ ErrorResponse ,
8+ GoogleLoginRequest ,
9+ PasswordResetConfirm ,
10+ PasswordResetRequest ,
11+ RefreshTokenRequest ,
12+ SuccessResponse ,
13+ TokenResponse ,
14+ TokenVerifyRequest ,
15+ UserResponse ,
716)
17+ from app .auth .security import create_access_token , oauth2_scheme # Import oauth2_scheme
818from app .auth .service import auth_service
9- from app .auth .security import create_access_token , oauth2_scheme # Import oauth2_scheme
10- from fastapi .security import OAuth2PasswordRequestForm # Import OAuth2PasswordRequestForm
11- from datetime import timedelta
1219from app .config import settings
20+ from fastapi import APIRouter , Depends , HTTPException , status
21+ from fastapi .security import ( # Import OAuth2PasswordRequestForm
22+ OAuth2PasswordRequestForm ,
23+ )
1324
1425router = APIRouter (prefix = "/auth" , tags = ["Authentication" ])
1526
16- @router .post ("/token" , response_model = TokenResponse , include_in_schema = False ) # include_in_schema=False to hide from docs if desired, or True to show
27+
28+ @router .post (
29+ "/token" , response_model = TokenResponse , include_in_schema = False
30+ ) # include_in_schema=False to hide from docs if desired, or True to show
1731async def login_for_access_token (form_data : OAuth2PasswordRequestForm = Depends ()):
1832 """
1933 OAuth2 compatible token login, get an access token for future requests.
@@ -24,13 +38,14 @@ async def login_for_access_token(form_data: OAuth2PasswordRequestForm = Depends(
2438 # Note: OAuth2PasswordRequestForm uses 'username' field for the user identifier.
2539 # We'll treat it as email here.
2640 result = await auth_service .authenticate_user_with_email (
27- email = form_data .username , # form_data.username is the email
28- password = form_data .password
41+ email = form_data .username , # form_data.username is the email
42+ password = form_data .password ,
2943 )
3044
3145 access_token = create_access_token (
3246 data = {"sub" : str (result ["user" ]["_id" ])},
33- expires_delta = timedelta (minutes = settings .access_token_expire_minutes )
47+ expires_delta = timedelta (
48+ minutes = settings .access_token_expire_minutes ),
3449 )
3550
3651 return TokenResponse (access_token = access_token , token_type = "bearer" )
@@ -40,233 +55,236 @@ async def login_for_access_token(form_data: OAuth2PasswordRequestForm = Depends(
4055 # It's good practice to log the exception here
4156 raise HTTPException (
4257 status_code = status .HTTP_500_INTERNAL_SERVER_ERROR ,
43- detail = f"Authentication failed: { str (e )} "
58+ detail = f"Authentication failed: { str (e )} " ,
4459 )
4560
61+
4662@router .post ("/signup/email" , response_model = AuthResponse )
4763async def signup_with_email (request : EmailSignupRequest ):
4864 """
4965 Registers a new user using email, password, and name, and returns authentication tokens and user information.
50-
66+
5167 Args:
5268 request: Contains the user's email, password, and name for registration.
53-
69+
5470 Returns:
5571 An AuthResponse with access token, refresh token, and user details.
56-
72+
5773 Raises:
5874 HTTPException: If registration fails or an unexpected error occurs.
5975 """
6076 try :
6177 result = await auth_service .create_user_with_email (
62- email = request .email ,
63- password = request .password ,
64- name = request .name
78+ email = request .email , password = request .password , name = request .name
6579 )
66-
80+
6781 # Create access token
6882 access_token = create_access_token (
6983 data = {"sub" : str (result ["user" ]["_id" ])},
70- expires_delta = timedelta (minutes = settings .access_token_expire_minutes )
84+ expires_delta = timedelta (
85+ minutes = settings .access_token_expire_minutes ),
7186 )
72-
87+
7388 # Convert ObjectId to string for response
7489 result ["user" ]["_id" ] = str (result ["user" ]["_id" ])
75-
90+
7691 return AuthResponse (
7792 access_token = access_token ,
7893 refresh_token = result ["refresh_token" ],
79- user = UserResponse (** result ["user" ])
94+ user = UserResponse (** result ["user" ]),
8095 )
8196 except HTTPException :
8297 raise
8398 except Exception as e :
8499 raise HTTPException (
85100 status_code = status .HTTP_500_INTERNAL_SERVER_ERROR ,
86- detail = f"Registration failed: { str (e )} "
101+ detail = f"Registration failed: { str (e )} " ,
87102 )
88103
104+
89105@router .post ("/login/email" , response_model = AuthResponse )
90106async def login_with_email (request : EmailLoginRequest ):
91107 """
92108 Authenticates a user using email and password credentials.
93-
109+
94110 On successful authentication, returns an access token, refresh token, and user information. Raises an HTTP 500 error if authentication fails due to an unexpected error.
95111 """
96112 try :
97113 result = await auth_service .authenticate_user_with_email (
98- email = request .email ,
99- password = request .password
114+ email = request .email , password = request .password
100115 )
101-
116+
102117 # Create access token
103118 access_token = create_access_token (
104119 data = {"sub" : str (result ["user" ]["_id" ])},
105- expires_delta = timedelta (minutes = settings .access_token_expire_minutes )
120+ expires_delta = timedelta (
121+ minutes = settings .access_token_expire_minutes ),
106122 )
107-
123+
108124 # Convert ObjectId to string for response
109125 result ["user" ]["_id" ] = str (result ["user" ]["_id" ])
110-
126+
111127 return AuthResponse (
112128 access_token = access_token ,
113129 refresh_token = result ["refresh_token" ],
114- user = UserResponse (** result ["user" ])
130+ user = UserResponse (** result ["user" ]),
115131 )
116132 except HTTPException :
117133 raise
118134 except Exception as e :
119135 raise HTTPException (
120136 status_code = status .HTTP_500_INTERNAL_SERVER_ERROR ,
121- detail = f"Login failed: { str (e )} "
137+ detail = f"Login failed: { str (e )} " ,
122138 )
123139
140+
124141@router .post ("/login/google" , response_model = AuthResponse )
125142async def login_with_google (request : GoogleLoginRequest ):
126143 """
127144 Authenticates or registers a user using a Google OAuth ID token.
128-
145+
129146 On success, returns an access token, refresh token, and user information. Raises an HTTP 500 error if Google authentication fails.
130147 """
131148 try :
132149 result = await auth_service .authenticate_with_google (request .id_token )
133-
150+
134151 # Create access token
135152 access_token = create_access_token (
136153 data = {"sub" : str (result ["user" ]["_id" ])},
137- expires_delta = timedelta (minutes = settings .access_token_expire_minutes )
154+ expires_delta = timedelta (
155+ minutes = settings .access_token_expire_minutes ),
138156 )
139-
157+
140158 # Convert ObjectId to string for response
141159 result ["user" ]["_id" ] = str (result ["user" ]["_id" ])
142-
160+
143161 return AuthResponse (
144162 access_token = access_token ,
145163 refresh_token = result ["refresh_token" ],
146- user = UserResponse (** result ["user" ])
164+ user = UserResponse (** result ["user" ]),
147165 )
148166 except HTTPException :
149167 raise
150168 except Exception as e :
151169 raise HTTPException (
152170 status_code = status .HTTP_500_INTERNAL_SERVER_ERROR ,
153- detail = f"Google authentication failed: { str (e )} "
171+ detail = f"Google authentication failed: { str (e )} " ,
154172 )
155173
174+
156175@router .post ("/refresh" , response_model = TokenResponse )
157176async def refresh_token (request : RefreshTokenRequest ):
158177 """
159178 Refreshes JWT tokens using a valid refresh token.
160-
179+
161180 Validates the provided refresh token, issues a new access token and refresh token if valid, and returns them. Raises a 401 error if the refresh token is invalid or revoked.
162-
181+
163182 Returns:
164- A TokenResponse containing the new access and refresh tokens.
183+ A TokenResponse containing the new access and refresh tokens.
165184 """
166185 try :
167- new_refresh_token = await auth_service .refresh_access_token (request .refresh_token )
168-
186+ new_refresh_token = await auth_service .refresh_access_token (
187+ request .refresh_token
188+ )
189+
169190 # Get user from the new refresh token to create access token
170191 from app .database import get_database
192+
171193 db = get_database ()
172- token_record = await db .refresh_tokens .find_one ({
173- "token" : new_refresh_token ,
174- "revoked" : False
175- })
176-
194+ token_record = await db .refresh_tokens .find_one (
195+ {"token" : new_refresh_token , "revoked" : False }
196+ )
197+
177198 if not token_record :
178199 raise HTTPException (
179200 status_code = status .HTTP_401_UNAUTHORIZED ,
180- detail = "Failed to create new tokens"
201+ detail = "Failed to create new tokens" ,
181202 )
182- # Create new access token
203+ # Create new access token
183204 access_token = create_access_token (
184205 data = {"sub" : str (token_record ["user_id" ])},
185- expires_delta = timedelta (minutes = settings .access_token_expire_minutes )
186- )
187-
188- return TokenResponse (
189- access_token = access_token ,
190- refresh_token = new_refresh_token
206+ expires_delta = timedelta (
207+ minutes = settings .access_token_expire_minutes ),
191208 )
209+
210+ return TokenResponse (access_token = access_token , refresh_token = new_refresh_token )
192211 except HTTPException :
193212 raise
194213 except Exception as e :
195214 raise HTTPException (
196215 status_code = status .HTTP_500_INTERNAL_SERVER_ERROR ,
197- detail = f"Token refresh failed: { str (e )} "
216+ detail = f"Token refresh failed: { str (e )} " ,
198217 )
199218
219+
200220@router .post ("/token/verify" , response_model = UserResponse )
201221async def verify_token (request : TokenVerifyRequest ):
202222 """
203223 Verifies an access token and returns the associated user information.
204-
224+
205225 Raises:
206226 HTTPException: If the token is invalid or expired, returns a 401 Unauthorized error.
207227 """
208228 try :
209229 user = await auth_service .verify_access_token (request .access_token )
210-
230+
211231 # Convert ObjectId to string for response
212232 user ["_id" ] = str (user ["_id" ])
213-
233+
214234 return UserResponse (** user )
215235 except HTTPException :
216236 raise
217237 except Exception as e :
218238 raise HTTPException (
219- status_code = status .HTTP_401_UNAUTHORIZED ,
220- detail = "Invalid or expired token"
239+ status_code = status .HTTP_401_UNAUTHORIZED , detail = "Invalid or expired token"
221240 )
222241
242+
223243@router .post ("/password/reset/request" , response_model = SuccessResponse )
224244async def request_password_reset (request : PasswordResetRequest ):
225245 """
226246 Initiates a password reset process by sending a reset link to the provided email address.
227-
247+
228248 Returns:
229249 SuccessResponse: Indicates whether the password reset email was sent if the email exists.
230250 """
231251 try :
232252 await auth_service .request_password_reset (request .email )
233253 return SuccessResponse (
234- success = True ,
235- message = "If the email exists, a reset link has been sent"
254+ success = True , message = "If the email exists, a reset link has been sent"
236255 )
237256 except Exception as e :
238257 raise HTTPException (
239258 status_code = status .HTTP_500_INTERNAL_SERVER_ERROR ,
240- detail = f"Password reset request failed: { str (e )} "
259+ detail = f"Password reset request failed: { str (e )} " ,
241260 )
242261
262+
243263@router .post ("/password/reset/confirm" , response_model = SuccessResponse )
244264async def confirm_password_reset (request : PasswordResetConfirm ):
245265 """
246266 Resets a user's password using a valid password reset token.
247-
267+
248268 Args:
249269 request: Contains the password reset token and the new password.
250-
270+
251271 Returns:
252272 SuccessResponse indicating the password has been reset successfully.
253-
273+
254274 Raises:
255275 HTTPException: If the reset token is invalid or an error occurs during the reset process.
256276 """
257277 try :
258278 await auth_service .confirm_password_reset (
259- reset_token = request .reset_token ,
260- new_password = request .new_password
279+ reset_token = request .reset_token , new_password = request .new_password
261280 )
262281 return SuccessResponse (
263- success = True ,
264- message = "Password has been reset successfully"
282+ success = True , message = "Password has been reset successfully"
265283 )
266284 except HTTPException :
267285 raise
268286 except Exception as e :
269287 raise HTTPException (
270288 status_code = status .HTTP_500_INTERNAL_SERVER_ERROR ,
271- detail = f"Password reset failed: { str (e )} "
289+ detail = f"Password reset failed: { str (e )} " ,
272290 )
0 commit comments