o
    hY                     @   sj  d 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Zddlm	Z	m
Z
mZ ddlmZmZ ddlmZ ddlmZmZmZ ddlZddlZddlmZmZmZ ddlmZ dd	lmZmZm Z m!Z!m"Z" dd
l#m$Z$m%Z% ddl&m'Z' e(e)Z*G dd de+Z,G dd de,Z-G dd de,Z.G dd deZ/G dd deZ0eG dd dZ1G dd dej2Z3dS )z|
OAuth2 Authentication implementation for HTTPX.

Implements authorization code flow with PKCE and automatic token refresh.
    N)AsyncGenerator	AwaitableCallable)	dataclassfield)Protocol)	urlencodeurljoinurlparse)	BaseModelFieldValidationError)MCP_PROTOCOL_VERSION)OAuthClientInformationFullOAuthClientMetadataOAuthMetadata
OAuthTokenProtectedResourceMetadata)check_resource_allowedresource_url_from_server_url)LATEST_PROTOCOL_VERSIONc                   @      e Zd ZdZdS )OAuthFlowErrorz%Base exception for OAuth flow errors.N__name__
__module____qualname____doc__ r   r   P/var/www/html/openai_agents/venv/lib/python3.10/site-packages/mcp/client/auth.pyr   %       r   c                   @   r   )OAuthTokenErrorz"Raised when token operations fail.Nr   r   r   r   r   r!   )   r    r!   c                   @   r   )OAuthRegistrationErrorz&Raised when client registration fails.Nr   r   r   r   r   r"   -   r    r"   c                   @   sL   e Zd ZU dZeddddZeed< eddddZeed< e	dd	d
Z
dS )PKCEParametersz.PKCE (Proof Key for Code Exchange) parameters..+      )
min_length
max_lengthcode_verifiercode_challengereturnc                 C   sJ   d dd tdD }t|  }t| 	d}| ||dS )zGenerate new PKCE parameters. c                 s   s&    | ]}t tjtj d  V  qdS )z-._~N)secretschoicestringascii_lettersdigits).0_r   r   r   	<genexpr>:   s   $ z*PKCEParameters.generate.<locals>.<genexpr>r%   =)r(   r)   )
joinrangehashlibsha256encodedigestbase64urlsafe_b64encodedecoderstrip)clsr(   r:   r)   r   r   r   generate7   s   zPKCEParameters.generateN)r*   r#   )r   r   r   r   r   r(   str__annotations__r)   classmethodr@   r   r   r   r   r#   1   s   
 r#   c                   @   sX   e Zd ZdZdedB fddZdeddfddZdedB fd	d
ZdeddfddZ	dS )TokenStoragez+Protocol for token storage implementations.r*   Nc                       dS )zGet stored tokens.Nr   selfr   r   r   
get_tokensC      zTokenStorage.get_tokenstokensc                    rE   )zStore tokens.Nr   )rG   rJ   r   r   r   
set_tokensG   rI   zTokenStorage.set_tokensc                    rE   )zGet stored client information.Nr   rF   r   r   r   get_client_infoK   rI   zTokenStorage.get_client_infoclient_infoc                    rE   )zStore client information.Nr   )rG   rM   r   r   r   set_client_infoO   rI   zTokenStorage.set_client_info)
r   r   r   r   r   rH   rK   r   rL   rN   r   r   r   r   rD   @   s    rD   c                   @   s  e Zd ZU dZeed< eed< eed< eege	d f ed< eg e	e
eedB f  f ed< dZeed	< dZedB ed
< dZedB ed< dZedB ed< dZedB ed< dZedB ed< dZedB ed< dZedB ed< eejdZejed< dZedB ed< dZedB ed< dedefddZdeddfddZde fddZ!de fddZ"d%dd Z#defd!d"Z$d&dedB de fd#d$Z%dS )'OAuthContextzOAuth flow context.
server_urlclient_metadatastorageNredirect_handlercallback_handler     r@timeoutprotected_resource_metadataoauth_metadataauth_server_urlprotocol_versionrM   current_tokenstoken_expiry_time)default_factorylockdiscovery_base_urldiscovery_pathnamer*   c                 C   s   t |}|j d|j S )z,Extract base URL by removing path component.://)r
   schemenetloc)rG   rP   parsedr   r   r   get_authorization_base_urls   s   z'OAuthContext.get_authorization_base_urltokenc                 C   s$   |j rt |j  | _dS d| _dS )zUpdate token expiry time.N)
