Ë
    tPêjÛ� ã                  óˆ  — U d Z ddlmZ ddlZddlZddlZddlZddlZddlZddl	m
Z
mZ ddlmZmZmZmZmZmZ ddlZddlZddlmZ ddlmZ ddlmZ dd	lmZmZ dd
lmZ ddl m!Z! ddl"m#Z# ddl$m%Z% ddl&m'Z'm(Z(m)Z)m*Z*m+Z+m,Z,m-Z-m.Z.m/Z/m0Z0m1Z1m2Z2m3Z3m4Z4m5Z5m6Z6m7Z7m8Z8m9Z9m:Z: ddl;m<Z< ddl=m>Z> ddl?m@Z@mAZA ddlBmCZCmDZD ddlEmFZF ddlGmHZHmIZImJZJmKZKmLZLmMZMmNZNmOZOmPZPmQZQmRZRmSZS  G d„ deT«      ZU G d„ deT«      ZVdZWdeXd<   dZYdeXd<   dZZdeXd <   d!Z[deXd"<   d#Z\deXd$<   d%Z]deXd&<    ed'e)e^z  ¬(«      Z_d.d)„Z` G d*„ d+«      Za G d,„ d-«      Zby)/zŽ
Supports using JWT's for authenticating into the proxy.

Currently only supports admin.

JWT token must have 'litellm_proxy_admin' in scope.
é    )ÚannotationsN)Ú	AwaitableÚCallable)ÚAnyÚFinalÚLiteralÚNoReturnÚTypeVarÚcast)Úx509)Údefault_backend)Úserialization)ÚHTTPExceptionÚstatus)ÚPyJWK)Úverbose_proxy_logger)Úget_nested_value)ÚHTTPHandler)ÚDEFAULT_JWKS_STALE_TTLÚ
RBAC_ROLESÚJWKKeyValueÚJWTAuthBuilderResultÚJWTIssuerConfigÚ
JWTKeyItemÚLiteLLM_EndUserTableÚLiteLLM_JWTAuthÚLiteLLM_OrganizationTableÚLiteLLM_TeamMembershipÚLiteLLM_TeamTableÚLiteLLM_UserTableÚLitellmUserRolesÚMemberÚProxyErrorTypesÚProxyExceptionÚScopeMappingÚSpanÚTeamMemberAddRequestÚUserAPIKeyAuth)Úcan_team_access_model)ÚRouteChecks)ÚUserApiKeyCacheÚget_management_object_ttl)ÚPrismaClientÚProxyLogging)ÚUserRepositoryé   )Ú_allowed_routes_checkÚallowed_routes_checkÚget_actual_routesÚget_end_user_objectÚget_org_objectÚget_org_object_by_aliasÚget_role_based_modelsÚget_role_based_routesÚget_team_membershipÚget_team_objectÚget_team_object_by_aliasÚget_user_objectc                  ó   — e Zd ZdZy)ÚNoMatchingJWTPublicKeyErrorzJRaised when a JWKS endpoint returns no key matching the requested ``kid``.N©Ú__name__Ú
__module__Ú__qualname__Ú__doc__© ó    úb/home/oem/CyberBrain26-unified/.venv/lib/python3.12/site-packages/litellm/proxy/auth/handle_jwt.pyr>   r>   N   s   „ ÚTrE   r>   c                  ó   — e Zd ZdZy)ÚJWKSUnreachableErrorznRaised when an IdP's JWKS / OIDC discovery endpoint is unreachable and no cached copy is left to fall back on.Nr?   rD   rE   rF   rH   rH   R   s   „ ÚxrE   rH   é   r   ÚJWKS_FETCH_ATTEMPTSg      Ð?Ú JWKS_FETCH_RETRY_BACKOFF_SECONDSé   Ú JWKS_UNREACHABLE_BACKOFF_SECONDSÚlitellm_stale_ÚSTALE_CACHE_KEY_PREFIXÚlitellm_stale_written_at_Ú!STALE_WRITTEN_AT_CACHE_KEY_PREFIXÚlitellm_jwks_unreachable_ÚUNREACHABLE_CACHE_KEY_PREFIXÚ_CachedValueT)Úboundc                ó^   — t        d| › �t        j                  dt        j                  ¬«      S )Nz Service Unavailable, the identity provider's JWKS endpoint is temporarily unreachable, so the JWT signature could not be verified. Please retry shortly. Error: ÚNone©ÚmessageÚtypeÚparamÚcode)r$   r#   Úauth_provider_unavailabler   ÚHTTP_503_SERVICE_UNAVAILABLE)Úerrors    rF   Újwks_unavailable_exceptionr`   `   s8   € ÜðeØejÐdkðmô ×6Ñ6ØÜ×0Ñ0ôð rE   c                  óÊ  — e Zd ZU dZded<   ded<   g d¢ZdZdZd	Zd
Z	dZ
dZdZeeee	e
eefZ	 	 dHd„Z	 dI	 	 	 	 	 	 	 	 	 dJd„ZedKd„«       ZedLd„«       ZdMd„ZdMd„ZdNd„ZdOd„ZdPd„ZdQd„ZdQd„ZdRd„ZdSd„ZdSd„ZdRd„ZdRd„ZdTdUd„ZdRd „Z dVd!„Z!dWd"„Z"dVd#„Z#dXd$„Z$dRd%„Z%dRd&„Z&dRd'„Z'dRd(„Z(dQd)„Z)dYd*„Z*dZd+„Z+d[d,„Z,d\d-„Z-d]d.„Z.	 	 	 	 	 	 	 	 	 	 d^d/„Z/	 	 	 	 	 	 	 	 	 	 d^d0„Z0d\d1„Z1	 	 	 	 	 	 	 	 	 	 d_d2„Z2dYd3„Z3d`d4„Z4d`d5„Z5dad6„Z6dbd7„Z7dcd8„Z8ddd9„Z9ded:„Z:dfd;„Z;d<Z<e=dgd=„«       Z>dhd>„Z?did?„Z@djd@„ZAdkdA„ZBdldB„ZC	 	 dm	 	 	 	 	 	 	 dndC„ZD	 	 	 do	 	 	 	 	 	 	 	 	 	 	 	 	 dpdD„ZEdqdE„ZFdfdF„ZGdG„ ZHy)rÚ
JWTHandlerzþ
    - treat the sub id passed in as the user id
    - return an error if id making request doesn't exist in proxy user table
    - track spend against the user id
    - if role="litellm_proxy_user" -> allow making calls + info. Can not edit budgets
    úPrismaClient | NoneÚprisma_clientr+   Úuser_api_key_cache)
ÚRS256ÚRS384ÚRS512ÚPS256ÚPS384ÚPS512ÚES256ÚES384ÚES512ÚEdDSAÚ_litellm_jwt_issuerÚ_litellm_user_idÚ_litellm_user_emailÚ_litellm_team_idÚ_litellm_team_idsÚ_litellm_org_idÚ_litellm_end_user_idc                ó>   — t        «       | _        d| _        i | _        y )Nr   )r   Úhttp_handlerÚleewayÚ_refresh_locks©Úselfs    rF   Ú__init__zJWTHandler.__init__–   s   € ô (›MˆÔØˆŒà79ˆÕrE   c                ó<   — || _         || _        || _        || _        y ©N)rd   re   Úlitellm_jwtauthry   )r|   rd   re   r€   ry   s        rF   Úupdate_environmentzJWTHandler.update_environmentž   s#   € ð +ˆÔØ"4ˆÔØ.ˆÔØˆ�rE   c                óF   — | €y| j                  d«      }t        |«      dk(  S )NFú.rI   )ÚsplitÚlen)ÚtokenÚpartss     rF   Úis_jwtzJWTHandler.is_jwtª   s&   € àˆ=ØØ—{‘{ 3Ó'ˆÜ�5‹z˜Q‰ÐrE   c                ó   — t         j                  | «      sy	 t        j                  | dddœt         j                  ¬«      }t        |t        «      r|S y# t        $ r }t        j                  d|«       Y d}~yd}~ww xY w)zˆ
        Decode JWT claims without signature verification.
        Used for routing decisions before selecting validation path.
        NF)Úverify_signatureÚ
verify_aud)ÚoptionsÚ
algorithmsz6Failed to decode unverified JWT claims for routing: %s)
rb   rˆ   ÚjwtÚdecodeÚSUPPORTED_JWT_ALGORITHMSÚ
isinstanceÚdictÚ	Exceptionr   Údebug)r†   ÚclaimsÚes      rF   Úget_unverified_claimsz JWTHandler.get_unverified_claims±   su   € ô × Ñ  Ô'Øð	ÜŸJ™JØØ-2À%ÑHÜ%×>Ñ>ôˆFô
 ˜&¤$Ô'Ø�ØøÜò 	Ü ×&Ñ&Ð'_ÐabÔcÜûð	ús   ˜:A Á	A=ÁA8Á8A=c                óâ   — | j                   j                  €y| j                  |d¬«      }|syt        |«      }| j                   j                  D ]  }|j                  |v sŒ|j
                  c S  y)a  
        Returns the RBAC role the token 'belongs' to based on role mappings.

        Args:
            token (dict): The JWT token containing role information

        Returns:
            Optional[RBAC_ROLES]: The mapped internal RBAC role if a mapping exists,
                                None otherwise

        Note:
            The function handles both single string roles and lists of roles from the JWT.
            If multiple mappings match the JWT roles, the first matching mapping is returned.
        N©r†   Údefault_value)r€   Úrole_mappingsÚget_jwt_roleÚsetÚroleÚinternal_role)r|   r†   Újwt_roleÚjwt_role_setÚrole_mappings        rF   Ú_rbac_role_from_role_mappingz'JWTHandler._rbac_role_from_role_mappingÇ   sw   € ð ×Ñ×-Ñ-Ð5Øà×+Ñ+°%ÀtÐ+ÓLˆÙØä! (›mˆà ×0Ñ0×>Ñ>ò 	2ˆLà× Ñ  LÒ0Ø#×1Ñ1Ò1ð	2ð
 rE   c                óv  — | j                  |¬«      }| j                  |¬«      }| j                  |d¬«      }|rt        j                  S | j                  |d¬«      �t        j                  S | j                  |d¬«      €|�"| j                  |¬«      rt        j                  S | j                  |¬«      x}r|S y)a^  
        Returns the RBAC role the token 'belongs' to.

        RBAC roles allowed to make requests:
        - PROXY_ADMIN: can make requests to all routes
        - TEAM: can make requests to routes associated with a team
        - INTERNAL_USER: can make requests to routes associated with a user

        Resolves: https://github.com/BerriAI/litellm/issues/6793

        Returns:
        - PROXY_ADMIN: if token is admin
        - TEAM: if token is associated with a team
        - INTERNAL_USER: if token is associated with a user
        - None: if token is not associated with a team or user
        ©r†   ©ÚscopesNr™   )Ú
user_roles)Ú
get_scopesÚis_adminÚget_user_rolesr!   ÚPROXY_ADMINÚget_team_idÚTEAMÚget_user_idÚis_allowed_user_roleÚINTERNAL_USERr£   )r|   r†   r§   rª   r¨   Ú	rbac_roles         rF   Úget_rbac_rolezJWTHandler.get_rbac_roleæ   sÅ   € ð" Ÿ™¨e˜Ó4ˆØŸ-™-¨v˜-Ó6ˆØ ×/Ñ/°eÈ4Ð/ÓPˆ
áÜ#×/Ñ/Ð/Ø×Ñ E¸ÐÓ>ÐJÜ#×(Ñ(Ð(à×Ñ 5¸ÐÓ=ÐIØÐ%Ø×)Ñ)°ZÐ)Ô@ä#×1Ñ1Ð1Ø×;Ñ;À%Ð;ÓHÐHˆYÐHØÐàrE   c                ó6   — | j                   j                  |v ryy)NTF)r€   Úadmin_jwt_scope)r|   r§   s     rF   rª   zJWTHandler.is_admin
  s   € Ø×Ñ×/Ñ/°6Ñ9ØØrE   c                óÄ   ‡— |j                  | j                  «      Št        ‰t        «      r‰syt	        | dd «      }t	        |dd «      xs g }t        ˆfd„|D «       «      S )NFr€   Úissuersc              3  ó<   •K  — | ]  }|j                   ‰k(  –— Œ y ­wr   )Úissuer)Ú.0Úissuer_configr¹   s     €rF   ú	<genexpr>zAJWTHandler._is_trusted_issuer_normalized_token.<locals>.<genexpr>  s   øè ø€ ÒV°m�=×'Ñ'¨6Õ1ÑVùs   ƒ)ÚgetÚLITELLM_JWT_ISSUER_CLAIMr‘   ÚstrÚgetattrÚany)r|   r†   r€   Úissuer_configsr¹   s       @rF   Ú#_is_trusted_issuer_normalized_tokenz.JWTHandler._is_trusted_issuer_normalized_token  sY   ø€ ØŸ	™	 $×"?Ñ"?Ó@ˆÜ˜&¤#Ô&©fØä!(¨Ð/@À$Ó!GˆÜ '¨¸ÀDÓ IÒ OÈRˆÜÓVÀ~ÔVÓVÐVrE   c                ó2   — | j                  |¬«      xr ||v S )Nr¥   )rÃ   )r|   r†   Úclaims      rF   Ú$_has_trusted_issuer_normalized_claimz/JWTHandler._has_trusted_issuer_normalized_claim  s    € Ø×7Ñ7¸eÐ7ÓDÒWÈÐRWÈÐWrE   c                ó@  — | j                  || j                  ¬«      rB|j                  | j                  «      }t        |t        «      r|S t        |t
        «      r|gS g S | j                  j                  �(t        || j                  j                  g ¬«      }|xs g S g S ©N©r†   rÅ   ©ÚdataÚkey_pathÚdefault)	rÆ   ÚLITELLM_TEAM_IDS_CLAIMr½   r‘   Úlistr¿   r€   Úteam_ids_jwt_fieldr   )r|   r†   Úissuer_team_idsÚteam_idss       rF   Úget_team_ids_from_jwtz JWTHandler.get_team_ids_from_jwt  s›   € Ø×4Ñ4¸5È×HcÑHcÐ4ÔdØ%*§Y¡Y¨t×/JÑ/JÓ%KˆOÜ˜/¬4Ô0Ø&Ð&Ü˜/¬3Ô/Ø'Ð(Ð(ð
 ˆIà×Ñ×2Ñ2Ð>Ü0@ØØ×-Ñ-×@Ñ@Øô1ˆHð
 ’>˜rÐ!àˆ	rE   c                óö  — t        | j                  |«      «      }d}| j                  || j                  ¬«      r|j	                  | j                  «      }n8| j
                  j                  �"t        || j
                  j                  d¬«      }|�it        |t         «      r0|D ])  }|€Œt        |«      }|sŒ||vsŒ|j                  |«       Œ+ |S |r't        |«      |vr|j                  t        |«      «       |S )uf  
        Return team IDs from both the plural ``team_ids_jwt_field`` and the
        singular ``team_id_jwt_field`` claim (string or list of strings), as a
        deduplicated list preserving plural-first order.

        Membership-reconciliation paths (SSO callback, JWT-bearer sync) need
        to consider both claim shapes. Reading only the plural field â€” as
        callers historically did â€” silently dropped users whose IdP populates
        the singular field, which is what Okta and Auth0 default to when a
        user has a single primary team.

        This intentionally does NOT consult ``team_id_default``: that fallback
        is a property of how the JWT-bearer auth flow resolves a single
        request-bound team, not of the token's claims. Callers that want the
        default-team behavior should still go through ``get_team_id``.
        NrÉ   rÊ   )rÏ   rÓ   rÆ   ÚLITELLM_TEAM_ID_CLAIMr½   r€   Úteam_id_jwt_fieldr   r‘   r¿   Úappend)r|   r†   rÒ   ÚsingularÚitemÚsids         rF   Úget_all_jwt_team_idszJWTHandler.get_all_jwt_team_ids2  sð   € ô" &*¨$×*DÑ*DÀUÓ*KÓ%LˆØˆØ×4Ñ4¸5È×HbÑHbÐ4ÔcØ—y‘y ×!;Ñ!;Ó<‰HØ×!Ñ!×3Ñ3Ð?Ü'ØØ×-Ñ-×?Ñ?ØôˆHð
 ÐÜ˜(¤DÔ)Ø$ò -�DØ�|Ø Ü˜d›)�CÚ˜s¨(Ò2Ø Ÿ™¨Õ,ð-ð ˆñ œc (›m°8Ñ;Ø—‘¤ H£Ô.ØˆrE   c                ó  — | j                  || j                  ¬«      r|j                  | j                  «      S 	 | j                  j                  �$t        || j                  j                  |¬«      }|S d }	 |S # t        $ r |}Y |S w xY wrÈ   )rÆ   ÚLITELLM_END_USER_ID_CLAIMr½   r€   Úend_user_id_jwt_fieldr   ÚKeyError©r|   r†   rš   Úuser_ids       rF   Úget_end_user_idzJWTHandler.get_end_user_idY  s–   € Ø×4Ñ4¸5È×HfÑHfÐ4ÔgØ—9‘9˜T×;Ñ;Ó<Ð<ð
	$Ø×#Ñ#×9Ñ9ÐEÜ*ØØ!×1Ñ1×GÑGØ)ô�ð ˆð	 ‘ð ˆøô ò 	$Ø#‰Gàˆð	$úó   º8A9 Á4A9 Á9BÂBc                ó^   — | j                   j                  €| j                   j                  €yy)z…
        Returns:
        - True: if 'team_id_jwt_field' or 'team_alias_jwt_field' is set
        - False: if neither is set
        FT)r€   rÖ   Úteam_alias_jwt_fieldr{   s    rF   Úis_required_team_idzJWTHandler.is_required_team_idk  s-   € ð ×Ñ×1Ñ1Ð9¸d×>RÑ>R×>gÑ>gÐ>oØØrE   c                óz   — | j                   j                  �%t        | j                   j                  t        «      ryy)zˆ
        Returns:
        - True: if 'user_allowed_email_domain' is set
        - False: if 'user_allowed_email_domain' is None
        TF)r€   Úuser_allowed_email_domainr‘   r¿   r{   s    rF   Úis_enforced_email_domainz#JWTHandler.is_enforced_email_domainu  s7   € ð ×Ñ×9Ñ9ÐEÌ*Ø× Ñ ×:Ñ:¼CôK
ð ØrE   c                ó°  — | j                  || j                  ¬«      r6|j                  | j                  «      }t        |t        «      r	|r|d   S |S |S 	 | j
                  j                  �¨t        «       }t        || j
                  j                  |¬«      }||u r.| j
                  j                  �| j
                  j                  S |S t        |t        «      r8|s|S t        j                  d| j
                  j                  ||d   «       |d   }|S | j
                  j                  �| j
                  j                  }|S d }	 |S # t        $ r |}Y |S w xY w)NrÉ   r   rÊ   z\JWT Auth: team_id_jwt_field '%s' returned a list %s; using first element '%s' automatically.)rÆ   rÕ   r½   r‘   rÏ   r€   rÖ   Úobjectr   Úteam_id_defaultr   r”   rß   )r|   r†   rš   Úteam_idÚsentinels        rF   r­   zJWTHandler.get_team_id‚  sb  € Ø×4Ñ4¸5È×HbÑHbÐ4ÔcØ—i‘i × :Ñ :Ó;ˆGÜ˜'¤4Ô(Ù%,�w˜q‘zÐ?°-Ð?ØˆNð"	$Ø×#Ñ#×5Ñ5ÐAä"(£(�Ü*ØØ!×1Ñ1×CÑCØ$ô�ð
 ˜hÑ&à×+Ñ+×;Ñ;ÐGØ#×3Ñ3×CÑCÐCà,Ð,ô ˜g¤tÔ,Ù"Ø,Ð,Ü(×.Ñ.ØvØ×,Ñ,×>Ñ>ØØ ™
