Ali2206 commited on
Commit
5c9fe8e
·
verified ·
1 Parent(s): d29fc57

Update core/security.py

Browse files
Files changed (1) hide show
  1. core/security.py +79 -40
core/security.py CHANGED
@@ -1,55 +1,94 @@
1
  from datetime import datetime, timedelta
2
- from fastapi import HTTPException, status, Request
3
- from fastapi.security import HTTPBearer, HTTPAuthorizationCredentials
4
  from jose import jwt, JWTError
 
 
5
  import logging
 
6
 
7
  logger = logging.getLogger(__name__)
8
 
9
- class JWTBearer(HTTPBearer):
10
- def __init__(self, auto_error: bool = True):
11
- super().__init__(auto_error=auto_error)
12
 
13
- async def __call__(self, request: Request):
14
- credentials: HTTPAuthorizationCredentials = await super().__call__(request)
15
- if credentials:
16
- if not credentials.scheme == "Bearer":
17
- raise HTTPException(
18
- status_code=status.HTTP_403_FORBIDDEN,
19
- detail="Invalid authentication scheme"
20
- )
21
- return credentials.credentials
22
- else:
 
 
 
 
 
 
 
 
 
23
  raise HTTPException(
24
- status_code=status.HTTP_403_FORBIDDEN,
25
- detail="Invalid authorization code"
26
  )
27
 
28
- async def get_current_user(request: Request, token: str = Depends(JWTBearer())):
29
- credentials_exception = HTTPException(
30
- status_code=status.HTTP_401_UNAUTHORIZED,
31
- detail="Could not validate credentials",
32
- headers={"WWW-Authenticate": "Bearer"},
33
- )
34
-
35
- try:
36
- # Remove 'Bearer ' prefix if present (double-check)
37
  if token.startswith("Bearer "):
38
  token = token[7:]
39
-
40
- payload = jwt.decode(token, SECRET_KEY, algorithms=[ALGORITHM])
41
- email: str = payload.get("sub")
42
- if email is None:
43
- logger.error("Token missing email in 'sub' claim")
44
- raise credentials_exception
45
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
46
  except JWTError as e:
47
- logger.error(f"JWT validation failed: {str(e)}")
48
- raise credentials_exception
 
 
 
49
 
50
- user = await users_collection.find_one({"email": email})
51
- if user is None:
52
- logger.error(f"User not found for email: {email}")
53
- raise credentials_exception
54
-
55
- return user
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
  from datetime import datetime, timedelta
2
+ from fastapi import HTTPException, status, Request, Depends
3
+ from fastapi.security import OAuth2PasswordBearer
4
  from jose import jwt, JWTError
5
+ from db.mongo import users_collection
6
+ from core.config import SECRET_KEY, ALGORITHM, ACCESS_TOKEN_EXPIRE_MINUTES
7
  import logging
8
+ from typing import Optional
9
 
10
  logger = logging.getLogger(__name__)
11
 
12
+ oauth2_scheme = OAuth2PasswordBearer(tokenUrl="/auth/login")
 
 
13
 
14
+ def create_access_token(data: dict) -> str:
15
+ try:
16
+ to_encode = data.copy()
17
+ expire = datetime.utcnow() + timedelta(minutes=ACCESS_TOKEN_EXPIRE_MINUTES)
18
+ to_encode.update({"exp": expire})
19
+ encoded_jwt = jwt.encode(to_encode, SECRET_KEY, algorithm=ALGORITHM)
20
+ logger.info(f"Token created for {data.get('sub')}")
21
+ return encoded_jwt
22
+ except Exception as e:
23
+ logger.error(f"Token creation failed: {str(e)}")
24
+ raise HTTPException(
25
+ status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
26
+ detail="Token creation failed"
27
+ )
28
+
29
+ async def validate_token(token: str) -> dict:
30
+ try:
31
+ if not token:
32
+ logger.error("Empty token provided")
33
  raise HTTPException(
34
+ status_code=status.HTTP_401_UNAUTHORIZED,
35
+ detail="No token provided"
36
  )
37
 
38
+ # Handle cases where 'Bearer ' prefix might be present
 
 
 
 
 
 
 
 
39
  if token.startswith("Bearer "):
40
  token = token[7:]
41
+ logger.debug("Removed 'Bearer ' prefix from token")
 
 
 
 
 
42
 
43
+ payload = jwt.decode(token, SECRET_KEY, algorithms=[ALGORITHM])
44
+ email = payload.get("sub")
45
+ if not email:
46
+ logger.error("Token missing 'sub' claim")
47
+ raise HTTPException(
48
+ status_code=status.HTTP_401_UNAUTHORIZED,
49
+ detail="Invalid token format"
50
+ )
51
+
52
+ logger.info(f"Token validated for {email}")
53
+ return payload
54
+
55
+ except jwt.ExpiredSignatureError:
56
+ logger.error("Token expired")
57
+ raise HTTPException(
58
+ status_code=status.HTTP_401_UNAUTHORIZED,
59
+ detail="Token expired"
60
+ )
61
  except JWTError as e:
62
+ logger.error(f"Token validation failed: {str(e)}")
63
+ raise HTTPException(
64
+ status_code=status.HTTP_401_UNAUTHORIZED,
65
+ detail="Could not validate credentials"
66
+ )
67
 
68
+ async def get_current_user(request: Request, token: str = Depends(oauth2_scheme)) -> dict:
69
+ logger.info(f"Incoming request headers: {dict(request.headers)}")
70
+ logger.info(f"Raw token received: {token[:15]}...") # Log first 15 chars for security
71
+
72
+ try:
73
+ payload = await validate_token(token)
74
+ email = payload.get("sub")
75
+
76
+ user = await users_collection.find_one({"email": email})
77
+ if not user:
78
+ logger.error(f"User not found in DB: {email}")
79
+ raise HTTPException(
80
+ status_code=status.HTTP_404_NOT_FOUND,
81
+ detail="User not found"
82
+ )
83
+
84
+ logger.info(f"User authenticated: {email}")
85
+ return user
86
+
87
+ except HTTPException:
88
+ raise
89
+ except Exception as e:
90
+ logger.error(f"Unexpected error in get_current_user: {str(e)}")
91
+ raise HTTPException(
92
+ status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
93
+ detail="Internal server error"
94
+ )