kamau1 commited on
Commit
ec15866
Β·
1 Parent(s): 52d9d84

fix: add missing Response param to all rate-limited auth endpoints, completing slowapi compatibility across OTP and auth flows

Browse files
docs/hflogs/runtimeerror.txt CHANGED
@@ -1,29 +1,43 @@
1
- ===== Application Startup at 2025-11-16 12:16:24 =====
2
 
3
- INFO: Started server process [8]
4
  INFO: Waiting for application startup.
5
- INFO: 2025-11-16T12:16:33 - app.main: ============================================================
6
- INFO: 2025-11-16T12:16:33 - app.main: πŸš€ SwiftOps API v1.0.0 | PRODUCTION
7
- INFO: 2025-11-16T12:16:33 - app.main: ============================================================
8
- INFO: 2025-11-16T12:16:33 - app.main: πŸ“¦ Database:
9
- INFO: 2025-11-16T12:16:37 - app.main: βœ“ Connected | 42 tables | 10 users
10
- INFO: 2025-11-16T12:16:37 - app.main: πŸ’Ύ Cache & Sessions:
11
- INFO: 2025-11-16T12:16:38 - app.services.otp_service: βœ… OTP Service initialized with Redis storage
12
- INFO: 2025-11-16T12:16:38 - app.main: βœ“ Redis: Connected
13
- INFO: 2025-11-16T12:16:38 - app.main: πŸ”Œ External Services:
14
- INFO: 2025-11-16T12:16:39 - app.main: βœ“ Cloudinary: Connected
15
- INFO: 2025-11-16T12:16:39 - app.main: βœ“ Resend: Configured
16
- INFO: 2025-11-16T12:16:39 - app.main: βœ“ WASender: Connected
17
- INFO: 2025-11-16T12:16:39 - app.main: βœ“ Supabase: Connected | 6 buckets
18
- INFO: 2025-11-16T12:16:39 - app.main: ============================================================
19
- INFO: 2025-11-16T12:16:39 - app.main: βœ… Startup complete | Ready to serve requests
20
- INFO: 2025-11-16T12:16:39 - app.main: ============================================================
21
  INFO: Application startup complete.
22
  INFO: Uvicorn running on http://0.0.0.0:7860 (Press CTRL+C to quit)
23
- INFO: 10.16.39.23:21217 - "GET /health HTTP/1.1" 200 OK
24
- INFO: 2025-11-16T12:17:25 - app.services.otp_service: OTP sent via whatsapp for password_reset (storage: redis)
25
- INFO: 2025-11-16T12:17:26 - app.services.audit_service: Audit log created: create on otp by system
26
- INFO: 10.16.39.23:46042 - "POST /api/v1/otp/send-public HTTP/1.1" 500 Internal Server Error
 
 
 
 
 
 
 
 
 
 
 
 
 
 
27
  ERROR: Exception in ASGI application
28
  Traceback (most recent call last):
29
  File "/usr/local/lib/python3.11/site-packages/uvicorn/protocols/http/httptools_impl.py", line 426, in run_asgi
 
1
+ ===== Application Startup at 2025-11-16 12:22:34 =====
2
 
3
+ INFO: Started server process [7]
4
  INFO: Waiting for application startup.
5
+ INFO: 2025-11-16T12:22:44 - app.main: ============================================================
6
+ INFO: 2025-11-16T12:22:44 - app.main: πŸš€ SwiftOps API v1.0.0 | PRODUCTION
7
+ INFO: 2025-11-16T12:22:44 - app.main: ============================================================
8
+ INFO: 2025-11-16T12:22:44 - app.main: πŸ“¦ Database:
9
+ INFO: 2025-11-16T12:22:47 - app.main: βœ“ Connected | 42 tables | 10 users
10
+ INFO: 2025-11-16T12:22:47 - app.main: πŸ’Ύ Cache & Sessions:
11
+ INFO: 2025-11-16T12:22:48 - app.services.otp_service: βœ… OTP Service initialized with Redis storage
12
+ INFO: 2025-11-16T12:22:49 - app.main: βœ“ Redis: Connected
13
+ INFO: 2025-11-16T12:22:49 - app.main: πŸ”Œ External Services:
14
+ INFO: 2025-11-16T12:22:50 - app.main: βœ“ Cloudinary: Connected
15
+ INFO: 2025-11-16T12:22:50 - app.main: βœ“ Resend: Configured
16
+ INFO: 2025-11-16T12:22:50 - app.main: βœ“ WASender: Connected
17
+ INFO: 2025-11-16T12:22:50 - app.main: βœ“ Supabase: Connected | 6 buckets
18
+ INFO: 2025-11-16T12:22:50 - app.main: ============================================================
19
+ INFO: 2025-11-16T12:22:50 - app.main: βœ… Startup complete | Ready to serve requests
20
+ INFO: 2025-11-16T12:22:50 - app.main: ============================================================
21
  INFO: Application startup complete.