ô	ð & a™j�GØ�Ø×%Ñ%×5Ñ5ÐAØ×.Ñ.×>Ñ>�ð
 ˆð ‘ð ˆøô ò 	$Ø#‰GØˆð	$ús1   ÁA1E ÃE Ã	E Ã5E Ä,E ÅE ÅEÅEc                ó¦   — 	 | j                   j                  �$t        || j                   j                  |¬«      }|S d}	 |S # t        $ r |}Y |S w xY w)aG  
        Extract team name/alias from JWT token using the configured team_alias_jwt_field.

        Args:
            token: The decoded JWT token dictionary
            default_value: Default value to return if field not found

        Returns:
            The team alias from the token, or default_value if not found
        NrÊ   )r€   rå   r   rß   )r|   r†   rš   Ú
team_aliass       rF   Úget_team_aliaszJWTHandler.get_team_alias®  si   € ð	'Ø×#Ñ#×8Ñ8ÐDÜ-ØØ!×1Ñ1×FÑFØ)ô�
ð
 "Ð!à!‘
ð Ðøô ò 	'Ø&‰JØÐð	'úó   ‚9A ¼A ÁAÁANc                ó8   — |du ry| j                   j                  S )z
        Returns:
        - True: if 'user_id_upsert' is set AND valid_user_email is not False
        - False: if not
        F)r€   Úuser_id_upsert)r|   Úvalid_user_emails     rF   Úis_upsert_user_idzJWTHandler.is_upsert_user_idÇ  s!   € ð ˜uÑ$ØØ×#Ñ#×2Ñ2Ð2rE   c                ó  — | j                  || j                  ¬«      r|j                  | j                  «      S 	 | j                  j                  �$t        || j                  j                  |¬«      }|S |}	 |S # t        $ r |}Y |S w xY wrÈ   )rÆ   ÚLITELLM_USER_ID_CLAIMr½   r€   Úuser_id_jwt_fieldr   rß   rà   s       rF   r¯   zJWTHandler.get_user_idÑ  s–   € Ø×4Ñ4¸5È×HbÑHbÐ4ÔcØ—9‘9˜T×7Ñ7Ó8Ð8ð
	$Ø×#Ñ#×5Ñ5ÐAÜ*ØØ!×1Ñ1×CÑCØ)ô�ð ˆð (‘ð ˆøô ò 	$Ø#‰GØˆð	$úrã   c                ó¦   — 	 | j                   j                  �$t        || j                   j                  |¬«      }|S |}	 |S # t        $ r |}Y |S w xY w)zn
        Returns the user role from the token.

        Set via 'user_roles_jwt_field' in the config.
        rÊ   )r€   Úuser_roles_jwt_fieldr   rß   ©r|   r†   rš   r¨   s       rF   r«   zJWTHandler.get_user_rolesâ  sk   € ð
	'Ø×#Ñ#×8Ñ8ÐDÜ-ØØ!×1Ñ1×FÑFØ)ô�
ð Ðð +‘
ð Ðøô ò 	'Ø&‰JØÐð	'úó   ‚8A ¼A ÁAÁAc                ó  — | j                   j                  sy| j                  |g ¬«      }|sy| j                   j                  D ]9  }|D ]2  }t        j                  ||j                  «      sŒ$|j
                  c c S  Œ; y)z(Map roles from JWT to LiteLLM user rolesNr™   )r€   Újwt_litellm_role_maprœ   Úfnmatchr    Úlitellm_role)r|   r†   Ú	jwt_rolesÚmappingrž   s        rF   Úmap_jwt_role_to_litellm_rolez'JWTHandler.map_jwt_role_to_litellm_roleõ  s~   € à×#Ñ#×8Ò8Øà×,Ñ,°5ÈÐ,ÓKˆ	ÙØà×+Ñ+×@Ñ@ò 	0ˆGØ!ò 0�Ü—?‘? 4¨×)9Ñ)9Õ:Ø"×/Ñ/Ô/ñ0ð	0ð rE   c                ó¦   — 	 | j                   j                  �$t        || j                   j                  |¬«      }|S |}	 |S # t        $ r |}Y |S w xY w)zË
        Generic implementation of `get_user_roles` that can be used for both user and team roles.

        Returns the jwt role from the token.

        Set via 'roles_jwt_field' in the config.
        rÊ   )r€   Úroles_jwt_fieldr   rß   rü   s       rF   rœ   zJWTHandler.get_jwt_role  sk   € ð
	'Ø×#Ñ#×3Ñ3Ð?Ü-ØØ!×1Ñ1×AÑAØ)ô�
ð Ðð +‘
ð Ðøô ò 	'Ø&‰JØÐð	'úrý   c                ó`   ‡ — |�+‰ j                   j                  �t        ˆ fd„|D «       «      ryy)zl
        Returns the user role from the token.

        Set via 'user_allowed_roles' in the config.
        c              3  óN   •K  — | ]  }|‰j                   j                  v –— Œ y ­wr   )r€   Úuser_allowed_roles)rº   rž   r|   s     €rF   r¼   z2JWTHandler.is_allowed_user_role.<locals>.<genexpr>"  s"   øè ø€ Ò[È�D˜D×0Ñ0×CÑCÔCÑ[ùs   ƒ"%TF)r€   r	  rÁ   )r|   r¨   s   ` rF   r°   zJWTHandler.is_allowed_user_role  s2   ø€ ð Ð"Ø×$Ñ$×7Ñ7ÐCÜÓ[ÐPZÔ[Ô[àØrE   c                ó  — | j                  || j                  ¬«      r|j                  | j                  «      S 	 | j                  j                  �$t        || j                  j                  |¬«      }|S d }	 |S # t        $ r |}Y |S w xY wrÈ   )rÆ   ÚLITELLM_USER_EMAIL_CLAIMr½   r€   Úuser_email_jwt_fieldr   rß   )r|   r†   rš   Ú
user_emails       rF   Úget_user_emailzJWTHandler.get_user_email'  s™   € Ø×4Ñ4¸5È×HeÑHeÐ4ÔfØ—9‘9˜T×:Ñ:Ó;Ð;ð
	'Ø×#Ñ#×8Ñ8ÐDÜ-ØØ!×1Ñ1×FÑFØ)ô�
ð Ðð "‘
ð Ðøô ò 	'Ø&‰JØÐð	'úrã   c                ó¦   — 	 | j                   j                  �$t        || j                   j                  |¬«      }|S |}	 |S # t        $ r |}Y |S w xY w)NrÊ   )r€   Úobject_id_jwt_fieldr   rß   )r|   r†   rš   Ú	object_ids       rF   Úget_object_idzJWTHandler.get_object_id8  sk   € ð
	&Ø×#Ñ#×7Ñ7ÐCÜ,ØØ!×1Ñ1×EÑEØ)ô�	ð Ðð *‘	ð Ðøô ò 	&Ø%‰IØÐð	&úrý   c                ó  — | j                  || j                  ¬«      r|j                  | j                  «      S 	 | j                  j                  �$t        || j                  j                  |¬«      }|S d }	 |S # t        $ r |}Y |S w xY wrÈ   )rÆ   ÚLITELLM_ORG_ID_CLAIMr½   r€   Úorg_id_jwt_fieldr   rß   )r|   r†   rš   Úorg_ids       rF   Ú
get_org_idzJWTHandler.get_org_idF  s–   € Ø×4Ñ4¸5È×HaÑHaÐ4ÔbØ—9‘9˜T×6Ñ6Ó7Ð7ð
	#Ø×#Ñ#×4Ñ4Ð@Ü)ØØ!×1Ñ1×BÑBØ)ô�ð ˆð ‘ð ˆøô ò 	#Ø"‰FØˆð	#úrã   c                ó¦   — 	 | j                   j                  �$t        || j                   j                  |¬«      }|S d}	 |S # t        $ r |}Y |S w xY w)aV  
        Extract organization name/alias from JWT token using the configured org_alias_jwt_field.

        Args:
            token: The decoded JWT token dictionary
            default_value: Default value to return if field not found

        Returns:
            The organization alias from the token, or default_value if not found
        NrÊ   )r€   Úorg_alias_jwt_fieldr   rß   )r|   r†   rš   Ú	org_aliass       rF   Úget_org_aliaszJWTHandler.get_org_aliasW  si   € ð	&Ø×#Ñ#×7Ñ7ÐCÜ,ØØ!×1Ñ1×EÑEØ)ô�	ð
 !Ð à ‘	ð Ðøô ò 	&Ø%‰IØÐð	&úrò   c                óâ   — 	 t        |d   t        «      r|d   j                  «       }|S t        |d   t        «      r|d   }|S t	        dt        |d   «      › d�«      ‚# t        $ r g }Y |S w xY w)NÚscopezUnmapped scope type - z. Supported types - list, str.)r‘   r¿   r„   rÏ   r“   rZ   rß   )r|   r†   r§   s      rF   r©   zJWTHandler.get_scopesp  s‰   € ð		Ü˜% ™.¬#Ô.à˜w™×-Ñ-Ó/�ð ˆô ˜E '™N¬DÔ1Ø˜w™�ð
 ˆô  Ð"8¼¸eÀG¹nÓ9MÐ8NÐNlÐ mÓnÐnøÜò 	Ø‰FØˆð	ús   ‚&A ªA ÁA ÁA.Á-A.c              ƒ  ó‚   ‡ ‡K  — d‰vr‰S ‰ j                  d‰› �‰ j                  «       ˆ ˆfd„d¬«      ƒ d{  –—† S 7 Œ­w)aS  
        If url points to an OIDC discovery document (*.well-known/openid-configuration),
        fetch it and return the jwks_uri contained within.  Otherwise return url unchanged.
        This lets JWT_PUBLIC_KEY_URL be set to a well-known discovery endpoint instead of
        requiring operators to manually find the JWKS URL.
        z .well-known/openid-configurationÚlitellm_oidc_discovery_c                 ó&   •— ‰ j                  ‰«      S r   )Ú_fetch_jwks_uri_from_discovery©r|   Úurls   €€rF   ú<lambda>z.JWTHandler._resolve_jwks_url.<locals>.<lambda>Š  s   ø€ ˜D×?Ñ?ÀÓD€ rE   zan OIDC discovery lookup©Ú	cache_keyÚttlÚrefreshÚlog_contextN)Ú_cached_with_stale_fallbackÚ_get_public_key_cache_ttlr"  s   ``rF   Ú_resolve_jwks_urlzJWTHandler._resolve_jwks_url}  sT   ùè ø€ ð .°SÑ8ØˆJà×5Ñ5Ø/°¨uÐ5Ø×.Ñ.Ó0ÜDØ2ð	 6ó 