expires_intimer\   )rG   rf   r   r   r   update_token_expiryx   s   
z OAuthContext.update_token_expiryc                 C   s(   t | jo| jjo| j pt | jkS )z Check if current token is valid.)boolr[   access_tokenr\   rh   rF   r   r   r   is_token_valid   s   zOAuthContext.is_token_validc                 C   s   t | jo
| jjo
| jS )z Check if token can be refreshed.)rj   r[   refresh_tokenrM   rF   r   r   r   can_refresh_token   s   zOAuthContext.can_refresh_tokenc                 C   s   d| _ d| _dS )zClear current tokens.N)r[   r\   rF   r   r   r   clear_tokens   s   
zOAuthContext.clear_tokensc                 C   s8   t | j}| jr| jjrt| jj}t||dr|}|S )zGet resource URL for RFC 8707.

        Uses PRM resource if it's a valid parent, otherwise uses canonical server URL.
        )requested_resourceconfigured_resource)r   rP   rW   resourcerA   r   )rG   rr   prm_resourcer   r   r   get_resource_url   s   
zOAuthContext.get_resource_urlc                 C   s   | j durdS |sdS |dkS )zDetermine if the resource parameter should be included in OAuth requests.

        Returns True if:
        - Protected resource metadata is available, OR
        - MCP-Protocol-Version header is 2025-06-18 or later
        NTFz
2025-06-18)rW   )rG   rZ   r   r   r   should_include_resource_param   s
   
