@@ -157,6 +157,7 @@ def _verify_bearer_token(
157157 self , token : str = Depends (oauth2_scheme )
158158 ) -> AuthenticatedEntity :
159159 """Verify the Azure AD JWT token and extract claims"""
160+
160161 try :
161162 # First decode without verification to get the key id (kid)
162163 unverified_headers = jwt .get_unverified_header (token )
@@ -170,15 +171,17 @@ def _verify_bearer_token(
170171 if not signing_key :
171172 raise HTTPException (status_code = 401 , detail = "Invalid token signing key" )
172173
173- # Verify and decode the token
174+ # For v2.0 tokens, 'appid' doesn't exist — 'azp' is used instead.
175+ # Remove "appid" from the 'require' list so v2 tokens won't fail.
174176 options = {
175177 "verify_signature" : True ,
176- "verify_aud" : False , # We'll validate manually
178+ "verify_aud" : False , # We'll validate manually below
177179 "verify_iat" : True ,
178180 "verify_exp" : True ,
179181 "verify_nbf" : True ,
180182 "verify_iss" : True ,
181- "require" : ["exp" , "iat" , "nbf" , "iss" , "sub" , "appid" ],
183+ # "require" the standard claims but NOT "appid"
184+ "require" : ["exp" , "iat" , "nbf" , "iss" , "sub" ],
182185 }
183186
184187 try :
@@ -190,13 +193,25 @@ def _verify_bearer_token(
190193 options = options ,
191194 )
192195
193- # Validate the appid claim instead of audience
194- if payload .get ("appid" ) != self .client_id :
196+ # In v1.0 tokens, client_id => 'appid'
197+ # In v2.0 tokens, client_id => 'azp'
198+ # We consider either one valid, so long as it matches self.client_id
199+ client_id_in_token = payload .get ("appid" ) or payload .get ("azp" )
200+
201+ if not client_id_in_token :
195202 raise HTTPException (
196- status_code = 401 , detail = "Invalid token application ID "
203+ status_code = 401 , detail = "No client ID (appid/azp) in token "
197204 )
198- # validate aud
199- if payload .get ("aud" ) != f"api://{ self .client_id } " :
205+
206+ if client_id_in_token != self .client_id :
207+ raise HTTPException (
208+ status_code = 401 ,
209+ detail = "Invalid token application ID (appid/azp)" ,
210+ )
211+
212+ # Validate the audience
213+ expected_aud = f"api://{ self .client_id } "
214+ if payload .get ("aud" ) != expected_aud :
200215 raise HTTPException (
201216 status_code = 401 , detail = "Invalid token audience"
202217 )
@@ -234,20 +249,34 @@ def _verify_bearer_token(
234249 # Map groups to role
235250 role_name = self .group_mapper .get_role_from_groups (groups )
236251 if not role_name :
252+ self .logger .warning (
253+ f"User { email } is not a member of any authorized groups for Keep" ,
254+ extra = {
255+ "tenant_id" : tenant_id ,
256+ "groups" : groups ,
257+ },
258+ )
237259 raise HTTPException (
238260 status_code = 403 ,
239- detail = "You are using Azure AD but the user is not a member of any authorized groups. You need to be a member of an authorized group to access Keep. " ,
261+ detail = "User not a member of any authorized groups for Keep" ,
240262 )
241263
242- # Validate role has required scopes
264+ # Validate role scopes
243265 role = get_role_by_role_name (role_name )
244266 if not role .has_scopes (self .scopes ):
267+ self .logger .warning (
268+ f"Role { role_name } does not have required permissions" ,
269+ extra = {
270+ "tenant_id" : tenant_id ,
271+ "role" : role_name ,
272+ },
273+ )
245274 raise HTTPException (
246275 status_code = 403 ,
247276 detail = f"Role { role_name } does not have required permissions" ,
248277 )
249278
250- # Auto-provision so we can list users
279+ # Auto-provisioning logic
251280 hashed_token = hashlib .sha256 (token .encode ()).hexdigest ()
252281 if hashed_token not in self .saw_tokens and not user_exists (
253282 tenant_id , email
@@ -258,14 +287,16 @@ def _verify_bearer_token(
258287
259288 if hashed_token not in self .saw_tokens :
260289 update_user_last_sign_in (tenant_id , email )
261- # Add token to seen tokens
262290 self .saw_tokens .add (hashed_token )
291+
263292 return AuthenticatedEntity (tenant_id , email , None , role_name )
264293
265294 except HTTPException :
295+ # Re-raise known HTTP errors
296+ self .logger .exception ("Token validation failed (HTTPException)" )
266297 raise
267- except Exception as e :
268- logger .error ( f "Token validation failed: { str ( e ) } " )
298+ except Exception :
299+ self . logger .exception ( "Token validation failed" )
269300 raise HTTPException (status_code = 401 , detail = "Invalid token" )
270301
271302 def _authorize (self , authenticated_entity : AuthenticatedEntity ) -> None :
0 commit comments