÷ 
ð 	
ð 
ús   „4?¸=¹?c           
   ƒ  óH  K  — t        dt        «      D ]'  }	 | j                  j                  |«      ƒ d{  –—† c S  	 | j                  j                  |«      ƒ d{  –—† S 7 Œ+# t        j
                  $ r`}t        j                  dt        |«      j                  ||t        |«       t        j                  t        |z  «      ƒ d{  –—†7   Y d}~Œ¾d}~ww xY w7 Œ|# t        j
                  $ r1}t        t        |«      j                  › d|› dt        › d�«      |‚d}~ww xY w­w)zSGET ``url``, retrying transport failures so one IdP blip does not fail the request.r0   Nz6JWT Auth: %s fetching %s (attempt %s/%s), retrying: %sz
 fetching z after z	 attempts)ÚrangerJ   rx   r½   ÚhttpxÚTransportErrorr   ÚwarningrZ   r@   ÚasyncioÚsleeprK   rH   )r|   r#  Úattemptr–   s       rF   Ú_get_with_transient_retriesz&JWTHandler._get_with_transient_retriesŽ  s  è ø€ ä˜QÔ 3Ó4ò 	PˆGðPØ!×.Ñ.×2Ñ2°3Ó7×7Ò7ð	Pð	yØ×*Ñ*×.Ñ.¨sÓ3×3Ð3ð 8ùÜ×'Ñ'ò 	PÜ$×,Ñ,ØLÜ˜“G×$Ñ$ØØÜ'Øôô —m‘mÔ$DÀwÑ$NÓO×OÖOûð	Púð 4ùÜ×#Ñ#ò 	yÜ&¬$¨q«'×*:Ñ*:Ð);¸:ÀcÀUÈ'ÔReÐQfÐfoÐ'pÓqÐwxÐxûð	yüs   ‚D"—A#µA!¶A#¹D"¾C ÁCÁC Á D"Á!A#Á#CÁ6ACÃC	ÃCÃD"ÃCÃD"ÃC ÃDÃ.,DÄDÄD"c              ƒ  ól   K  — | j                   j                  |«      ƒ d {  –—† }t        d|«      S 7 Œ­w)Nú_CachedValueT | None)re   Úasync_get_cacher   ©r|   r&  Úcacheds      rF   Ú_get_cached_valuezJWTHandler._get_cached_value£  s3   è ø€ Ø"×5Ñ5×EÑEÀiÓP×PˆÜÐ*¨FÓ3Ð3ð Qús   ‚4¡2¢4c              ƒ  óš   K  — | j                   j                  |«      ƒ d {  –—† }t        |t        t        f«      rt	        |«      S d S 7 Œ'­wr   )re   r8  r‘   ÚintÚfloatr9  s      rF   Ú_get_cached_timestampz JWTHandler._get_cached_timestamp§  sA   è ø€ Ø"×5Ñ5×EÑEÀiÓP×Pˆä *¨6´C¼°<Ô @Œu�V‹}ÐJÀdÐJð Qús   ‚A¡A	¢(Ac              ƒ  ó\   K  — | j                   j                  |||¬«      ƒ d {  –—†  y 7 Œ­w)N©ÚkeyÚvaluer'  )re   Úasync_set_cache)r|   r&  rC  r'  s       rF   Ú_put_cached_valuezJWTHandler._put_cached_value¬  s(   è ø€ Ø×%Ñ%×5Ñ5¸)È5ÐVYÐ5ÓZ×ZÒZús   ‚",¤*¥,c              ƒ  ó°  K  — | j                  |«      ƒ d{  –—† }|�|S | j                  j                  |t        j                  «       «      }|4 ƒd{  –—†  | j                  |«      ƒ d{  –—† }|�|cddd«      ƒd{  –—†  S | j                  ||||¬«      ƒ d{  –—† cddd«      ƒd{  –—†  S 7 Œ�7 Œb7 ŒK7 Œ97 Œ7 Œ# 1 ƒd{  –—†7  sw Y   yxY w­w)zIRead ``cache_key``, refreshing it through a single-flight lock on a miss.Nr%  )r;  rz   Ú
setdefaultr2  ÚLockÚ_refresh_or_serve_stale)r|   r&  r'  r(  r)  r:  ÚlockÚcached_after_locks           rF   r*  z&JWTHandler._cached_with_stale_fallback¯  sâ   è ø€ ð 59×4JÑ4JÈ9Ó4U×.UˆØÐØˆMà×)Ñ)×4Ñ4°YÄÇÁÃÓOˆØ÷ 	ñ 	ØCG×CYÑCYÐZcÓCd×=dÐØ Ð,Ø(÷	÷ 	ñ 	ð ×5Ñ5Ø#¨°gÈ;ð 6ó ÷ ÷		÷ 	ñ 	ð /Vøð
	øØ=døð	øðøð		ø÷ 	÷ 	ñ 	üs™   ‚C—B5˜<CÁB7ÁCÁCÁ-B9Á.CÁ5CÂB;ÂCÂCÂB=Â CÂ#CÂ/B?Â0CÂ7CÂ9CÂ;CÂ=CÂ?CÃCÃC
ÃCÃCc           
   ƒ  óˆ  K  — | j                  «       }| j                  ||||¬«      ƒ d{  –—† }t        |t        «      s|S |dk  r|‚| j	                  t
        › |› �«      ƒ d{  –—† }| j                  |«      ƒ d{  –—† }||z   }	|�|�||	kD  r|‚t        j                  d|||t        |	|z
  d«      |«       |S 7 Œ�7 ŒX7 ŒA­w)a^  Refresh ``cache_key`` from the IdP, falling back to the last-known-good copy when it is unreachable.

        Signing keys rotate rarely, so a last-known-good key beats failing authentication during an IdP blip.
        How long a key the IdP has since removed stays trusted is bounded by ``public_key_ttl`` +
        ``public_key_stale_ttl`` measured from when the copy was taken, and that bound is enforced here on every
        read rather than baked into the cache entry's own expiry. An operator who lowers ``public_key_stale_ttl``,
        or sets it to 0 to fail closed, is usually doing it mid-incident, and a copy written under the old longer
        setting would otherwise stay servable until it aged out on its own. A copy whose write time cannot be
        established is not servable, so the bound cannot be dodged by losing the timestamp.
        )r&  r'  Ú	stale_ttlr(  Nr   z§JWT Auth: identity provider unreachable, authenticating %s against a stale JWKS copy of %s (last refreshed %.0fs ago, stops being trusted in %.0fs). Refresh failed: %s)
Ú_get_public_key_stale_ttlÚ_refresh_or_record_outager‘   rH   r;  rO   Ú_stale_copy_ager   r1  Úmax)
r|   r&  r'  r(  r)  rM  ÚoutcomeÚstaleÚageÚlifetimes
             rF   rI  z"JWTHandler._refresh_or_serve_staleÄ  sì   è ø€ ð"  ×9Ñ9Ó;ˆ	Ø#×=Ñ=Ø S°IÀwð  >ó  
÷ 
ˆô ˜'Ô#7Ô8ØˆNØ˜Š>ØˆMà37×3IÑ3IÔMcÐLdÐenÐdoÐJpÓ3q×-qˆØ×/Ñ/°	Ó:×:ˆØ 	™/ˆØˆ=˜C˜K¨3°ª>ØˆMÜ×$Ñ$ð[àØØÜ�˜3‘ Ó"Øô	
ð ˆð-
øð .røØ:ús3   ‚)C«B<¬9CÁ%B>Á&CÁ>C Á?>CÂ>CÃ Cc              ƒ  ó†   K  — | j                  t        › |› �«      ƒ d {  –—† }|€d S t        j                  «       |z
  S 7 Œ­wr   )r?  rQ   Útime)r|   r&  Ú
written_ats      rF   rP  zJWTHandler._stale_copy_ageî  sG   è ø€ Ø"&×"<Ñ"<Ô@aÐ?bÐclÐbmÐ=nÓ"o×oˆ
Ø!Ð)ˆtÐG¬t¯y©y«{¸ZÑ/GÐGð pús   ‚AŸ?  Ac              ƒ  ó(  K  — t         › |› �}| j                  |«      ƒ d{  –—† }|�t        |«      S 	  |«       ƒ d{  –—† }| j                  |||¬«      ƒ d{  –—†  |dkD  r`| j                  t        › |› �|||z   ¬«      ƒ d{  –—†  | j                  t        › |› �t        j                  «       ||z   ¬«      ƒ d{  –—†  |S 7 Œ¤7 Œ‰# t        $ r6}| j                  |t	        |«      t
        ¬«      ƒ d{  –—†7   |cY d}~S d}~ww xY w7 Œ±7 Œ‡7 ŒP­w)a   Refresh ``cache_key``, returning the outage as a value rather than raising it.

        A failed refresh is remembered for ``JWKS_UNREACHABLE_BACKOFF_SECONDS`` so a sustained outage costs one
        fetch per window instead of one per request serialised behind the refresh lock.
        N)r&  rC  r'  r   )	rS   r;  rH   rE  r¿   rM   rO   rQ   rW  )	r|   r&  r'  rM  r(  Úunreachable_cache_keyÚrecent_failureÚ	refreshedr–   s	            rF   rO  z$JWTHandler._refresh_or_record_outageò  sL  è ø€ ô +GÐ)GÈ	À{Ð'SÐØ26×2HÑ2HÐI^Ó2_×,_ˆØÐ%Ü'¨Ó7Ð7ð	Ù%,£YŸˆIð ×$Ñ$¨yÀ	ÈsÐ$ÓS×SÐSØ�qŠ=Ø×(Ñ(Ü3Ð4°Y°KÐ@È	ÐWZÐ]fÑWfð )ó ÷ ð ð ×(Ñ(Ü>Ð?À	¸{ÐKÔSW×S\ÑS\ÓS^ÐdgÐjsÑdsð )ó ÷ ð ð Ðð) -`øð
  /ùÜ#ò 	Ø×(Ñ(Ø/´s¸1³vÔCcð )ó ÷ ñ ð �Hûð		úð 	TøðøðúsŠ   ‚D¡C¢D´
C
 ¾C¿C
 ÁDÁDÁ+DÂDÂ8DÂ?DÃ DÃC
 Ã
	D	Ã$DÃ7C:Ã8DÃ>D	Ã?DÄD	Ä	DÄDÄDc              ƒ  ó¶  K  — t        j                  d|«       | j                  |«      ƒ d {  –—† }|j                  dk7  r(t	        d|› d|j                  › d|j
                  › �«      ‚	 |j                  «       }|j                  d«      }|st	        d|› d	�«      ‚t        j                  d
||«       |S 7 Œ‡# t        $ r}t	        d|› d|› �«      ‚d }~ww xY w­w)Nz2JWT Auth: Fetching OIDC discovery document from %séÈ   z"JWT Auth: OIDC discovery endpoint ú returned status ú: z5JWT Auth: Failed to parse OIDC discovery document at Újwks_uriz%JWT Auth: OIDC discovery document at z% does not contain a 'jwks_uri' field.z3JWT Auth: Resolved OIDC discovery %s -> jwks_uri=%s)r   r”   r5  Ústatus_coder“   ÚtextÚjsonr½   )r|   r#  ÚresponseÚ	discoveryr–   ra  s         rF   r!  z)JWTHandler._fetch_jwks_uri_from_discovery  sø   è ø€ Ü×"Ñ"Ð#WÐY\Ô]Ø $× @Ñ @ÀÓ E×EˆØ×Ñ 3Ò&ÜØ4°S°EÐ9JÈ8×K_ÑK_ÐJ`Ð`bÐck×cpÑcpÐbqÐróð ð	`Ø'Ÿ}™}›ˆIð $Ÿ-™-¨
Ó3ˆÙÜÐCÀCÀ5ÐHmÐnÓoÐoä×"Ñ"Ð#XÐZ]Ð_gÔhØˆð Fùô ò 	`ÜÐSÐTWÐSXÐXZÐ[\ÐZ]Ð^Ó_Ð_ûð	`üs3   ‚+C­B5®;CÁ*B7 Á:<CÂ7	CÃ CÃCÃCc                ó:   — t        | dd «      }|€y|j                  S )Nr€   iX  )rÀ   Úpublic_key_ttl©r|   r€   s     rF   r+  z$JWTHandler._get_public_key_cache_ttl(  s&   € Ü!(¨Ð/@À$Ó!GˆØÐ"ØØ×-Ñ-Ð-rE   c                óD   — t        | dd «      }|€t        S |j                  S )Nr€   )rÀ   r   Úpublic_key_stale_ttlri  s     rF   rN  z$JWTHandler._get_public_key_stale_ttl.  s)   € Ü!(¨Ð/@À$Ó!GˆØÐ"Ü)Ð)Ø×3Ñ3Ð3rE   c              ƒ  óˆ  K  — | j                  |«      ƒ d {  –—† }|j                  dk7  r(t        d|› d|j                  › d|j                  › �«      ‚	 |j	                  «       }d|v r|d   n|}t        t        |«      S 7 Œg# t        $ r5}t        j                  d||j                  «       t        d|› d�«      ‚d }~ww xY w­w)	Nr^  zJWT Auth: JWKS endpoint r_  r`  z1Error parsing response: %s. Original Response: %szError parsing response: z*. Check server logs for original response.Úkeys)	r5  rb  r“   rc  rd  r   r_   r   r   )r|   Úresolved_jwks_urlre  Úresponse_jsonr–   rm  s         rF   Ú_fetch_jwks_keyszJWTHandler._fetch_jwks_keys4  sÛ   è ø€ Ø $× @Ñ @ÐARÓ S×SˆØ×Ñ 3Ò&ÜØ*Ð+<Ð*=Ð=NÈx×OcÑOcÐNdÐdfÐgo×gtÑgtÐfuÐvóð ð	fØ#+§=¡=£?ˆMð
 06¸Ñ/F�m FÒ+ÈMˆÜ”K Ó&Ð&ð Tùô ò 	fÜ ×&Ñ&Ð'ZÐ\]Ð_g×_lÑ_lÔmÜÐ6°q°cÐ9cÐdÓeÐeûð	füs3   ‚C—A?˜;CÁB Á$CÂ	B?Â
0B:Â:B?Â?Cc              ƒ  ó  ‡ ‡K  — ‰ j                  |«      ƒ d {  –—† Š‰ j                  d‰› �‰ j                  «       ˆˆ fd„d|› �¬«      ƒ d {  –—† }‰ j                  ||¬«      }|�t	        t
        |«      S t        d‰› d|› �«      ‚7 Œo7 Œ<­w)NÚlitellm_jwt_auth_keys_c                 ó&   •— ‰j                  ‰ «      S r   )rp  )rn  r|   s   €€rF   r$  z:JWTHandler._get_public_key_from_jwks_url.<locals>.<lambda>I  s   ø€ ˜D×1Ñ1Ð2CÓD€ rE   zkid=r%  )rm  Úkidú#No matching public key found. keys=ú, kid=)r,  r*  r+  Ú