22
  INFO: Uvicorn running on http://0.0.0.0:7860 (Press CTRL+C to quit)
23
+ INFO: 10.16.24.211:6043 - "GET /?logs=container HTTP/1.1" 200 OK
24
+ INFO: 10.16.39.23:26777 - "GET /?logs=container HTTP/1.1" 200 OK
25
+ INFO: 2025-11-16T12:23:02 - app.services.otp_service: OTP sent via whatsapp for password_reset (storage: redis)
26
+ INFO: 2025-11-16T12:23:03 - app.services.audit_service: Audit log created: create on otp by system
27
+ INFO: 10.16.6.135:36185 - "POST /api/v1/otp/send-public HTTP/1.1" 200 OK
28
+ INFO: 2025-11-16T12:23:22 - app.services.otp_service: OTP verified successfully for password_reset (storage: redis)
29
+ INFO: 2025-11-16T12:23:24 - app.services.audit_service: Audit log created: update on otp by system
30
+ INFO: 10.16.39.23:57790 - "POST /api/v1/otp/verify-public HTTP/1.1" 200 OK
31
+ INFO: 2025-11-16T12:25:53 - app.services.otp_service: OTP sent via whatsapp for password_reset (storage: redis)
32
+ INFO: 2025-11-16T12:25:54 - app.services.audit_service: Audit log created: create on otp by system
33
+ INFO: 10.16.39.23:17864 - "POST /api/v1/otp/send-public HTTP/1.1" 200 OK
34
+ INFO: 2025-11-16T12:26:08 - app.services.otp_service: OTP verified successfully for password_reset (storage: redis)
35
+ INFO: 2025-11-16T12:26:09 - app.services.audit_service: Audit log created: update on otp by system
36
+ INFO: 10.16.25.32:65235 - "POST /api/v1/otp/verify-public HTTP/1.1" 200 OK
37
+ INFO: 2025-11-16T12:26:11 - app.services.notification_service: Email sent to lesley@example.com: Reset Your SwiftOps Password
38
+ INFO: 2025-11-16T12:26:11 - app.services.password_reset_service: Password reset requested for: lesley@example.com
39
+ INFO: 2025-11-16T12:26:12 - app.services.audit_service: Audit log created: create on auth by system
40
+ INFO: 10.16.39.23:11335 - "POST /api/v1/auth/forgot-password HTTP/1.1" 500 Internal Server Error
41
  ERROR: Exception in ASGI application
42
  Traceback (most recent call last):
43
  File "/usr/local/lib/python3.11/site-packages/uvicorn/protocols/http/httptools_impl.py", line 426, in run_asgi
src/app/api/v1/auth.py CHANGED
@@ -1,7 +1,7 @@
1
  """
2
  Authentication Endpoints - Supabase Auth Integration
3
  """
4
- from fastapi import APIRouter, Depends, HTTPException, status, Request
5
  from sqlalchemy.orm import Session
6
  from app.api.deps import get_db, get_current_active_user
7
  from app.core.rate_limit import limiter
@@ -22,7 +22,7 @@ router = APIRouter(prefix="/auth", tags=["Authentication"])
22
 
23
  @router.post("/register", response_model=TokenResponse, status_code=status.HTTP_201_CREATED)
24
  @limiter.limit("5/hour") # 5 registrations per hour per IP
25
- async def register(request: Request, user_data: UserCreate, db: Session = Depends(get_db)):
26
  """
27
  Register a new user account via Supabase Auth
28
 
@@ -120,7 +120,7 @@ async def register(request: Request, user_data: UserCreate, db: Session = Depend
120
 
121
  @router.post("/login", response_model=TokenResponse)
122
  @limiter.limit("10/minute") # 10 login attempts per minute per IP
123
- async def login(request: Request, credentials: LoginRequest, db: Session = Depends(get_db)):
124
  """
125
  Login with email and password via Supabase Auth
126
 