z*OAuthContext.should_include_resource_paramr*   N)N)&r   r   r   r   rA   rB   r   rD   r   r   tuplerV   floatrW   r   rX   r   rY   rZ   rM   r   r[   r   r\   r   anyioLockr^   r_   r`   re   ri   rj   rl   rn   ro   rt   ru   r   r   r   r   rO   T   s2   
  
rO   c                   @   s  e Zd ZdZdZ	d4dedededeege	d f d	eg e	e
eedB f  f d
efddZdejdedB fddZdejdejfddZdejddfddZdee fddZdejdB fddZdejddfddZde
eef fddZdededejfd d!Zdejddfd"d#Zdejfd$d%Zdejdefd&d'Zd5d(d)Zd*ejddfd+d,Zd-edejfd.d/Zdejddfd0d1Z d*ejde!ejejf fd2d3Z"dS )6OAuthClientProviderzw
    OAuth2 authentication for httpx.
    Handles OAuth flow with automatic client registration and token storage.
    TrU   rP   rQ   rR   rS   NrT   rV   c                 C   s    t ||||||d| _d| _dS )z!Initialize OAuth2 authentication.)rP   rQ   rR   rS   rT   rV   FN)rO   context_initialized)rG   rP   rQ   rR   rS   rT   rV   r   r   r   __init__   s   

zOAuthClientProvider.__init__init_responser*   c                 C   sR   |r|j dkr	dS |jd}|sdS d}t||}|r'|dp&|dS dS )z
        Extract protected resource metadata URL from WWW-Authenticate header as per RFC9728.

        Returns:
            Resource metadata URL if found in WWW-Authenticate header, None otherwise
          NzWWW-Authenticatez)resource_metadata=(?:"([^"]+)"|([^\s,]+))      )status_codeheadersgetresearchgroup)rG   r   www_auth_headerpatternmatchr   r   r   (_extract_resource_metadata_from_www_auth   s   z<OAuthClientProvider._extract_resource_metadata_from_www_authc                    s>   |  |}|s| j| jj}t|d}tjd|ttidS )Nz%/.well-known/oauth-protected-resourceGETr   )	r   r|   re   rP   r	   httpxRequestr   r   )rG   r   urlauth_base_urlr   r   r   _discover_protected_resource   s   

z0OAuthClientProvider._discover_protected_resourceresponsec                    sj   |j dkr3z"| I dH }t|}|| j_|jr&t|jd | j_W dS W dS  t	y2   Y dS w dS )zHandle discovery response.   Nr   )
r   areadr   model_validate_jsonr|   rW   authorization_serversrA   rY   r   rG   r   contentmetadatar   r   r   #_handle_protected_resource_response   s   

z7OAuthClientProvider._handle_protected_resource_responsec                 C   s   g }| j jp	| j j}t|}|j d|j }|jr0|jdkr0d|jd }|t	|| |t	|d |jrQ|jdkrQd|jd }|t	|| |d d}|| |S )zCGenerate ordered list of (url, type) tuples for discovery attempts.ra   /z'/.well-known/oauth-authorization-serverz!/.well-known/openid-configuration)
r|   rY   rP   r
   rb   rc   pathr>   appendr	   )rG   urlsrY   rd   base_url
oauth_path	oidc_pathoidc_fallbackr   r   r   _get_discovery_urls   s   
z'OAuthClientProvider._get_discovery_urlsc                    st   | j jrdS | j jr| j jjrt| j jj}n| j | j j}t|d}| j jj	dddd}t
jd||ddid	S )
z9Build registration request or skip if already registered.Nz	/registerTjson)by_aliasmodeexclude_nonePOSTContent-Typezapplication/json)r   r   )r|   rM   rX   registration_endpointrA   re   rP   r	   rQ   
model_dumpr   r   )rG   registration_urlr   registration_datar   r   r   _register_client  s   
z$OAuthClientProvider._register_clientc              
      s   |j dvr| I dH  td|j  d|j z| I dH }t|}|| j_| jj	|I dH  W dS  t
yI } ztd| d}~ww )zHandle registration response.)r      NzRegistration failed:  zInvalid registration response: )r   r   r"   textr   r   r|   rM   rR   rN   r   )rG   r   r   rM   er   r   r   _handle_registration_response*  s   

z1OAuthClientProvider._handle_registration_responsec           	         s4  | j jr| j jjrt| j jj}n| j | j j}t|d}| j js'tdt	
 }td}d| j jjt| j jjd ||jdd}| j | j jrS| j  |d< | j jjr_| j jj|d	< | d
t| }| j |I dH  | j  I dH \}}|du st||std| d| |std||jfS )z5Perform the authorization redirect and get auth code.z
/authorizez*No client info available for authorization    coder   S256)response_type	client_idredirect_uristater)   code_challenge_methodrr   scope?NzState parameter mismatch: z != zNo authorization code received)r|   rX   authorization_endpointrA   re   rP   r	   rM   r   r#   r@   r,   token_urlsafer   rQ   redirect_urisr)   ru   rZ   rt   r   r   rS   rT   compare_digestr(   )	rG   auth_endpointr   pkce_paramsr   auth_paramsauthorization_url	auth_codereturned_stater   r   r   _perform_authorization8  s8   




z*OAuthClientProvider._perform_authorizationr   r(   c                    s   | j js	td| j jr| j jjrt| j jj}n| j | j j}t|d}d|t| j j	j
d | j jj|d}| j | j jrH| j  |d< | j jjrT| j jj|d< tjd||d	d
idS )zBuild token exchange request.zMissing client info/tokenauthorization_coder   )
grant_typer   r   r   r(   rr   client_secretr   r   !application/x-www-form-urlencodeddatar   )r|   rM   r   rX   token_endpointrA   re   rP   r	   rQ   r   r   ru   rZ   rt   r   r   r   )rG   r   r(   	token_urlr   
token_datar   r   r   _exchange_tokenf  s(   
	
z#OAuthClientProvider._exchange_tokenc              
      s   |j dkrtd|j  zH| I dH }t|}|jr@| jjjr@t| jjj	 }t|j	 }|| }|r@td| || j_
| j| | jj|I dH  W dS  tyi } ztd| d}~ww )zHandle token exchange response.r   zToken exchange failed: Nz$Server granted unauthorized scopes: zInvalid token response: )r   r!   r   r   r   r   r|   rQ   setsplitr[   ri   rR   rK   r   )rG   r   r   token_responserequested_scopesreturned_scopesunauthorized_scopesr   r   r   r   _handle_token_response  s&   

z*OAuthClientProvider._handle_token_responsec                    s   | j jr
| j jjstd| j jstd| j jr'| j jjr't| j jj}n| j | j j	}t
|d}d| j jj| j jjd}| j | j jrO| j  |d< | j jjr[| j jj|d< tjd||d	d
idS )zBuild token refresh request.zNo refresh token availablezNo client info availabler   rm   )r   rm   r   rr   r   r   r   r   r   )r|   r[   rm   r!   rM   rX   r   rA   re   rP   r	   r   ru   rZ   rt   r   r   r   )rG   r   r   refresh_datar   r   r   _refresh_token  s(   

z"OAuthClientProvider._refresh_tokenc                    s   |j dkrtd|j   | j  dS z#| I dH }t|}|| j_| j	| | jj
|I dH  W dS  tyM   td | j  Y dS w )z:Handle token refresh response. Returns True if successful.r   zToken refresh failed: FNTzInvalid refresh response)r   loggerwarningr|   ro   r   r   r   r[   ri   rR   rK   r   	exception)rG   r   r   r   r   r   r   _handle_refresh_response  s"   




z,OAuthClientProvider._handle_refresh_responsec                    s8   | j j I dH | j _| j j I dH | j _d| _dS )z#Load stored tokens and client info.NT)r|   rR   rH   r[   rL   rM   r}   rF   r   r   r   _initialize  s   
zOAuthClientProvider._initializerequestc                 C   s4   | j jr| j jjrd| j jj |jd< dS dS dS )z<Add authorization header to request if we have valid tokens.zBearer AuthorizationN)r|   r[   rk   r   )rG   r   r   r   r   _add_auth_header  s   z$OAuthClientProvider._add_auth_headerr   c                 C   s   t jd|ttidS )Nr   r   )r   r   r   r   )rG   r   r   r   r   _create_oauth_metadata_request  s   z2OAuthClientProvider._create_oauth_metadata_requestc                    sX   |  I d H }t|}|| j_| jjjd u r(|jd ur*d|j| jj_d S d S d S )Nr   )	r   r   r   r|   rX   rQ   r   scopes_supportedr5   r   r   r   r   _handle_oauth_metadata_response  s   
z3OAuthClientProvider._handle_oauth_metadata_responsec              
   C  s  | j j4 I dH  | js|  I dH  |jt| j _| j  s;| j 	 r;| 
 I dH }|V }| |I dH s;d| _| j  rE| | |V }|jdkrz}| |I dH }|V }| |I dH  |  }|D ]0}| |}	|	V }
|
jdkrz| |
I dH  W  n ty   Y qgw |
jdk s|
jdkr nqg|  I dH }|r|V }| |I dH  |  I dH \}}| ||I dH }|V }| |I dH  W n ty   td  w | | |V  W d  I dH  dS W d  I dH  dS 1 I dH sw   Y  dS )zHTTPX auth flow integration.NFr   r   i  i  zOAuth flow error)r|   r^   r}   r   r   r   r   rZ   rl   rn   r   r   r   r   r   r   r   r   r   r   r   r   r   r   r   	Exceptionr   r   )rG   r   refresh_requestrefresh_responser   discovery_requestdiscovery_responsediscovery_urlsr   oauth_metadata_requestoauth_metadata_responseregistration_requestregistration_responser   r(   token_requestr   r   r   r   async_auth_flow  sb   






.z#OAuthClientProvider.async_auth_flow)rU   rv   )#r   r   r   r   requires_response_bodyrA   r   rD   r   r   rw   rx   r~   r   Responser   r   r   r   listr   r   r   r   r   r   r   rj   r   r   r   r   r   r   r   r   r   r   r   r{      sB    	
.
$r{   )4r   r;   r7   loggingr   r,   r.   rh   collections.abcr   r   r   dataclassesr   r   typingr   urllib.parser   r	   r
   ry   r   pydanticr   r   r   mcp.client.streamable_httpr   mcp.shared.authr   r   r   r   r   mcp.shared.auth_utilsr   r   	mcp.typesr   	getLoggerr   r   r   r   r!   r"   r#   rD   rO   Authr{   r   r   r   r   <module>   s8    
^