parse_keysr   r’   r>   )r|   Újwks_urlrt  rm  Ú
public_keyrn  s   `    @rF   Ú_get_public_key_from_jwks_urlz(JWTHandler._get_public_key_from_jwks_urlD  sª   ùè ø€ Ø)-×)?Ñ)?ÀÓ)I×#IÐØ ×<Ñ<Ø.Ð/@Ð.AÐBØ×.Ñ.Ó0ÜDØ˜s˜e˜ð	 =ó 
÷ 
ˆð !ŸO™O°¸3˜OÓ?ˆ
ØÐ!Üœ˜jÓ)Ð)ä)Ð,OÐPaÐObÐbhÐilÐhmÐ*nÓoÐoð $Jøð
ús!   „B™B	š4BÁBÁ;BÂBc              ƒ  óø  K  — t        j                  d«      }|€t        d«      ‚|j                  d«      D �cg c]#  }|j	                  «       sŒ|j	                  «       ‘Œ% }}|D ]  }	 | j                  ||¬«      ƒ d {  –—† c S  t        d|› d|› �«      ‚c c}w 7 Œ# t        $ r!}t        j                  d||«       Y d }~Œ_d }~wt        $ r(}t        j                  d||«       t        |«      |‚d }~ww xY w­w)	NÚJWT_PUBLIC_KEY_URLz,Missing JWT Public Key URL from environment.ú,©rx  rt  z0JWT Auth: No matching public key found at %s: %sz*JWT Auth: JWKS endpoint %s unreachable: %sru  rv  )ÚosÚgetenvr“   r„   Ústriprz  r>   r   r”   rH   r_   r`   )r|   rt  Úkeys_urlr#  Úkeys_url_listÚkey_urlr–   s          rF   Úget_public_keyzJWTHandler.get_public_keyS  sý   è ø€ ÜŸ)™)Ð$8Ó9ˆàÐÜÐJÓKÐKà7?·~±~ÀcÓ7JÖZ°ÈcÏiÉiÍk §	¡	¥ÐZˆÐZà$ò 	;ˆGð;Ø!×?Ñ?ÈÐVYÐ?ÓZ×ZÒZð	;ô *Ð,OÐP]ÈÐ^dÐehÐdiÐ*jÓkÐkùò  [ð [ùÜ.ò kÜ$×*Ñ*Ð+]Ð_fÐhi×jÑjûÜ'ò ;Ü$×*Ñ*Ð+WÐY`ÐbcÔdÜ0°Ó3¸Ð:ûð;üs^   ‚6C:¸BÁBÁ C:Á(BÁ>BÁ?BÂC:ÂBÂ	C7Â'CÂ>C:ÃC7Ã#C2Ã2C7Ã7C:c                ó�  — d }t        |«      dk(  r\t        |t        «      r|j                  dd «      |k(  s|€|}|S t        |t        «      r|d   j                  dd «      |k(  s|€|d   }|S t        |«      dkD  rK|D ]F  }t        |t        «      r|j                  dd «      }nd }|€Œ+t        |t        «      sŒ<|€Œ?||k(  sŒE|}ŒH |S )Nr0   rt  r   )r…   r‘   r’   r½   rÏ   )r|   rm  rt  ry  rB  Úkey_kids         rF   rw  zJWTHandler.parse_keysf  sÜ   € Ø(,ˆ
Üˆt‹9˜Š>Ü˜$¤Ô%¨4¯8©8°E¸4Ó+@ÀCÒ+GÈ3È;Ø!�
ð Ðô ˜D¤$Ô'¨T°!©W¯[©[¸ÀÓ-EÈÒ-LÐPSÐP[Ø! !™W�
ð Ðô �‹Y˜Š]Øò %�Ü˜c¤4Ô(Ø!Ÿg™g e¨TÓ2‘Gà"�GØ‘?¤z°#´tÕ'<ÀÑATÐY`ÐdgÓYgØ!$‘Jð%ð ÐrE   c                óŽ   — | j                   j                  €y|j                  d«      d   }|| j                   j                  k(  ryy)NTú@éÿÿÿÿF)r€   rè   r„   )r|   r  Úemail_domains      rF   Úis_allowed_domainzJWTHandler.is_allowed_domainx  sF   € Ø×Ñ×9Ñ9ÐAØà(×.Ñ.¨sÓ3°BÑ7ˆØ˜4×/Ñ/×IÑIÒIØàrE   c              ƒ  ó¤  K  — | j                   j                  st        d«      ‚dt        j                  |j                  «       «      j                  «       › �}| j                  j                  |«      ƒ d{  –—† }|�t        j                  d«       |S t        j                  d| j                   j                  «       	 | j                  j                  | j                   j                  d|› �ddœ¬	«      ƒ d{  –—† }|j                  d
k7  r%t        d|j                  › d|j                  › �«      ‚|j                  «       }t        j                  d|«       | j                  j!                  ||| j                   j"                  ¬«      ƒ d{  –—†  |S 7 �Œ7 Œ�7 Œ# t        $ r)}t        j$                  d|«       t        d|› �«      ‚d}~ww xY w­w)aö  
        Fetch user information from OIDC UserInfo endpoint.

        This follows the OpenID Connect protocol where an access token
        is sent to the identity provider's UserInfo endpoint to retrieve
        user identity information.

        Args:
            token: The access token to use for authentication

        Returns:
            dict: User information from the UserInfo endpoint

        Raises:
            Exception: If UserInfo endpoint is not configured or request fails
        zWOIDC UserInfo endpoint not configured. Set 'oidc_userinfo_endpoint' in JWT auth config.Úoidc_userinfo_NzReturning cached OIDC UserInfoz"Calling OIDC UserInfo endpoint: %szBearer zapplication/json)ÚAuthorizationÚAccept)r#  Úheadersr^  z'OIDC UserInfo endpoint returned status r`  zReceived OIDC UserInfo: %srA  z Error fetching OIDC UserInfo: %szFailed to fetch OIDC UserInfo: )r€   Úoidc_userinfo_endpointr“   ÚhashlibÚsha256ÚencodeÚ	hexdigestre   r8  r   r”   rx   r½   rb  rc  rd  rD  Úoidc_userinfo_cache_ttlr_   )r|   r†   r&  Úcached_userinfore  Úuserinfor–   s          rF   Úget_oidc_userinfozJWTHandler.get_oidc_userinfo‚  sÁ  è ø€ ð" ×#Ñ#×:Ò:ÜÐuÓvÐvð ,¬G¯N©N¸5¿<¹<»>Ó,J×,TÑ,TÓ,VÐ+WÐXˆ	Ø'+×'>Ñ'>×'NÑ'NÈyÓ'Y×!YˆàÐ&Ü ×&Ñ&Ð'GÔHØ"Ð"ä×"Ñ"Ð#GÈ×I]ÑI]×ItÑItÔuð	Cà$(×$5Ñ$5×$9Ñ$9Ø×(Ñ(×?Ñ?à'.¨u¨gÐ%6Ø0ñð %:ó %÷ ˆHð ×#Ñ# sÒ*ÜÐ"IÈ(×J^ÑJ^ÐI_Ð_aÐbj×boÑboÐapÐ qÓrÐrà&Ÿm™m›oˆHÜ ×&Ñ&Ð'CÀXÔNð ×)Ñ)×9Ñ9ØØØ×(Ñ(×@Ñ@ð :ó ÷ ð ð ˆOð? "Zùðøðùô ò 	CÜ ×&Ñ&Ð'IÈ1ÔMÜÐ=¸a¸SÐAÓBÐBûð	Cüs\   ‚A4GÁ6FÁ7AGÂ?:F Ã9FÃ:BF ÆFÆF ÆGÆF ÆF Æ	GÆ$$GÇGÇGFc                óâ   — t        j                  d«      }t        j                  d«      }|€*|€(| j                  st        j                  d«       d| _        i }|€d|d<   |€d|d<   |||xs dd	œS )
aà  Build the audience/issuer/options kwargs for ``jwt.decode``.

        Setting ``JWT_AUDIENCE`` (and optionally ``JWT_ISSUER``) turns on the
        corresponding PyJWT verifications, blocking cross-tenant tokens
        minted by other applications that share the same IdP signing keys.
        When both are unset PyJWT only checks the signature and expiry, which
        is preserved for backward compatibility but logged once as a warning.

        The warning fires even in mixed deployments that also configure
        ``LiteLLM_JWTAuth.issuers``: tokens whose ``iss`` does not match any
        configured issuer fall through to this global path, and if env-var
        scoping is absent that fallback is itself unscoped.
        ÚJWT_AUDIENCEÚ
JWT_ISSUERNzâJWT auth is enabled but neither JWT_AUDIENCE nor JWT_ISSUER is configured. Tokens minted by any application that shares the same IdP signing keys will be accepted. Set JWT_AUDIENCE (and ideally JWT_ISSUER) to scope this proxy.TFr‹   Ú
verify_iss)Úaudiencer¹   rŒ   )r  r€  Ú_unscoped_jwt_warning_emittedr   r1  )ÚclsrŸ  r¹   rŒ   s       rF   Ú_build_decode_kwargszJWTHandler._build_decode_kwargs¿  s�   € ô Ÿ)™) NÓ3ˆÜŸ	™	 ,Ó/ˆàÐ  °s×7XÒ7XÜ ×(Ñ(ð@ôð 15ˆCÔ-à!ˆØÐØ$)ˆG�LÑ!Øˆ>Ø$)ˆG�LÑ!ð !ØØ’ $ñ
ð 	
rE   c                óè   — t        | dd «      }|€y |j                  }|sy | j                  |¬«      }|€y |j                  d«      }t	        |t
        «      r|sy |D ]  }|j                  |k(  sŒ|c S  y )Nr€   r¥   Úiss)rÀ   r·   r—   r½   r‘   r¿   r¹   )r|   r†   r€   rÂ   r•   r¹   r»   s          rF   Ú_get_configured_issuerz!JWTHandler._get_configured_issueræ  s‹   € Ü!(¨Ð/@À$Ó!GˆØÐ"Øà /× 7Ñ 7ˆÙØà×2Ñ2¸Ð2Ó?ˆØˆ>ØàŸ
™
 5Ó)ˆÜ˜&¤#Ô&©fØà+ò 	%ˆMØ×#Ñ# vÓ-Ø$Ò$ð	%ð rE   c                ón   — |j                   r|j                   S |j                  j                  d«      › d�S )Nú/z!/.well-known/openid-configuration)rx  r¹   Úrstrip)r|   r»   s     rF   Ú_get_jwks_url_for_issuerz#JWTHandler._get_jwks_url_for_issuerý  s9   € Ø×!Ò!Ø ×)Ñ)Ð)ð  ×&Ñ&×-Ñ-¨cÓ2Ð3Ð3TÐUÐUrE   c                óN   — t        «       }t        |||¬«      }||u s|�|dk(  ry|S )u  Resolve a mapped claim from ``token``.

        Returns ``None`` when the field is absent or empty so that mapped claims
        behave like the global ``litellm_jwtauth`` path â€” present claims override
        the normalised value, missing ones simply leave it ``None``.
        rÊ   NÚ )rë   r   )r|   r†   Úclaim_fieldrî   Úclaim_values        rF   Ú#_get_claim_value_for_issuer_mappingz.JWTHandler._get_claim_value_for_issuer_mapping  s=   € ô !›(ˆÜ-ØØ Øô
ˆð
 ˜(Ñ" kÐ&9¸[ÈBÒ=NØØÐrE   c                ó  — |j                  «       D ��ci c]  \  }}|| j                  vsŒ||“Œ }}}|j                  || j                  <   |j                  | j
                  f|j                  | j                  f|j                  | j                  f|j                  | j                  f|j                  | j                  f|j                  | j                  fg}|D ]#  \  }}|€Œ	| j!                  ||¬«      }	|	€Œ|	||<   Œ% |S c c}}w )N)r†   r¬  )ÚitemsÚLITELLM_INTERNAL_CLAIMSr¹   r¾   rù   rø   r  r  rÖ   rÕ   rÐ   rÎ   r  r  rÞ   rÝ   r®  )
r|   r†   r»   ÚkÚvÚ
normalizedÚclaim_mappingsÚsource_claimÚnormalized_claimr­  s
             rF   Ú_apply_issuer_claim_mappingsz'JWTHandler._apply_issuer_claim_mappings  s"  € Ø49·K±K³M×"k©D¨A¨qÀQÈd×NjÑNjÒEj 1 a¡4Ð"kˆ
Ñ"kØ4A×4HÑ4Hˆ
�4×0Ñ0Ñ1à×,Ñ,¨d×.HÑ.HÐIØ×/Ñ/°×1NÑ1NÐOØ×,Ñ,¨d×.HÑ.HÐIØ×-Ñ-¨t×/JÑ/JÐKØ×+Ñ+¨T×-FÑ-FÐGØ×0Ñ0°$×2PÑ2PÐQð!
ˆð /=ò 	;Ñ*ˆLÐ*ØÐ#ØØ×BÑBØØ(ð Có ˆKð Ñ&Ø/:�
Ð+Ò,ð	;ð Ðùó+ #ls
   ”D«Dc                ó2   — i }dD ]  }||v sŒ||   ||<   Œ |S )N)Úktyrt  Únr–   ÚxÚyÚcrvrD   )r|   ry  ÚjwkrB  s       rF   Ú_get_jwk_from_public_keyz#JWTHandler._get_jwk_from_public_key-  s2   € ØˆØ<ò 	+ˆCØ�jÒ Ø% c™?��C’ð	+ð ˆ
rE   c                óL   — |€|st        d«      ‚i }|€d|d<   |€d|d<   |xs d S )NzAaudience must be provided unless disable_audience_validation=TrueFr‹   rž  )Ú
ValueError)r|   rŸ  r¹   Údisable_audience_validationrŒ   s        rF   Ú_get_decode_optionszJWTHandler._get_decode_options4  sG   € ð ÐÑ$?ÜÐ`ÓaÐaØ!ˆØÐØ$)ˆG�LÑ!Øˆ>Ø$)ˆG�LÑ!ØŠ˜$ÐrE   c           	     óh  — |�|n| j                  |||¬«      }t        |t        «      r_t        j                  | j                  |¬«      «      j                  }t        j                  ||| j                  |||| j                  ¬«      S t        j                  |j                  «       t        «       «      }	|	j                  «       j!                  t"        j$                  j&                  t"        j(                  j*                  «      }
t        j                  ||
| j                  |||| j                  ¬«      S )N)rŸ  r¹   rÃ  )ry  )r�   rŒ   rŸ  r¹   ry   )r�   rŸ  r¹   rŒ   ry   )rÄ  r‘   r’   r   Ú	from_dictrÀ  rB  rŽ   r�   r�   ry   r   Úload_pem_x509_certificater•  r   ry  Úpublic_bytesr   ÚEncodingÚPEMÚPublicFormatÚSubjectPublicKeyInfo)r|   r†   ry  rŸ  r¹   rŒ   rÃ  Údecode_optionsÚpublic_key_objÚcertrB  s              rF   Ú_decode_jwt_with_public_keyz&JWTHandler._decode_jwt_with_public_keyI  s  € ð Ð"ñ à×)Ñ)Ø!ØØ,Gð *ó ð 	ô �j¤$Ô'Ü$)§O¡O°D×4QÑ4QÐ]gÐ4QÓ4hÓ$i×$mÑ$mˆNÜ—:‘:ØØØ×8Ñ8Ø&Ø!ØØ—{‘{ôð ô ×4Ñ4°Z×5FÑ5FÓ5HÌ/ÓJ[Ó\ˆØ—_‘_Ó&×3Ñ3Ü×"Ñ"×&Ñ&Ü×&Ñ&×;Ñ;ó
ˆô �z‰zØØØ×4Ñ4ØØØ"Ø—;‘;ô
ð 	
rE   c              ƒ  óè  K  — 	 | j                  | j                  |¬«      |¬«      ƒ d {  –—† }	 | j	                  |||j
                  |j                  |j                  ¬«      }| j!                  ||¬«      S 7 ŒL# t        $ r}t        |«      |‚d }~ww xY w# t        j                  $ r, t        dt        j                  d t        j                  ¬«      ‚t        $ r}t        d|› �«      ‚d }~ww xY w­w)N)r»   r~  )r†   ry  rŸ  r¹   rÃ  úToken ExpiredrX   úValidation fails: )r†   r»   )rz  r©  rH   r`   rÐ  rŸ  r¹   rÃ  rŽ   ÚExpiredSignatureErrorr$   r#   Úexpired_keyr   ÚHTTP_401_UNAUTHORIZEDr“   r¸  )r|   r†   r»   rt  ry  r–   Úpayloads          rF   Ú_auth_jwt_with_issuerz JWTHandler._auth_jwt_with_issuerw  s  è ø€ ð	7Ø&*×&HÑ&HØ×6Ñ6À]Ð6ÓSØð 'Ió '÷ !ˆJð	6Ø!×=Ñ=ØØ%Ø&×/Ñ/Ø$×+Ñ+Ø,9×,UÑ,Uð >ó ˆGð" ×0Ñ0ØØ'ð 1ó 
