1+ import json
2+ import logging
3+ from datetime import UTC , datetime
14from typing import Optional
25from uuid import uuid4
6+
37import aiohttp
48from fastapi import HTTPException , Request
5- from fastapi .security import HTTPBearer , HTTPAuthorizationCredentials
9+ from fastapi .security import HTTPAuthorizationCredentials , HTTPBearer
610from jose import ExpiredSignatureError , jwt
7- import json
8- import logging
9- from datetime import UTC , datetime
1011
1112from app .core .config import get_settings
13+ from app .db .session import AsyncSessionLocal
14+ from app .models .domain .user import User
1215
1316# from app.api.dependencies import get_db
1417from app .repositories .implementations .user_repository import UserRepository
1518from app .services .user_service import UserService
16- from app .models .domain .user import User
17- from app .db .session import AsyncSessionLocal
1819
1920logger = logging .getLogger (__name__ )
2021settings = get_settings ()
@@ -76,8 +77,9 @@ async def _get_jwks(self) -> dict:
7677 raise HTTPException (status_code = 500 , detail = "Authentication service unavailable" )
7778 return self .jwks
7879
80+ """
7981 async def _verify_token(self, token: str) -> dict:
80- """Verify JWT token and return payload."""
82+
8183 try:
8284 unverified_header = jwt.get_unverified_header(token)
8385 logger.debug(f"Unverified token header: {json.dumps(unverified_header, indent=2)}")
@@ -94,14 +96,71 @@ async def _verify_token(self, token: str) -> dict:
9496 raise HTTPException(status_code=401, detail="Invalid token key")
9597
9698 payload = jwt.decode(token, rsa_key, algorithms=self.algorithms, audience=self.audience, issuer=self.issuer)
97- logger .debug (f "Decoded token payload: { json . dumps ( payload , indent = 2 ) } " )
99+ logger.debug("Decoded token claim names: %s", sorted(payload.keys()) )
98100 return payload
99101
100102 except ExpiredSignatureError:
101103 raise HTTPException(status_code=401, detail="Token has expired")
102104 except Exception as e:
103105 logger.error(f"Token verification error: {str(e)}")
104106 raise HTTPException(status_code=401, detail="Invalid token")
107+ """
108+
109+ async def _verify_token (self , token : str ) -> dict :
110+ """Verify JWT token locally and return its claims."""
111+ try :
112+ unverified_header = jwt .get_unverified_header (token )
113+ kid = unverified_header .get ("kid" )
114+
115+ if not kid :
116+ raise HTTPException (
117+ status_code = 401 ,
118+ detail = "Token is missing a key identifier" ,
119+ )
120+
121+ jwks = await self ._get_jwks ()
122+ rsa_key = next (
123+ (key for key in jwks .get ("keys" , []) if key .get ("kid" ) == kid ),
124+ None ,
125+ )
126+
127+ if not rsa_key :
128+ raise HTTPException (
129+ status_code = 401 ,
130+ detail = "Invalid token key" ,
131+ )
132+
133+ payload = jwt .decode (
134+ token ,
135+ rsa_key ,
136+ algorithms = self .algorithms ,
137+ audience = self .audience ,
138+ issuer = self .issuer ,
139+ )
140+
141+ # Log field names only, without personal values.
142+ logger .debug (
143+ "Decoded token claim names: %s" ,
144+ sorted (payload .keys ()),
145+ )
146+
147+ return payload
148+
149+ except ExpiredSignatureError as exc :
150+ raise HTTPException (
151+ status_code = 401 ,
152+ detail = "Token has expired" ,
153+ ) from exc
154+
155+ except HTTPException :
156+ raise
157+
158+ except Exception :
159+ logger .exception ("Token verification failed" )
160+ raise HTTPException (
161+ status_code = 401 ,
162+ detail = "Invalid token" ,
163+ )
105164
106165 async def _fetch_user_info (self , access_token : str ) -> dict :
107166 """Fetch additional user info from Auth0."""
@@ -127,8 +186,9 @@ def _generate_username(self, payload: dict) -> str:
127186 return email .split ("@" )[0 ]
128187 return f"user_{ uuid4 ().hex [:8 ]} "
129188
189+ """
130190 async def authenticate_request(self, request: Request) -> User:
131- """Authenticate a request and return the user."""
191+
132192 try:
133193 credentials = await self.security(request)
134194 if not credentials:
@@ -141,6 +201,34 @@ async def authenticate_request(self, request: Request) -> User:
141201 except Exception as e:
142202 logger.error(f"Authentication error: {str(e)}")
143203 raise HTTPException(status_code=401, detail="Authentication failed")
204+ """
205+
206+ async def authenticate_request (self , request : Request ) -> User :
207+ """Authenticate a request and return the user."""
208+ try :
209+ credentials = await self .security (request )
210+ if not credentials :
211+ raise HTTPException (
212+ status_code = 401 ,
213+ detail = "No valid authentication credentials found" ,
214+ )
215+
216+ token = credentials .credentials
217+
218+ # Verify the token locally using the cached Auth0 JWKS.
219+ payload = await self ._verify_token (token )
220+
221+ # Do not call Auth0 /userinfo for every API request.
222+ return await self ._get_or_create_user (payload )
223+
224+ except HTTPException :
225+ raise
226+ except Exception :
227+ logger .exception ("Unexpected authentication error" )
228+ raise HTTPException (
229+ status_code = 401 ,
230+ detail = "Authentication failed" ,
231+ )
144232
145233 async def _get_or_create_user (self , user_data : dict ) -> User :
146234 """Get existing user or create new one."""
0 commit comments