22import os
33
44import jwt
5+ import requests
56from fastapi import Depends , HTTPException
67
78from keep .api .core .config import config
@@ -47,6 +48,12 @@ def __init__(self, scopes: list[str] = []) -> None:
4748 self .jwks_client = jwt .PyJWKClient (self .jwks_url )
4849 logger .info (f"Initialized JWKS client with URL: { self .jwks_url } " )
4950
51+ # Userinfo endpoint for fetching group claims not present in the access token
52+ self .userinfo_url = os .environ .get (
53+ "OKTA_USERINFO_URL" ,
54+ f"{ self .okta_issuer } /v1/userinfo" if self .okta_issuer else None ,
55+ )
56+
5057 # Build group → Keep role mapping from environment variables.
5158 # OKTA_ADMIN_GROUPS, OKTA_NOC_GROUPS, OKTA_WEBHOOK_GROUPS accept
5259 # comma-separated Okta group names.
@@ -64,6 +71,23 @@ def __init__(self, scopes: list[str] = []) -> None:
6471
6572 self .auto_create_user = config ("OKTA_AUTO_CREATE_USER" , default = True , cast = bool )
6673
74+ def _get_userinfo (self , token : str ) -> dict :
75+ """Call Okta's userinfo endpoint to retrieve claims not present in the access token (e.g. groups)."""
76+ if not self .userinfo_url :
77+ return {}
78+ try :
79+ resp = requests .get (
80+ self .userinfo_url ,
81+ headers = {"Authorization" : f"Bearer { token } " },
82+ timeout = 5 ,
83+ )
84+ if resp .status_code == 200 :
85+ return resp .json ()
86+ logger .warning (f"Userinfo endpoint returned { resp .status_code } " )
87+ except Exception :
88+ logger .exception ("Failed to call userinfo endpoint" )
89+ return {}
90+
6791 def _verify_bearer_token (self , token : str = Depends (oauth2_scheme )) -> AuthenticatedEntity :
6892 if not token :
6993 raise HTTPException (status_code = 401 , detail = "No token provided" )
@@ -82,10 +106,20 @@ def _verify_bearer_token(self, token: str = Depends(oauth2_scheme)) -> Authentic
82106 options = {"verify_exp" : True }
83107 )
84108
109+ # Enrich with userinfo claims (groups, name, etc. may not be in the access token)
110+ userinfo = self ._get_userinfo (token )
111+
85112 tenant_id = payload .get ("keep_tenant_id" , "keep" )
86- email = payload .get ("email" ) or payload .get ("sub" ) or payload .get ("preferred_username" )
87- name = payload .get ("name" ) or payload .get ("displayName" ) or email
88- groups = payload .get ("groups" , [])
113+ email = (
114+ payload .get ("email" ) or userinfo .get ("email" )
115+ or payload .get ("sub" ) or payload .get ("preferred_username" )
116+ )
117+ name = (
118+ userinfo .get ("name" ) or userinfo .get ("displayName" )
119+ or payload .get ("name" ) or payload .get ("displayName" )
120+ or email
121+ )
122+ groups = userinfo .get ("groups" , []) or payload .get ("groups" , [])
89123
90124 logger .info (f"Token claims — email={ email } , name={ name } , groups={ groups } , group_mappings={ self .group_mappings } " )
91125
0 commit comments