ð 	
ð3!ùô $ò 	7Ü,¨QÓ/°QÐ6ûð	7ûô ×(Ñ(ò 	Ü Ø'Ü$×0Ñ0ØÜ×1Ñ1ô	ð ô ò 	6ÜÐ0°°Ð4Ó5Ð5ûð	6üsV   ‚C2„&A9 ªA7«A9 °4B Á$C2Á7A9 Á9	BÂBÂBÂC2ÂAC/ÃC*Ã*C/Ã/C2c              ƒ  óÄ  K  — t        j                  |«      }t        j                  d|«       |j	                  dd «      }| j                  |¬«      }|�| j                  |||¬«      ƒ d {  –—† S | j                  «       }| j                  |¬«      ƒ d {  –—† }|�S	 | j                  |||d   |d   |d   ¬	«      }|j                  «       D ��	ci c]  \  }}	|| j                  vsŒ||	“Œ c}	}S t%        d«      ‚7 ŒŽ7 Œfc c}	}w # t         j                  $ r, t        d
t        j                  d t         j"                  ¬«      ‚t$        $ r}
t%        d|
› �«      ‚d }
~
ww xY w­w)Nz
header: %srt  r¥   )r†   r»   rt  )rt  rŸ  r¹   rŒ   )r†   ry  rŸ  r¹   rŒ   rÒ  rX   rÓ  zInvalid JWT Submitted)rŽ   Úget_unverified_headerr   r”   r½   r¥  rØ  r¢  r…  rÐ  r°  r±  rÔ  r$   r#   rÕ  r   rÖ  r“   )r|   r†   Úheaderrt  r»   Údecode_kwargsry  r×  r²  r³  r–   s              rF   Úauth_jwtzJWTHandler.auth_jwt—  s~  è ø€ Ü×1Ñ1°%Ó8ˆä×"Ñ" <°Ô8à—Z‘Z  tÓ,ˆà#×:Ñ:ÀÐ:ÓGˆØÐ$Ø×3Ñ3ØØ+Øð 4ó ÷ ð ð  $×8Ñ8Ó:ˆà"&×"5Ñ"5¸#Ð"5Ó">×>ˆ
àÐ!ð:Ø!%×!AÑ!AØØ)Ø*¨:Ñ6Ø(¨Ñ2Ø)¨)Ñ4ð "Bó "�ð *1¯©«×b¡  A¸AÀT×EaÑEaÒ<a˜˜1™ÓbÐbô Ð/Ó0Ð0ð?øð ?úó cøä×,Ñ,ò Ü$Ø+Ü(×4Ñ4ØÜ×5Ñ5ô	ð ô ò :ÜÐ"4°Q°CÐ 8Ó9Ð9ûð:üsa   ‚A)E Á+C:Á,)E ÂC<ÂE Â2D ÃC>Ã&C>Ã+D Ã.E Ã<E Ã>D ÄAEÅ
EÅEÅE c              ƒ  óT   K  — | j                   j                  «       ƒ d {  –—†  y 7 Œ­wr   )rx   Úcloser{   s    rF   rß  zJWTHandler.closeÁ  s   è ø€ Ø×Ñ×%Ñ%Ó'×'Ò'ús   ‚( &¡()ÚreturnrW   )r   )
rd   rc   re   r+   r€   r   ry   r=  rà  rW   )r†   ú
str | Nonerà  Úbool)r†   r¿   rà  údict | None)r†   r’   rà  úRBAC_ROLES | None)r§   rÏ   rà  râ  )r†   r’   rà  râ  )r†   r’   rÅ   r¿   rà  râ  )r†   r’   rà  ú	list[str])r†   r’   rš   rá  rà  rá  )rà  râ  r   )rõ   úbool | Nonerà  râ  )r†   r’   rš   úlist[str] | Nonerà  rç  )r†   r’   rà  zLitellmUserRoles | None)r¨   rç  rà  râ  )r#  r¿   rà  r¿   )r#  r¿   rà  zhttpx.Response)r&  r¿   rà  r7  )r&  r¿   rà  zfloat | None)r&  r¿   rC  zJWKKeyValue | str | floatr'  r>  rà  rW   )
r&  r¿   r'  r>  r(  ú&Callable[[], Awaitable[_CachedValueT]]r)  r¿   rà  rT   )
r&  r¿   r'  r>  rM  r>  r(  rè  rà  z$_CachedValueT | JWKSUnreachableError)rà  r>  )rn  r¿   rà  r   )rx  r¿   rt  rá  rà  r’   )rt  rá  rà  r’   )rm  r   rt  rá  rà  zJWTKeyItem | None)r  r¿   rà  râ  )r†   r¿   rà  r’   )rà  r’   )r†   r¿   rà  zJWTIssuerConfig | None)r»   r   rà  r¿   )r†   r’   r¬  r¿   rà  r   )r†   r’   r»   r   rà  r’   )ry  r’   rà  r’   )NF)rŸ  ústr | list[str] | Noner¹   rá  rÃ  râ  rà  rã  )NNF)r†   r¿   ry  z
dict | strrŸ  ré  r¹   rá  rŒ   rã  rÃ  râ  rà  r’   )r†   r¿   r»   r   rt  rá  rà  r’   )Ir@   rA   rB   rC   Ú__annotations__r�   r¾   rø   r  rÕ   rÎ   r  rÝ   r±  r}   r�   Ústaticmethodrˆ   r—   r£   r³   rª   rÃ   rÆ   rÓ   rÛ   râ   ræ   ré   r­   rñ   rö   r¯   r«   r  rœ   r°   r  r  r  r  r©   r,  r5  r;  r?  rE  r*  rI  rP  rO  r!  r+  rN  rp  rz  r…  rw  rŒ  rš  r   Úclassmethodr¢  r¥  r©  r®  r¸  rÀ  rÄ  rÐ  rØ  rÝ  rß  rD   rE   rF   rb   rb   l   s  … ñð 'Ó&Ø'Ó'ò Ðð  5ÐØ.ÐØ4ÐØ.ÐØ0ÐØ,ÐØ 6Ðà ØØ ØØØØ!ðÐð:à	ó:ð ð
à*ð
ð ,ð
ð )ð	
ð
 ð
ð 
ó
ð òó ðð òó ðó*ó>"óHó
WóXóó.%óNó$óó*óXô23óó"ó&óó*óó"óó"ó2ó
ó"yó*4óKó
[ðàðð ðð 8ð	ð
 ðð 
óð*(àð(ð ð(ð 8ð	(ð
 ð(ð 
ó(óTHð!àð!ð ð!ð ð	!ð
 8ð!ð 
.ó!óFó&.ó4ó'ó póló&ó$ó9Cðv %*Ð!àò$
ó ð$
óLó.Vóó"ó0ð "Ø,1ð	à(ðð ðð &*ð	ð
 
óð4 "Ø#Ø,1ð,
àð,
ð ð,
ð )ð	,
ð
 ð,
ð ð,
ð &*ð,
ð 
ó,
ó\
ó@(1óT(rE   rb   c                  óô  — e Zd ZdZe	 	 	 	 	 	 	 	 dd„«       Ze	 	 	 	 	 	 	 	 dd„«       Ze	 	 	 	 	 	 	 	 	 	 dd„«       Ze	 	 	 	 	 	 	 	 	 	 	 	 	 	 dd„«       Ze	 	 d	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 d d„«       Z	e	 	 	 	 	 	 	 	 	 	 	 	 	 	 d!d„«       Z
ed"d	„«       Ze	 d#	 	 	 	 	 	 	 d$d
„«       Zed%d„«       Ze	 d#	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 d&d„«       Ze	 	 	 	 	 	 d'd„«       Ze	 	 	 	 	 	 d(d„«       Ze	 d#	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 d)d„«       Ze	 	 	 	 	 	 	 	 	 	 d*d„«       Ze	 d+	 	 	 	 	 	 	 d,d„«       Ze	 	 	 	 d-d„«       Ze	 d#	 	 	 	 	 	 	 	 	 	 	 d.d„«       Ze	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 d/d„«       Ze	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 d0d„«       Ze	 d#	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 d1d„«       Ze	 	 	 	 	 	 	 	 d2d„«       Zed3d„«       Ze	 	 	 	 	 	 d4d„«       Ze	 	 d	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 d5d„«       Zy)6ÚJWTAuthManagerz7Manages JWT authentication and authorization operationsc           	     ór   — t        | |¬«      }|�|€yt        ||¬«      }|st        dd| › d|› d|› �¬«      ‚y)	zU
        Checks if user is allowed to access the route, based on their role.
        ©r²   Úgeneral_settingsT)Ú
user_routeÚallowed_routesé“  úRole=z not allowed to call route=z. Allowed routes=©rb  Údetail)r8   r1   r   )r²   rñ  ÚrouteÚrole_based_routesÚ
is_alloweds        rF   Úcan_rbac_role_call_routez'JWTAuthManager.can_rbac_role_call_routeÈ  sc   € ô $9À9Ð_oÔ#pÐàÐ$¨¨Øä1ØØ,ô
ˆ
ñ
 ÜØØ˜y˜kÐ)DÀUÀGÐK\Ð]nÐ\oÐpôð ð
 rE   c           	     ó\   — t        | |¬«      }|�|€y||vrt        dd| › d|› d|› �¬«      ‚y)zU
        Checks if user is allowed to access the model, based on their role.
        rð  Trô  rõ  z not allowed to call model=z. Allowed models=rö  )r7   r   )r²   rñ  ÚmodelÚrole_based_modelss       rF   Úcan_rbac_role_call_modelz'JWTAuthManager.can_rbac_role_call_modelã  sW   € ô $9À9Ð_oÔ#pÐØÐ$¨¨ØàÐ)Ñ)ÜØØ˜y˜kÐ)DÀUÀGÐK\Ð]nÐ\oÐpôð ð
 rE   c                óä   — | syg }| D ]9  }|j                   |v sŒ|j                  sŒ|j                  |j                  «       Œ; |j                  d«      }|sy||vrt	        ddd|› d|› �i¬«      ‚y)zE
        Check if scope allows access to the requested model
        Nrý  rô  r_   zmodel=z not allowed. Allowed_models=rö  )r  ÚmodelsÚextendr½   r   )Úscope_mappingsr§   Úrequest_datarñ  Úallowed_modelsÚsmÚrequested_models          rF   Úcheck_scope_based_accessz'JWTAuthManager.check_scope_based_accessø  s�   € ñ Øà "ˆØ ò 	1ˆBØ�x‰x˜6Ò! b§i£iØ×%Ñ% b§i¡iÕ0ð	1ð ".×!1Ñ!1°'Ó!:ˆáØà .Ñ0ÜØØ 6¨/Ð):Ð:WÐXfÐWgÐ!hÐiôð ð 	rE   c              ƒ  óÚ   K  — | j                   j                  du rO|€t        dd¬«      ‚t        j	                  |||j                  d«      ¬«       t        j                  |||¬«       yy­w)	z/Validate RBAC role and model access permissionsTNrô  zjUnmatched token passed in. enforce_rbac is set to True. Token must belong to a proxy admin, team, or user.rö  rý  )r²   rñ  rý  )r²   rñ  rø  )r€   Úenforce_rbacr   rî  rÿ  r½   rû  )Újwt_handlerÚjwt_valid_tokenrñ  r  rø  r²   s         rF   Úcheck_rbac_rolezJWTAuthManager.check_rbac_role  s„   è ø€ ð ×&Ñ&×3Ñ3°tÑ;ØÐ Ü#Ø #ð Hôð ô ×3Ñ3Ø#Ø!1Ø"×&Ñ& wÓ/ð 4ô ô
 ×3Ñ3Ø#Ø!1Øð 4õ ð <ùs   ‚A)A+Nc              ƒ  ó  K  — | j                  |¬«      syt        t        j                  || j                  ¬«      }|s3| j                  j
                  }	t        |	¬«      }
t        d|› d|
› �«      ‚t        ddddd|d||d|d|xs i ¬«      S ­w)	z/Check admin status and route access permissionsr¦   N©Ú	user_rolerò  Úlitellm_proxy_roles)ró  z.Admin not allowed to access this route. Route=z, Allowed Routes=T)Úis_proxy_adminÚteam_objectÚuser_objectÚend_user_objectÚ
org_objectr†   rí   rá   r  Úend_user_idr  Úteam_membershipÚ
jwt_claims)	rª   r2   r!   r¬   r€   Úadmin_allowed_routesr3   r“   r   )r  r§   rø  rá   r  Úapi_keyr  r  rú  ró  Úactual_routess              rF   Úcheck_admin_accessz!JWTAuthManager.check_admin_access1  s°   è ø€ ð ×#Ñ#¨6Ð#Ô2Øä0Ü&×2Ñ2ØØ +× ;Ñ ;ô
ˆ
ñ
 Ø/:×/JÑ/J×/_Ñ/_ˆNÜ#4ÀNÔ#SˆMÜÐLÈUÈGÐSdÐerÐdsÐtÓuÐuä#ØØØØ ØØØØØ!ØØØ Ø&Ò,¨"ô
ð 	
ùs   ‚BBc              ƒ  ó  K  — | j                  |d¬«      }| j                  |d¬«      }|r|�| j                  |¬«      sd}d}|r2	 t        |||||| j                  j
                  ¬«      ƒ d{  –—† }||fS |rWt        j                  d|«       t        |||||¬	«      ƒ d{  –—† }|r'|j                  }t        j                  d