@@ -296,6 +296,7 @@ async def update_profile(
296
  @limiter.limit("5/hour") # 5 password changes per hour per user
297
  async def change_password(
298
  request: Request,
 
299
  password_data: PasswordChange,
300
  current_user: User = Depends(get_current_active_user),
301
  db: Session = Depends(get_db)
@@ -361,6 +362,7 @@ async def change_password(
361
  @limiter.limit("3/hour") # 3 password reset requests per hour per IP
362
  async def forgot_password(
363
  request: Request,
 
364
  request_data: ForgotPasswordRequest,
365
  db: Session = Depends(get_db)
366
  ):
@@ -396,6 +398,7 @@ async def forgot_password(
396
  @limiter.limit("5/hour") # 5 password reset attempts per hour per IP
397
  async def reset_password(
398
  request: Request,
 
399
  reset_data: ResetPasswordRequest,
400
  db: Session = Depends(get_db)
401
  ):
 
1
  """
2
  Authentication Endpoints - Supabase Auth Integration
3
  """
4
+ from fastapi import APIRouter, Depends, HTTPException, status, Request, Response
5
  from sqlalchemy.orm import Session
6
  from app.api.deps import get_db, get_current_active_user
7
  from app.core.rate_limit import limiter
 
22
 
23
  @router.post("/register", response_model=TokenResponse, status_code=status.HTTP_201_CREATED)
24
  @limiter.limit("5/hour") # 5 registrations per hour per IP
25
+ async def register(request: Request, response: Response, user_data: UserCreate, db: Session = Depends(get_db)):
26
  """
27
  Register a new user account via Supabase Auth
28
 
 
120
 
121
  @router.post("/login", response_model=TokenResponse)
122
  @limiter.limit("10/minute") # 10 login attempts per minute per IP
123
+ async def login(request: Request, response: Response, credentials: LoginRequest, db: Session = Depends(get_db)):
124
  """
125
  Login with email and password via Supabase Auth
126
 
 
296
  @limiter.limit("5/hour") # 5 password changes per hour per user
297
  async def change_password(
298
  request: Request,
299
+ response: Response,
300
  password_data: PasswordChange,
301
  current_user: User = Depends(get_current_active_user),
302
  db: Session = Depends(get_db)
 
362
  @limiter.limit("3/hour") # 3 password reset requests per hour per IP
363
  async def forgot_password(
364
  request: Request,
365
+ response: Response,
366
  request_data: ForgotPasswordRequest,
367
  db: Session = Depends(get_db)
368
  ):
 
398
  @limiter.limit("5/hour") # 5 password reset attempts per hour per IP
399
  async def reset_password(
400
  request: Request,
401
+ response: Response,
402
  reset_data: ResetPasswordRequest,
403
  db: Session = Depends(get_db)
404
  ):
tests/integration/reset-password-otp.js CHANGED
@@ -143,7 +143,13 @@ async function sendOTP() {
143
 
144
  console.log(`${colors.green}βœ“ OTP sent successfully!${colors.reset}`);
145
  console.log(`${colors.dim}Message: ${response.message}${colors.reset}`);
146
- console.log(`${colors.dim}Expires in: ${response.expires_in_seconds} seconds${colors.reset}\n`);
 
 
 
 
 
 
147
 
148
  return true;
149
  } catch (error) {
@@ -165,12 +171,16 @@ async function verifyOTP(code) {
165
  purpose: 'password_reset'
166
  });
167
 
168
- if (response.is_valid) {
169
  console.log(`${colors.green}βœ“ OTP verified successfully!${colors.reset}\n`);
170
  return true;
171
  } else {
172
  console.log(`${colors.red}βœ— Invalid OTP${colors.reset}`);
173
- console.log(`${colors.dim}Message: ${response.message}${colors.reset}\n`);
 
 
 
 
174
  return false;
175
  }
176
  } catch (error) {
 
143
 
144
  console.log(`${colors.green}βœ“ OTP sent successfully!${colors.reset}`);
145
  console.log(`${colors.dim}Message: ${response.message}${colors.reset}`);
146
+ if (response.expires_in_minutes) {
147
+ console.log(`${colors.dim}Expires in: ${response.expires_in_minutes} minutes${colors.reset}`);
148
+ }
149
+ if (response.masked_contact) {
150
+ console.log(`${colors.dim}Sent to: ${response.masked_contact}${colors.reset}`);
151
+ }
152
+ console.log();
153
 
154
  return true;
155
  } catch (error) {
 
171
  purpose: 'password_reset'
172
  });
173
 
174
+ if (response.verified) {
175
  console.log(`${colors.green}βœ“ OTP verified successfully!${colors.reset}\n`);
176
  return true;
177
  } else {
178
  console.log(`${colors.red}βœ— Invalid OTP${colors.reset}`);
179
+ console.log(`${colors.dim}Message: ${response.message}${colors.reset}`);
180
+ if (response.attempts_remaining !== undefined) {
181
+ console.log(`${colors.dim}Attempts remaining: ${response.attempts_remaining}${colors.reset}`);
182
+ }
183
+ console.log();
184
  return false;
185
  }
186
  } catch (error) {