Skip to content

Commit 0e940a3

Browse files
Add pre-commit configuration for code formatting tools (#82)
* chore: add pre-commit configuration for code formatting tools * fix(pre-commit): add args for isort to use black profile * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * chore(pre-commit): ensure newline at end of file in autopep8 hook * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci --------- Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com>
1 parent 969ef81 commit 0e940a3

29 files changed

+3112
-1892
lines changed

.pre-commit-config.yaml

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
repos:
2+
- repo: https://github.com/psf/black
3+
rev: 24.8.0 # Use the latest stable version of Black
4+
hooks:
5+
- id: black
6+
7+
- repo: https://github.com/pycqa/isort
8+
rev: 5.13.2
9+
hooks:
10+
- id: isort
11+
args: [--profile, black]
12+
- repo: https://github.com/hhatto/autopep8
13+
rev: v2.3.1 # Use the latest stable version of autopep8
14+
hooks:
15+
- id: autopep8

backend/app/auth/__init__.py

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,13 @@
11
# Auth module
22
from .routes import router
3-
from .service import auth_service
4-
from .security import verify_token, create_access_token
53
from .schemas import UserResponse
4+
from .security import create_access_token, verify_token
5+
from .service import auth_service
66

7-
__all__ = ["router", "auth_service", "verify_token", "create_access_token", "UserResponse"]
7+
__all__ = [
8+
"router",
9+
"auth_service",
10+
"verify_token",
11+
"create_access_token",
12+
"UserResponse",
13+
]

backend/app/auth/routes.py

Lines changed: 95 additions & 77 deletions
Original file line numberDiff line numberDiff line change
@@ -1,19 +1,33 @@
1-
from fastapi import APIRouter, HTTPException, status, Depends
1+
from datetime import timedelta
2+
23
from 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
818
from 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
1219
from app.config import settings
20+
from fastapi import APIRouter, Depends, HTTPException, status
21+
from fastapi.security import ( # Import OAuth2PasswordRequestForm
22+
OAuth2PasswordRequestForm,
23+
)
1324

1425
router = 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
1731
async 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)
4763
async 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)
90106
async 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)
125142
async 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)
157176
async 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)
201221
async 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)
224244
async 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)
244264
async 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

Comments
 (0)