||«       ||fS | j                  «       du rÃ| j                  j                   }
| j                  j"                  }d}|
rd|
v r4|
j%                  dd«      }|d   j'                  «       rV|d   }d|
› d|› d�}nGd|
v rC|
j)                  d«      r2t+        j,                  d|
«      }|r|j/                  d«      }d|
› d|› d�}t1        d|
› d|› d|› �«      ‚||fS 7 �Œ;# t        $ rQ}	|	j                  dk7  s| j                  j                  s‚ t        j                  d||	j                  «       Y d}	~	yd}	~	ww xY w7 �Œg­w)zQFind and validate specific team ID from team_id_jwt_field or team_alias_jwt_fieldNr™   r¥   ©rí   rd   re   Úparent_otel_spanÚproxy_logging_objÚteam_id_upserti”  z4JWT team_id claim '%s' did not resolve to a team: %s©NNz'JWT Auth: Resolving team by alias: '%s')rð   rd   re   r   r!  z2JWT Auth: Resolved team_alias='%s' to team_id='%s'Tr«  rƒ   r0   rŠ  r   z* Hint: dot-notation array indexing (e.g. 'z') is not supported. Use 'uZ   ' instead â€” LiteLLM automatically uses the first element when the field value is a list.ú[ú]z^(\w+)\[(\d+)\]$z Hint: array indexing (e.g. 'z/') is not supported in team_id_jwt_field. Use 'z/No team found in token. Checked team_id field 'z' and team_alias field 'ú'.)r­   rñ   rÛ   r:   r€   r"  r   rb  Úteam_claim_fallbackr   r”   r÷  Úinfor;   rí   ræ   rÖ   rå   ÚrsplitÚisdigitÚendswithÚreÚmatchÚgroupr“   )r  r  rd   re   r   r!  Úindividual_team_idrð   r  r–   Úteam_id_fieldÚteam_alias_fieldÚhintr‡   Ú
base_fieldÚms                   rF   Ú"find_and_validate_specific_team_idz1JWTAuthManager.find_and_validate_specific_team_idZ  s”  è ø€ ð )×4Ñ4¸?ÐZ^Ð4Ó_ÐØ'×6Ñ6¸_Ð\`Ð6Óaˆ
ñ Ø"Ð.Ø×4Ñ4¸?Ð4ÔKà!%Ðà04ˆáð"Ü$3Ø.Ø"/Ø'9Ø%5Ø&7Ø#.×#>Ñ#>×#MÑ#Mô%÷ �ð *¨;Ð6Ð6ñ Ü ×%Ñ%Ð&OÐQ[Ô\Ü 8Ø%Ø+Ø#5Ø!1Ø"3ô!÷ ˆKñ Ø%0×%8Ñ%8Ð"Ü$×)Ñ)ØHÈ*ÐVhôð *¨;Ð6Ð6ð ×*Ñ*Ó,°Ñ4Ø#.×#>Ñ#>×#PÑ#PˆMØ&1×&AÑ&A×&VÑ&VÐØˆDÙà˜-Ñ'Ø#0×#7Ñ#7¸¸QÓ#?�EØ˜R‘y×(Ñ(Ô*Ø%*¨1¡X˜
àHÈÈð X/Ø/9¨lð ;UðVñ ð ˜MÑ)¨m×.DÑ.DÀSÔ.IÜ!Ÿx™xÐ(;¸]ÓK�AÙØ%&§W¡W¨Q£Z˜
à;¸M¸?ð K:Ø:D¸ð Fcðdð ô
 ØAÀ-ÀÐPhÐiyÐhzÐz|ð  ~Bð  }Cð  Dóð ð " ;Ð.Ð.ðEúô !ò 	"Ø—=‘= CÒ'¨{×/JÑ/J×/^Ò/^Øä$×*Ñ*ØJØ&Ø—H‘Hôô
 "ûð	"úðûsP   ‚AHÁ(F, Á.F)Á/F, Á6,HÂ"H	Â#DHÆ)F, Æ,	HÆ5AHÇ<HÈHÈHc                ó@   — | j                  |¬«      }t        |«      }|S )z8Get combined team IDs from groups and individual team_idr¥   )rÓ   r�   )r  r  Úteam_ids_from_groupsÚall_team_idss       rF   Úget_all_team_idszJWTAuthManager.get_all_team_ids»  s)   € ð '2×&GÑ&GÈoÐ&GÓ&^Ðä!Ð"6Ó7ˆàÐrE   c                óÜ   — t        |t        «      r|j                  «       nd }t        j                  ||¬«      syt        j
                  |t        | r| j                  xs i ni ¬«      ¬«      S )N©rø  ÚmethodT)Úteam_metadata)rø  Úuser_api_key_dict)r‘   r¿   Úupperr*   Ú#is_auth_enforced_pass_through_routeÚcheck_passthrough_route_accessr(   Úmetadata)r  rø  Úrequest_methodÚnormalized_request_methods       rF   Ú"_team_has_passthrough_route_accessz1JWTAuthManager._team_has_passthrough_route_accessÄ  sn   € ô FPÐP^Ô`cÔEd¨>×+?Ñ+?Ô+AÐjnÐ!Ü×>Ñ>ØØ,õ
ð ô ×9Ñ9ØÜ,Ñ[f¸K×<PÑ<PÒ<VÑTVÐlnÔoô
ð 	
rE   c                ó$   — t        dd| › d�¬«      ‚)Nrô  z-Team not allowed to access passthrough route z5. Configure `allowed_passthrough_routes` on the team.rö  ©r   ©rø  s    rF   Ú$_raise_team_passthrough_route_denialz3JWTAuthManager._raise_team_passthrough_route_denialØ  s&   € äØà?À¸wð GFð Fô
ð 	
rE   c	              ƒ  óâ  K  — ddl m}	 d}
| s:|j                  j                  r#|j                  j                  st        dd¬«      ‚yd}| D ]Æ  }	 t        |||||¬«      ƒ d	{  –—† }|�d
}|r¥|j                  �™|j                  }t        |t        «      r}|rt        |||	d	¬«      ƒ d	{  –—† rdt        t        j                  ||j                  ¬«      }|rt        j                  |||¬«      sd}d
}
t!        j"                  d|||«       |r||fc S ŒÈ |
rt        j'                  |¬«       |r,|s|j                  j(                  st        dd|› d| › d�¬«      ‚y7 Œø7 Œ³# t$        $ r Y �Œ w xY w­w)z2Find first team with access to the requested modelr   ©Ú
llm_routerFrô  zgNo teams found in token. `enforce_team_based_model_access` is set to True. Token must belong to a team.rö  r#  )rí   rd   re   r   r!  NT©rý  r  rL  Úteam_model_aliasesr  ©r  rø  rC  z9JWT team route check: team_id=%s, route=%s, is_allowed=%srH  z+No team has access to the requested model: z. Checked teams=z.. Check `/models` to see all available models.)Úlitellm.proxy.proxy_serverrL  r€   Úenforce_team_based_model_accessÚfallback_to_db_teamsr   r:   r  r‘   rÏ   r)   r2   r!   r®   rî  rE  r   r”   r“   rI  r'  )rÒ   r  rø  r  rd   re   r   r!  rC  rL  Ú'denied_auth_enforced_pass_through_routeÚany_claim_team_resolvedrí   r  Úteam_modelsrú  s                   rF   Úfind_team_with_model_accessz*JWTAuthManager.find_team_with_model_accessâ  sÏ  è ø€ õ 	:à27Ð/áà×+Ñ+×KÒKØ#×3Ñ3×HÒHä#Ø #ð Eôð ð à"'ÐØò *	ˆGð)Ü$3Ø#Ø"/Ø'9Ø%5Ø&7ô%÷ �ð Ð*Ø.2Ð+á ;×#5Ñ#5Ð#AØ"-×"4Ñ"4�KÜ! +¬tÔ4Ù+Ü!6Ø"1Ø(3Ø'1Ø/3ô	"÷ ð ô &:Ü&6×&;Ñ&;Ø',Ø0;×0KÑ0Kô&˜
ñ
 &¬n×._Ñ._Ø(3Ø"'Ø+9ð /`ô /ð
 */˜JØFJÐCÜ,×2Ñ2ØWÐY`ÐbgÐisôñ &Ø#*¨KÐ#7Ò7øðQ*	ñX 3Ü×?Ñ?ÀeÐ?ÔLáÑ 7¸{×?ZÑ?Z×?nÒ?näØØDÀ_ÐDUÐUeÐfnÐeoð  p^ð  _ôð ð ðkøðùô0 ò ÚðüsQ   ‚AE/ÁEÁ"EÁ#AEÂ)EÂ*A%EÄAE/ÅEÅEÅ	E,Å(E/Å+E,Å,E/c              ƒ  ó°   K  — | j                  |d¬«      }d}| j                  «       r|€dn| j                  |¬«      }| j                  ||¬«      }|||fS ­w)z$Get user email and validation statusNr™   F©r  )r  ré   rŒ  r¯   )r  r  r  rõ   rá   s        rF   Úget_user_infozJWTAuthManager.get_user_info8  sp   è ø€ ð (×6Ñ6¸_Ð\`Ð6Óaˆ
ØÐØ×/Ñ/Ô1Ø(2Ð(:™uÀ×@]Ñ@]ÐisÐ@]Ó@tÐØ$×0Ñ0°ÐV`Ð0ÓaˆØ˜
Ð$4Ð4Ð4ùs   ‚AAc                ó:   — |�|j                   r|j                   S | S )a  Id used for spend / team-membership attribution.

        JWT claim (often email) is only a lookup key. If fuzzy match in
        ``get_user_object`` resolved a legacy row with a different ``user_id``,
        use that row's id; otherwise keep the claim. GH #26789.
        )rá   ©rá   r  s     rF   Ú_canonical_user_id_from_dbz)JWTAuthManager._canonical_user_id_from_dbE  s#   € ð Ð" {×':Ò':Ø×&Ñ&Ð&ØˆrE   c           
   ƒ  óê  K  — d}|r|rt        ||||	|
¬«      ƒ d{  –—† nd}nS|rQt        j                  d|«       t        ||||	|
¬«      ƒ d{  –—† }|r!t        j                  d||j                  «       |du r7t        d|› d|j                  j                  › �t        j                  d	d
¬«      ‚d}| r/| r+t        | |||j                  |¬«      |	|
|| ¬«      ƒ d{  –—† nd}d}|r|rt        ||||	|
|¬«      ƒ d{  –—† nd}t        j                  | |¬«      }|| k7  rt        j                  d| |«       |} d}| r!|r| r|rt!        | ||||	|
¬«      ƒ d{  –—† nd}||||| fS 7 �ŒK7 �Œ7 ŒŽ7 Œo7 Œ­w)zµGet user, org, end-user, and team-membership objects.

        Returns ``(..., effective_user_id)``: JWT claim unless fuzzy lookup
        matched a legacy row (GH #26789).
        N)r  rd   re   r   r!  z&JWT Auth: Resolving org by alias: '%s')r  rd   re   r   r!  z0JWT Auth: Resolved org_alias='%s' to org_id='%s'Fz&Email domain not allowed. User email: z. Allowed domain: r  rô  rX   )rõ   )rá   rd   re   rô   r   r!  r  Ússo_user_id)r  rd   re   r   r!  rø  r[  zAJWT Auth: rebinding user_id %r -> DB user_id %r (email/sso match)©rá   rí   rd   re   r   r!  )r5   r   r(  r6   Úorganization_idr$   r€   rè   r#   Ú
auth_errorr<   rö   r4   rî  r\  r”   r9   )rá   r  r  r  rí   rõ   r  rd   re   r   r!  rø  r  r  r  r  Úeffective_user_idÚteam_membership_objects                     rF   Úget_objectszJWTAuthManager.get_objectsT  s:  è ø€ ð: 8<ˆ
Ùñ ô %Ø!Ø"/Ø'9Ø%5Ø&7ô÷ ð ð ñ ñ Ü ×%Ñ%Ð&NÐPYÔZÜ6Ø#Ø+Ø#5Ø!1Ø"3ô ÷ ˆJñ Ü$×)Ñ)ØFÈ	ÐS]×SmÑSmôð
 ˜uÑ$Ü Ø@ÀÀÐL^Ð_j×_zÑ_z÷  `Uñ  `Uð  _Vð  WÜ$×/Ñ/Ø"Øô	ð ð 15ˆÙñ ô &Ø#Ø"/Ø'9Ø#.×#@Ñ#@ÐRbÐ#@Ó#cØ%5Ø&7Ø)Ø 'ô	÷ 	ð 	ð ð ð 8<ˆÙñ ô *Ø +Ø"/Ø'9Ø%5Ø&7Øô÷ ð ð ð ô $2×#LÑ#LÐU\ÐjuÐ#LÓ#vÐØ Ò'Ü ×&Ñ&ØSØØ!ôð
 $ˆà@DÐÙ‘wñ ™wô *Ø#Ø#Ø"/Ø'9Ø%5Ø&7ô÷ ð ð ð #ð ØØØ"Øð
ð 	
ðuùðùð0	øð"øð2úsY   ‚E3œE'�2E3ÁE*ÁBE3ÃE-Ã E3Ã?E/Ä AE3ÅE1ÅE3Å*E3Å-E3Å/E3Å1E3c                ó.   — |r|s| s|st        dd¬«      ‚y)zUIf enforce_rbac is true, validate that a valid rbac id is returned for spend trackingrô  zrNo user or team id found in token. enforce_rbac is set to True. Token must belong to a proxy admin, team, or user.rö  TrG  ©rá   rí   r
  r  s       rF   Úvalidate_object_idz!JWTAuthManager.validate_object_idÖ  s(   € ñ ¡±wÁwÜØð Lôð ð rE   c           	     ó   — | sy| j                  «       D ��ci c]  \  }}|j                  «       |“Œ }}}|j                  d«      }|sy|xr | }|s ||vrt        dd|› dt	        |«      › �¬«      ‚t        j                  d|«       |S c c}}w )a  
        Extract team_id from x-litellm-team-id header if present.
        Validates that the team is in the user's allowed teams from JWT.

        Args:
            request_headers: Dictionary of request headers
            allowed_team_ids: Set of team IDs the user is allowed to access (from JWT)
            fallback_to_db_teams: When True and the JWT carries no team claims
                (allowed_team_ids is empty), the header value is returned
                provisionally and validated against DB memberships later in
                auth_builder instead of being rejected here.

        Returns:
            The team_id from header if valid, None otherwise

        Raises:
            HTTPException: If team_id is provided but not in allowed_team_ids
        Núx-litellm-team-idrô  úTeam 'zS' from x-litellm-team-id header is not in your JWT's allowed teams. Allowed teams: rö  z/Using team_id from x-litellm-team-id header: %s)r°  Úlowerr½   r   rÏ   r   r”   )Úrequest_headersÚallowed_team_idsrR  r²  r³  Únormalized_headersÚheader_team_idÚdefer_to_db_memberships           rF   Úget_team_id_from_headerz&JWTAuthManager.get_team_id_from_headerå  sÉ   € ñ0 Øð ?N×>SÑ>SÓ>U×$V±d°a¸ Q§W¡W£Y°¡\Ð$VÐÑ$VØ 2× 6Ñ 6Ð7JÓ KˆáØà(<Ò(UÐEUÐAUÐÙ%¨.Ð@PÑ*PÜØØ Ð/ð  0Cô  DHð  IYó  DZð  C[ð  \ôð ô
 	×"Ñ"Ð#TÐVdÔeØÐùó %Ws   —B
c              ƒ  ó^  K  — ddl m} | sy|sy|j                  D ]*  }|j                  sŒ|j                  | j                  k(  sŒ* y t	        t        | j                  d¬«      |j                  ¬«      }	  ||t        t        j                  ¬«      ¬«      ƒ d{  –—†  t        j                  d	| j                  |j                  «       y7 Œ0# t        $ rT}|j                  t        j                  k(  r0t        j                  d
| j                  |j                  «       Y d}~y|‚d}~ww xY w­w)z„
        Map user to teams.
        - If user is not in team, add them to the team
        - If user is in team, do nothing
        r   )Úteam_member_addNÚuser)rá   rž   )Úmemberrí   )r  )rË   r>  z%Successfully added user %s to team %sz&User %s is already a member of team %s)Ú1litellm.proxy.management_endpoints.team_endpointsrs  Úmembers_with_rolesrá   r'   r"   rí   r(   r!   r¬   r   r”   r$   rZ   r#   Úteam_member_already_in_team)r  r  rs  ru  rË   r–   s         rF   Úmap_user_to_teamsz JWTAuthManager.map_user_to_teams  s  è ø€ õ 	VáØáØð "×4Ñ4ò 	ˆFØ�~‹~ &§.¡.°K×4GÑ4GÓ"GÙð	ô +ÜØ#×+Ñ+Øôð  ×'Ñ'ô
