/ backend / dependencies.py
dependencies.py
 1  """
 2  FastAPI dependencies - reusable dependency injection functions.
 3  """
 4  from fastapi import Depends, HTTPException, status, Cookie
 5  from fastapi.security import HTTPBearer, HTTPAuthorizationCredentials
 6  from sqlalchemy.ext.asyncio import AsyncSession
 7  from sqlalchemy import select
 8  
 9  from database import get_db
10  from utils.auth import verify_access_token
11  from models.user import User
12  
13  # HTTP Bearer token scheme (for Authorization header)
14  security = HTTPBearer(auto_error=False)
15  
16  
17  async def get_current_user(
18      db: AsyncSession = Depends(get_db),
19      credentials: HTTPAuthorizationCredentials | None = Depends(security),
20      token: str | None = Cookie(default=None)
21  ) -> User:
22      """
23      Dependency to get the current authenticated user.
24      
25      Checks for JWT token in:
26      1. Authorization header (Bearer token)
27      2. Cookie (http-only cookie named "token")
28      
29      Usage in routes:
30          @router.get("/protected")
31          async def protected_route(current_user: User = Depends(get_current_user)):
32              # current_user is the authenticated User object
33      
34      Raises:
35          HTTPException: 401 if not authenticated
36      """
37      # TODO: Implement authentication check
38      # 
39      # 1. Get token from either credentials.credentials (header) or token (cookie)
40      # 2. If no token found, raise 401
41      # 3. Verify token using verify_access_token()
42      # 4. If invalid, raise 401
43      # 5. Query database for user with that ID
44      # 6. If user not found or not active, raise 401
45      # 7. Return user object
46      #
47      # Example:
48      # token_str = None
49      # if credentials:
50      #     token_str = credentials.credentials
51      # elif token:
52      #     token_str = token
53      # 
54      # if not token_str:
55      #     raise HTTPException(status_code=401, detail="Not authenticated")
56      #
57      # user_id = verify_access_token(token_str)
58      # if not user_id:
59      #     raise HTTPException(status_code=401, detail="Invalid token")
60      #
61      # result = await db.execute(select(User).where(User.id == user_id))
62      # user = result.scalar_one_or_none()
63      #
64      # if not user or not user.active:
65      #     raise HTTPException(status_code=401, detail="User not found")
66      #
67      # return user
68      
69      raise HTTPException(
70          status_code=status.HTTP_401_UNAUTHORIZED,
71          detail="Not authenticated"
72      )
73  
74  
75  async def get_optional_user(
76      db: AsyncSession = Depends(get_db),
77      credentials: HTTPAuthorizationCredentials | None = Depends(security),
78      token: str | None = Cookie(default=None)
79  ) -> User | None:
80      """
81      Optional authentication - returns user if authenticated, None otherwise.
82      Does not raise an exception if not authenticated.
83      
84      Useful for routes that behave differently for authenticated vs anonymous users.
85      """
86      # TODO: Similar to get_current_user but returns None instead of raising exception
87      
88      return None
89