ˆð	Ù!ØÜ"0Ü.×:Ñ:ô#ô÷ ð ô !×&Ñ&Ø7¸×9LÑ9LÈk×NaÑNaôð 	ð#ùô ò 	Ø�v‰vœ×DÑDÒDÜ$×*Ñ*Ø<¸k×>QÑ>QÐS^×SfÑSfôô à�ûð	üsS   ‚(D-«D-Á/D-Á5%C ÂCÂ/C Ã
D-ÃC Ã	D*ÃAD%ÄD-Ä#D%Ä%D*Ä*D-c              ƒ  óÞ  K  — | j                   j                  sy|�|€y| j                  |«      }|r¦|j                  |j                  k7  r�t        |«      j                  j                  d|j                  id|j                  i¬«      ƒ d{  –—†  |j                  |_        |�4|j                  |j                  |t        t        |«      ¬«      ƒ d{  –—†  t        | j                   j                  r| j                  |«      n| j                  |«      «      }t        |j                   xs g «      }||z
  }| j                   j                  xr | }	|	r
t        «       n||z
  }
|s|
r{ddlm}  ||j                  t'        |«      t'        |
«      ¬«      ƒ d{  –—†  t'        |«      |_        |�4|j                  |j                  |t        t        |«      ¬«      ƒ d{  –—†  y7 �ŒW7 �Œ7 ŒQ7 Œ­w)	ak  
        Sync user role and team memberships with JWT claims

        The goal of this method is to ensure:
        1. The user role on LiteLLM DB is in sync with the IDP provider role
        2. The user is a member of the teams specified in the JWT token

        This method is only called if sync_user_role_and_teams is set to True in the JWT config.
        Nrá   r  )ÚwhererË   )rB  rC  Ú
model_typer'  r   )Úpatch_team_membership)rá   Úteams_ids_to_add_user_toÚteams_ids_to_remove_user_from)r€   Úsync_user_role_and_teamsr  r  rC  r/   ÚtableÚupdaterá   rD  r    r,   r�   rR  rÛ   rÓ   ÚteamsÚ/litellm.proxy.management_endpoints.scim.scim_v2r}  rÏ   )r  r  r  rd   re   Únew_roleÚjwt_team_idsÚexisting_teamsÚteams_to_addÚ preserve_db_teams_without_claimsÚteams_to_remover}  s               rF   r€  z'JWTAuthManager.sync_user_role_and_teamsD  sþ  è ø€ ð" ×*Ñ*×CÒCØàÐ -Ð"7Øð &×BÑBÀ?ÓSˆÙ˜×-Ñ-°·±Ò?Ü  Ó/×5Ñ5×<Ñ<Ø  +×"5Ñ"5Ð6Ø! 8§>¡>Ð2ð =ó ÷ ð ð %-§N¡NˆKÔ!Ø!Ð-Ø(×8Ñ8Ø#×+Ñ+Ø%Ü0Ü1Ð2DÓEð	 9ó ÷ ð ô "à×*Ñ*×?Ò?ð ×,Ñ,¨_Ô=à×2Ñ2°?ÓCó
ˆô
 !$ K×$5Ñ$5Ò$;¸Ó <ˆØ*¨^Ñ;ˆØ2=×2MÑ2M×2bÑ2bÒ2wÐkwÐgwÐ(Ù*J¤¤ÐP^ÐamÑPmˆÙ™?õñ (Ø#×+Ñ+Ü)-¨lÓ);Ü.2°?Ó.Cô÷ ð ô
 !% \Ó 2ˆKÔØ!Ð-Ø(×8Ñ8Ø#×+Ñ+Ø%Ü0Ü1Ð2DÓEð	 9ó ÷ ð ð 	ð[ùðùð4øðúsK   ‚B
G-ÂG#ÂAG-ÃG&ÃCG-ÆG)ÆAG-ÇG+ÇG-Ç&G-Ç)G-Ç+G-c              ƒ  ó8  K  — |r|j                  d«      nd}|rt        j                  |¬«      sy	 t        ||||||j                  j
                  ¬«      ƒ d{  –—† }	|| d<   |	| d<   y7 Œ# t        $ r"}
t        j                  d|||
«       Y d}
~
yd}
~
ww xY w­w)a,  Attach team context from x-litellm-team-id to an admin result.

        Only applies on LLM API routes so team TPM/RPM limits and attribution
        are enforced when admins act on behalf of a team. Admin management
        routes ignore the header to preserve pre-existing bypass behavior.
        ri  NrH  r  z{admin x-litellm-team-id=%r on route=%s could not be resolved (%s); proceeding with admin access, team context NOT attached.rí   r  )	r½   r*   Úis_llm_api_router:   r€   r"  r“   r   r1  )Úadmin_resultrø  rl  r  rd   re   r   r!  ro  r  r–   s              rF   Ú"_attach_team_from_header_for_adminz1JWTAuthManager._attach_team_from_header_for_admin�  sµ   è ø€ ñ" M\ × 3Ñ 3Ð4GÔ HÐaeˆÙ¤[×%AÑ%AÈÕ%NØð	Ü'6Ø&Ø+Ø#5Ø!1Ø"3Ø*×:Ñ:×IÑIô(÷ "ˆKð( #1ˆ�YÑØ&1ˆ�]Ò#ð+"ùô ò 	ô !×(Ñ(ðKàØØôô ûð	üs@   ‚/B²(A, ÁA*ÁA, ÁBÁ*A, Á,	BÁ5BÂBÂBÂBc              ƒ  óP  K  — | �$| j                   rt        | j                   «      dk7  ry| j                   d   }	 t        ||||||¬«      ƒ d{  –—† }|€y|s||dfS t        ||||||¬«      ƒ d{  –—† }	|||	fS 7 Œ,7 Œ# t        $ r t        j                  d|d¬	«       Y yw xY w­w)
u¥  
        If JWT did not resolve team_id, but the user belongs to exactly one team
        in LiteLLM, load that team (and membership when user_id is set) so that
        spend / metadata can be attributed correctly.

        Returns (team_id, team_object, team_membership_object).
        Any DB error is debug-logged and the tuple is (None, None, None) â€” no
        exception ever propagates from this helper.
        Nr0   ©NNNr   r  r_  z4JWT single-team fallback error, skipping. team_id=%sT)Úexc_info)rƒ  r…   r:   r9   r“   r   r”   )
r  rá   rd   re   r   r!  r"  Ú_tidÚteam_rowr  s
             rF   Ú_resolve_single_team_fallbackz,JWTAuthManager._resolve_single_team_fallback¹  sä   è ø€ ð& Ð k×&7Ò&7¼3¸{×?PÑ?PÓ;QÐUVÒ;VØ#à!×'Ñ'¨Ñ*ˆð	$Ü$3ØØ+Ø#5Ø!1Ø"3Ø-ô%÷ ˆHð ÐØ'áØ˜X tÐ+Ð+ä+>ØØØ+Ø#5Ø!1Ø"3ô,÷ &ˆOð ˜ ?Ð2Ð2ð-øð&ùô ò 	$Ü ×&Ñ&ØFØØõñ
 $ð	$üsd   ‚7B&ºA? ÁA;ÁA? ÁB&ÁA? ÁB&ÁA? Á1A=Á2A? Á:B&Á;A? Á=A? Á?!B#Â B&Â"B#Â#B&c              ƒ  óô  K  — ddl m} | r| j                  ng }t        j	                  |||¬«      }d}|D ]w  }	 t        ||||	|
|¬«      ƒ d{  –—† }d}|r	 t        |||d¬«      ƒ d{  –—†  |sŒ<t        j                  d	|«       |r||t        |||||	|
¬
«      ƒ d{  –—† fc S ||dfc S  |r"|r|rt        dd|› d�¬«      ‚t        dd¬«      ‚y7 Œ†# t        $ r Y Œ¬w xY w7 Œ{# t        $ r Y Œ½w xY w7 ŒV­w)u\  
        Resolve a team for a user whose JWT carries no team claims by selecting
        the first DB team membership that loads successfully and, when a model is
        requested, can access that model â€” mirroring the per-team model-access
        check the claim-based path enforces, so a team's `models` restriction is
        not bypassed by the fallback.

        The same `team_allowed_routes` gate the claim-based path applies is
        enforced here too, so a DB-selected team cannot reach a route the JWT
        config excludes for team-role callers. Auth-enforced passthrough routes
        are exempt from that gate by design (they are governed by the team's
        `allowed_passthrough_routes`, re-checked by the caller).

        The resolved team's membership row is loaded too (when user_id is set) so
        per-team membership budget limits are enforced on the fallback path the
        same as on the claim-based path.

        Raises HTTP 403 when the user has no usable DB team membership and
        `enforce_team_based_model_access` is set; otherwise returns (None, None, None).
        r   rK  ©rø  rC  r  Fr  NTrM  zAJWT DB team fallback: resolved team_id=%s from user DB membershipr_  rô  z?No team you are a member of has access to the requested model: z5. Check `/models` to see the models available to you.rö  zSUser is not a member of any team. Add the user to a team via the LiteLLM UI or API.r�  )rP  rL  rƒ  rî  Ú_is_team_route_allowedr:   r   r)   r$   r   r”   r9   )r  rá   r  rø  r  rQ  r"  rd   re   r   r!  rC  rL  Úuser_team_idsÚteam_route_allowedÚany_team_resolvedÚcandidate_team_idr  s                     rF   Ú_resolve_db_team_fallbackz(JWTAuthManager._resolve_db_team_fallbackð  sˆ  è ø€ õF 	:á4?˜{×0Ò0ÀRˆÜ$2×$IÑ$IØ¨ÀKð %Jó %
Ðð "ÐØ!.ò *	8Ðð
Ü$3Ø-Ø"/Ø'9Ø%5Ø&7Ø#1ô%÷ �ð !%ÐÙðÜ/Ø-Ø$/Ø#-Ø+/ô	÷ ð ñ &ØÜ ×&Ñ&ØSØ!ôñ à%ØÜ-Ø 'Ø 1Ø&3Ø+=Ø)9Ø*;ô÷ ðò ð % k°4Ð7Ò7ðU*	8ñX +ÙÑ#4Ü#Ø #ð"Ø"1Ð!2ð 3,ð-ôð ô  ØØmôð ð  ðqùô !ò Ùðúð
ùô &ò Ùðúðús|   ‚6C8¹CÁCÁCÁC8ÁC'Á)C%Á*C'Á.1C8ÂC6
Â 4C8ÃCÃ	C"ÃC8Ã!C"Ã"C8Ã%C'Ã'	C3Ã0C8Ã2C3Ã3C8c                óÄ   — t        |t        «      r|j                  «       nd}t        j                  | |¬«      xs& t        t        j                  | |j                  ¬«      S )zì
        Whether a team-role caller may reach `route` per the JWT config's
        `team_allowed_routes`. Auth-enforced passthrough routes are exempt
        here; their team's `allowed_passthrough_routes` gate runs separately.
        Nr;  r  )	r‘   r¿   r?  r*   r@  r2   r!   r®   r€   )rø  rC  r  Únormalized_methods       rF   r—  z%JWTAuthManager._is_team_route_allowedV  s\   € ô >HÈÔX[Ô=\ >×#7Ñ#7Ô#9ÐbfÐÜ×>Ñ>ØÐ 1ô
ò 
ä!Ü&×+Ñ+ØØ +× ;Ñ ;ô
ð	
rE   c                ó$   — t        dd| › d�¬«      ‚)zó
        The single denial shape for a provisional x-litellm-team-id header,
        raised identically for nonexistent teams and for teams the user is not
        a member of, so the response does not reveal whether a team id exists.
        rô  rj  zB' (from x-litellm-team-id header) is not in your team memberships.rö  rG  )rí   s    rF   Ú$_raise_header_team_membership_denialz3JWTAuthManager._raise_header_team_membership_denialj  s!   € ô ØØ˜W˜IÐ%gÐhô
ð 	
rE   c                óX   — |r|j                   ng }| |v ryt        j                  | «       y)zô
        A provisional team_id from the x-litellm-team-id header (accepted without
        JWT-team validation when the JWT carries no team claims) must exist in the
        user's DB team memberships before it becomes request context.
        N)rƒ  rî  r   )rí   r  r˜  s      rF   Ú&_validate_header_team_in_db_membershipz5JWTAuthManager._validate_header_team_in_db_membershipv  s-   € ñ 5@˜{×0Ò0ÀRˆØ�mÑ#ØÜ×;Ñ;¸GÕDrE   c              ƒ  óH  K  — |j                   j                  rB|j                  | ¬«      s0t        j                  d«       |j                  | ¬«      ƒ d{  –—† }n|j                  | ¬«      ƒ d{  –—† }|j                   j                  r(|j                   j                  |«      st        dd¬«      ‚|j                  |¬«      }t        j                  ||||||«      ƒ d{  –—†  |j                  |¬«      }|j                   j                  rC|j                   j                  r-t        j                  |j                   j                  |||¬«       |j!                  |d¬«      }t        j#                  ||«      ƒ d{  –—† \  }}}|j%                  |d¬«      }|j'                  |d¬«      }d}d}|j!                  |d¬«      }|r-|r+|t(        j*                  k(  r|}n|t(        j,                  k(  r|}t        j/                  |||||| ||¬	«      ƒ d{  –—† }|r't        j1                  |||	|||||¬
«      ƒ d{  –—†  |S t        j3                  ||«      }|j5                  |d¬«      }|j                   j6                  xr/ |j9                  |¬«       xr |j;                  |d¬«       xr |du }|r|s|j=                  |«       t        j?                  |	||¬«      }|r6|}	 tA        ||||||j                   jB                  xr | ¬«      ƒ d{  –—† }n)|s'|s%t        jG                  ||||||«      ƒ d{  –—† \  }}|s:|s8t        jI                  ||jK                  d«      ||
|||||¬«	      ƒ d{  –—† \  }}|rf|€dtM        jN                  |tQ        |
tR        «      r|
jU                  «       nd¬«      r-tA        ||||||j                   jB                  ¬«      ƒ d{  –—† }|r.t        jW                  |||
¬«      st        jY                  |¬«       |j[                  |d¬«      }t        j]                  |||||||||||||¬«      ƒ d{  –—† \  }}}}}|r|j^                  n|} t        ja                  |||||¬«      ƒ d{  –—†  |€—|r•t        jc                  |||jK                  d«      |||j                   jd                  |j                   jB                  |||||
¬«      ƒ d{  –—† \  }}}|r¸t        jW                  |||
¬«      s t        jY                  |¬«       n‰|€=t        jg                  |||||||j                   jB                  ¬«      ƒ d{  –—† \  }}}nJ|rH||k(  rCt        ji                  ||¬«       t        jk                  ||
|¬«      st        dd|› d|› d�¬«      ‚t        jm                  ||¬«      ƒ d{  –—†  t        jo                  |||jK                  dd«      d¬«       tq        |xr |jr                  t(        jt                  k(  «      }!tw        |!||||�|jx                  r|jx                  n||| |||| ||¬«      S 7 �Œù7 �Œá7 �Œr7 �ŒÙ7 �ŒG7 �Œ#7 �ŒP# t        $ r |s‚ t        jE                  |«       Y �ŒHw xY w7 �ŒT7 �Œ7 �Œ³7 �ŒI7 �Œ7 �Œ³7 �ŒG7 ŒÛ­w) z-Main authentication and authorization builderr¥   zDOIDC UserInfo is enabled. Fetching user info from UserInfo endpoint.Nrô  zInvalid JWT tokenrö  )r  r§   r  rñ  r™   rX  )r�  rø  rl  r  rd   re   r   r!  )rl  rm  rR  r  rý  )	rÒ   r  rø  rC  r  rd   re   r   r!  r;  rO  rH  )rá   r  r  r  rí   rõ   r  rd   re   r   r!  rø  r  )r  r  r  rd   re   )r  rá   r  rø  r  rQ  r"  rd   re   r   r!  rC  )r  rá   rd   re   r   r!  r"  )rí   r  r–  rj  zB' (from x-litellm-team-id header) is not allowed to access route 'r&  )r  r  r
  Frf  )r  rí   r  rá   r  r  r  r  r  r  r†   r  r  )=r€   Úoidc_userinfo_enabledrˆ   r   r”   rš  rÝ  Úcustom_validater   r³   rî  r  r©   Úenforce_scope_based_accessr  r  r  rY  r  râ   r!   r®   r±   r  rŽ  r9  r­   rR  rÛ   rñ   Úaddrq  r:   r"  r   r5  rV  r½   r*   r@  r‘   r¿   r?  rE  rI  r  rd  r`  r€  rœ  rQ  r”  r¢  r—  ry  rg  râ  r  r¬   r   r  )"r  r  r  rñ  rø  rd   re   r   r!  rl  rC  r  r²   r§   r  rá   r  rõ   r  r  rí   r  r�  r8  Úspecific_team_idÚdb_team_fallbackro  r  r  r  r  rc  Úresolved_org_idr  s"                                     rF   Úauth_builderzJWTAuthManager.auth_builder…  sÖ  è ø€ ð" ×&Ñ&×<Ò<À[×EWÑEWÐ^eÐEWÔEfÜ ×&Ñ&Ð'mÔnà*5×*GÑ*GÈgÐ*GÓ*V×$V‰Oð %0×$8Ñ$8¸wÐ$8Ó$G×GˆOð ×&Ñ&×6Ò6Ø×.Ñ.×>Ñ>¸ÔOÜ#Ø #Ø.ôð ð '×4Ñ4¸?Ð4ÓKˆ	Ü×,Ñ,ØØØØØØó
÷ 	
ð 	
ð $×.Ñ.°_Ð.ÓEˆØ×&Ñ&×AÒAÀk×FaÑFa×FpÒFpÜ×3Ñ3Ø*×:Ñ:×IÑIØØ)Ø!1ð	 4ô ð  ×-Ñ-°OÐSWÐ-ÓXˆ	ô 7E×6RÑ6RÐS^Ð`oÓ6p×0pÑ-ˆ�Ð-ð $×.Ñ.°_ÐTXÐ.ÓYˆØ(×8Ñ8¸Ð^bÐ8ÓcˆØ"ˆØ04ˆØ×-Ñ-°OÐSWÐ-ÓXˆ	á™ØÔ,×1Ñ1Ò1Ø#‘ØÔ.×<Ñ<Ò<Ø#�ô %3×$EÑ$EØ˜ ¨°¸À/Ð^hð %Fó %
÷ 
ˆñ Ü ×CÑCØ)ØØ /Ø'Ø+Ø#5Ø!1Ø"3ð Dó 	÷ 	ð 	ð  Ðô -×=Ñ=¸kÈ?Ó[ˆØ"-×"9Ñ"9ÀÐ_cÐ"9Ó"dÐð ×'Ñ'×<Ñ<ò  Ø×4Ñ4¸?Ð4ÓKÐKò à×.Ñ.°_ÐTXÐ.ÓYÐYò ð ˜4�ð	 	ñ Ñ$4Ø×ÑÐ-Ô.ä .× FÑ FØ+Ø)Ø!1ð !Gó !
ˆñ
 Ø$ˆGðMÜ$3Ø#Ø"/Ø'9Ø%5Ø&7Ø$/×$?Ñ$?×$NÑ$NÒ$gÐWgÐSgô%÷ ‘ñ Ñ!1ô
 %×GÑGØØØØ"Ø Ø!ó÷ ñØØñ ¡7ä)7×)SÑ)SØ%Ø ,× 0Ñ 0°Ó 9ØØ-Ø'Ø+Ø#5Ø!1Ø"3ð *Tó 
*÷ 
$Ñ ˆG�[ñ  ØÐ#Ü×?Ñ?ØÜ2<¸^ÌSÔ2Q˜×,Ñ,Ô.ÐW[õô
 !0ØØ+Ø#5Ø!1Ø"3Ø*×:Ñ:×IÑIô!÷ ˆKñ œ>×LÑLØ#ØØ)ð Mô 
ô
 ×?Ñ?ÀeÐ?ÔLð '×4Ñ4¸?ÐZ^Ð4Ó_ˆ	ô !×,Ñ,ØØ!ØØ#ØØ-Ø#Ø'Ø1Ø-Ø/ØØð -ó 
÷ 
ñ	
ØØØØ"Øñ$ @J ×!;Ò!;Èvˆä×5Ñ5Ø#Ø+Ø#Ø'Ø1ð 6ó 
÷ 	
ð 	
ð ˆ?Ñ/ô
 %×>Ñ>Ø'ØØ ,× 0Ñ 0°Ó 9ØØ'Ø0;×0KÑ0K×0kÑ0kØ*×:Ñ:×IÑIØ+Ø#5Ø!1Ø"3Ø-ð ?ó ÷ ñ	ØØØ&ñ$ œ~×PÑPØ'ØØ-ð  Qô  ô
 ×CÑCÈ%ÐCÕPØˆ_ô
 %×BÑBØ'ØØ+Ø#5Ø!1Ø"3Ø*×:Ñ:×IÑIð Có ÷ ñ	ØØÙ&ñ  '¨^Ò";Ü×AÑAØØ'ð Bô ô "×8Ñ8ØØ-Ø'ð 9ô ô
 $Ø #à   	Ð)kÐlqÐkrÐrtÐuôð ô ×.Ñ.Ø#Ø#ð /ó 
÷ 	
ð 	
ô 	×)Ñ)ØØØ)×-Ñ-¨n¸eÓDØ ð	 	*ô 	
ô !% [Ò%j°[×5JÑ5JÔN^×NjÑNjÑ5jÓ kˆä#Ø)ØØ#ØØ2=Ð2IÈk×NdÒNd˜×.Ò.ÐjtØ#Ø"Ø!Ø#Ø+ØØ2Ø&ô
ð 	
ðy %Wùð Hùð	
ùð, 1qùð 
ùð	ùð\úô !ò MÙ'ØÜ×CÑCÀG×LðMúðùð
$ùð.ùð4
ùð&	
ùðùð8ùð6	
ús6  ‚AZ"ÁYÁZ"Á0YÁ1A1Z"Ã"YÃ#BZ"Å>YÅ?BZ"ÈYÈ&Z"È:YÈ;B'Z"Ë#-Y# ÌY ÌY# Ì"Z"Ì7ZÌ8;Z"Í3ZÍ4A*Z"ÏZÏA,Z"ÑZÑ7Z"ÒZÒA#Z"Ó'ZÓ(A.Z"ÕZÕA-Z"×Z ×B
Z"ÙZ"ÙZ"ÙZ"ÙZ"ÙZ"Ù Y# Ù#!ZÚZ"ÚZÚZ"ÚZ"ÚZ"ÚZ"ÚZ"ÚZ"ÚZ"Ú Z")r²   r   rñ  r’   rø  r¿   rà  úLiteral[True])r²   r   rñ  r’   rý  rá  rà  r¬  )
r  zlist[ScopeMapping]r§   rå  r  r’   rñ  r’   rà  rW   )r  rb   r  r’   rñ  r’   r  r’   rø  r¿   r²   rä  rà  rW   r#  )r  rb   r§   rÏ   rø  r¿   rá   rá  r  rá  r  r¿   r  rã  r  rá  rà  zJWTAuthBuilderResult | None)r  rb   r  r’   rd   rc   re   r+   r   úSpan | Noner!  r.   rà  ú+tuple[str | None, LiteLLM_TeamTable | None])r  rb   r  r’   rà  úset[str]r   )r  úLiteLLM_TeamTable | Nonerø  r¿   rC  rá  rà  râ  )rø  r¿   rà  rW   )rÒ   r¯  r  rá  rø  r¿   r  rb   rd   rc   re   r+   r   r­  r!  r.   rC  rá  rà  r®  )r  rb   r  r’   rà  z*tuple[str | None, str | None, bool | None])rá   rá  r  úLiteLLM_UserTable | Nonerà  rá  )rá   rá  r  rá  r  rá  r  rá  rí   rá  rõ   ræ  r  rb   rd   rc   re   r+   r   r­  r!  r.   rø  r¿   r  rá  rà  z‰tuple[LiteLLM_UserTable | None, LiteLLM_OrganizationTable | None, LiteLLM_EndUserTable | None, LiteLLM_TeamMembership | None, str | None])
rá   rá  rí   rá  r
  râ  r  râ  rà  r¬  )F)rl  rã  rm  r¯  rR  râ  rà  rá  )r  r±  r  r°  )r  rb   r  r’   r  r±  rd   rc   re   zUserApiKeyCache | Nonerà  rW   )r�  r   rø  r¿   rl  rã  r  rb   rd   rc   re   r+   r   r­  r!  r.   rà  rW   )r  r±  rá   rá  rd   rc   re   r+   r   r­  r!  r.   r"  ræ  rà  Útuple)r  r±  rá   rá  r  rá  rø  r¿   r  rb   rQ  râ  r"  râ  rd   rc   re   r+   r   r­  r!  r.   rC  rá  rà  zJtuple[str | None, LiteLLM_TeamTable | None, LiteLLM_TeamMembership | None])rø  r¿   rC  rá  r  rb   rà  râ  )rí   r¿   rà  r	   )rí   r¿   r  r±  rà  rW   )r  r¿   r  rb   r  r’   rñ  r’   rø  r¿   rd   rc   re   r+   r   r­  r!  r.   rl  rã  rC  rá  rà  r   )r@   rA   rB   rC   rë  rû  rÿ  r  r  r  r5  r9  rE  rI  rV  rY  r\  rd  rg  rq  ry  r€  rŽ  r”  rœ  r—  r   r¢  r«  rD   rE   rF   rî  rî  Å  sg  „ ÙAàðØðàðð ðð 
ò	ó ðð4 ðØðàðð ðð 
ò	ó ðð( ðØ*ðàðð ðð ð	ð
 
òó ðð: ðØðàðð ðð ð	ð
 ðð %ðð 
òó ðð4 ð (,Ø!%ð&
Øð&
àð&
ð ð&
ð ð	&
ð
 ð&
ð ð&
ð %ð&
ð ð&
ð 
%ò&
ó ð&
ðP ð^/Øð^/àð^/ð +ð^/ð ,ð	^/ð
 &ð^/ð (ð^/ð 
5ò^/ó ð^/ð@ òó ðð ð &*ð
Ø-ð
àð
ð #ð
ð 
ò	
ó ð
ð& ò
ó ð
ð ð &*ðSØðSà#ðSð ðSð  ð	Sð
 +ðSð ,ðSð &ðSð (ðSð #ðSð 
5òSó ðSðj ð
5Øð
5àð
5ð 
4ò
5ó ð
5ð ðØðà-ðð 
òó ðð ð !%ð
Øð
àð
ð ð
ð  ð	
ð
 ð
ð &ð
ð  ð
ð +ð
ð ,ð
ð &ð
ð (ð
ð ð
ð ð
ð
ò
ó ð
ðB ðØðàðð ðð ð	ð
 
òó ðð ð &+ð)Ø$ð)à"ð)ð #ð)ð 
ò	)ó ð)ðV ð0Ø-ð0à-ò0ó ð0ðd ð 6:ðFØðFàðFð .ðFð +ð	Fð
 3ðFð 
òFó ðFðP ð)2Ø*ð)2àð)2ð %ð)2ð  ð	)2ð
 +ð)2ð ,ð)2ð &ð)2ð (ð)2ð 
ò)2ó ð)2ðV ð4$Ø-ð4$àð4$ð +ð4$ð ,ð	4$ð
 &ð4$ð (ð4$ð $ð4$ð 
ò4$ó ð4$ðl ð &*ðc Ø-ðc àðc ð $ðc ð ð	c ð
  ðc ð *.ðc ð ðc ð +ðc ð ,ðc ð &ðc ð (ðc ð #ðc ð 
Tòc ó ðc ðJ ð
Øð
à"ð
ð  ð
ð 
ò	
ó ð
ð& ò	
ó ð	
ð ðEØðEà-ðEð 
òEó ðEð ð (,Ø%)ð}
Øð}
àð}
ð ð}
ð ð	}
ð
 ð}
ð +ð}
ð ,ð}
ð &ð}
ð (ð}
ð %ð}
ð #ð}
ð 
ò}
ó ñ}
rE   rî  )r_   rH   rà  r$   )crC   Ú
__future__r   r2  r   r“  r  r,  rW  Úcollections.abcr   r   Útypingr   r   r   r	   r
   r   r/  rŽ   Úcryptographyr   Úcryptography.hazmat.backendsr   Úcryptography.hazmat.primitivesr   Úfastapir   r   Újwt.api_jwkr   Úlitellm._loggingr   Ú0litellm.litellm_core_utils.dot_notation_indexingr   Ú'litellm.llms.custom_httpx.httpx_handlerr   Úlitellm.proxy._typesr   r   r   r   r   r   r   r   r   r   r   r    r!   r"   r#   r$   r%   r&   r'   r(   Úlitellm.proxy.auth.auth_checksr)   Úlitellm.proxy.auth.route_checksr*   Ú-litellm.proxy.common_utils.user_api_key_cacher+   r,   Úlitellm.proxy.utilsr-   r.   Ú$litellm.repositories.user_repositoryr/   Úauth_checksr1   r2   r3   r4   r5   r6   r7   r8   r9   r:   r;   r<   r“   r>   rH   rJ   rê  rK   rM   rO   rQ   rS   r¿   rT   r`   rb   rî  rD   rE   rF   ú<module>rÅ     s  ðòõ #ã Û Û Û 	Û 	Û ß /ß ?× ?ã Û 
Ý Ý 8Ý 8ß )Ý å 1Ý MÝ ?÷÷ ÷ ÷ ÷ ó õ, AÝ 7÷÷ ;Ý ?÷÷ ÷ ó ô U )ô Uôy˜9ô yð Ð �UÓ Ø*.Ð   %Ó .Ø*,Ð   %Ó ,Ø 0Ð ˜Ó 0Ø+FÐ ! 5Ó FØ&AÐ ˜eÓ Aá˜¨{¸SÑ/@ÔA€ó	÷V(ñ V(÷r"~
ò ~
rE   