o
    *ik                     @   s  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mZm	Z	m
Z
 ddlmZmZ ddlmZmZ ddlmZ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mZ dd	lm Z m!Z!m"Z"m#Z#m$Z$m%Z%m&Z&m'Z'm(Z(m)Z)m*Z*m+Z+m,Z,m-Z-m.Z. dd
l/m0Z0 ddl1m2Z2m3Z3m4Z4m5Z5m6Z6 ddl7m8Z8m9Z9m:Z: e;e<Z=G dd deZ>G dd deZ?eG dd dZ@G dd dejAZBdS )z|
OAuth2 Authentication implementation for HTTPX.

Implements authorization code flow with PKCE and automatic token refresh.
    N)AsyncGenerator	AwaitableCallable)	dataclassfield)AnyProtocol)quote	urlencodeurljoinurlparse)	BaseModelFieldValidationError)OAuthFlowErrorOAuthTokenError)8build_oauth_authorization_server_metadata_discovery_urls0build_protected_resource_metadata_discovery_urls$create_client_info_from_metadata_url"create_client_registration_requestcreate_oauth_metadata_requestextract_field_from_www_auth'extract_resource_metadata_from_www_authextract_scope_from_www_authget_client_metadata_scopeshandle_auth_metadata_response"handle_protected_resource_responsehandle_registration_responsehandle_token_response_scopesis_valid_client_metadata_urlshould_use_client_metadata_url)MCP_PROTOCOL_VERSION)OAuthClientInformationFullOAuthClientMetadataOAuthMetadata
OAuthTokenProtectedResourceMetadata)calculate_token_expirycheck_resource_allowedresource_url_from_server_urlc                   @   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:   S/home/ubuntu/veenaModal/venv/lib/python3.10/site-packages/mcp/client/auth/oauth2.py	<genexpr>B   s   $ z*PKCEParameters.generate.<locals>.<genexpr>r,   =)r/   r0   )
joinrangehashlibsha256encodedigestbase64urlsafe_b64encodedecoderstrip)clsr/   rC   r0   r:   r:   r;   generate?   s   zPKCEParameters.generateN)r1   r*   )__name__
__module____qualname____doc__r   r/   str__annotations__r0   classmethodrI   r:   r:   r:   r;   r*   9   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.r1   Nc                       dS )zGet stored tokens.Nr:   selfr:   r:   r;   
get_tokensK      zTokenStorage.get_tokenstokensc                    rR   )zStore tokens.Nr:   )rT   rW   r:   r:   r;   
set_tokensO   rV   zTokenStorage.set_tokensc                    rR   )zGet stored client information.Nr:   rS   r:   r:   r;   get_client_infoS   rV   zTokenStorage.get_client_infoclient_infoc                    rR   )zStore client information.Nr:   )rT   rZ   r:   r:   r;   set_client_infoW   rV   zTokenStorage.set_client_info)
rJ   rK   rL   rM   r%   rU   rX   r"   rY   r[   r:   r:   r:   r;   rQ   H   s    rQ   c                   @   s  e Zd ZU dZeed< eed< eed< eege	d f dB ed< eg e	e
eedB f  f dB 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< dZedB ed< eejdZej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)d$e%eef d%e%eef dB de
e%eef e%eef f fd&d'Z&dS )*OAuthContextzOAuth flow context.
server_urlclient_metadatastorageNredirect_handlercallback_handler     r@timeoutclient_metadata_urlprotected_resource_metadataoauth_metadataauth_server_urlprotocol_versionrZ   current_tokenstoken_expiry_time)default_factorylockr1   c                 C   s   t |}|j d|j S )z,Extract base URL by removing path component.z://)r   schemenetloc)rT   r]   parsedr:   r:   r;   get_authorization_base_urlx   s   z'OAuthContext.get_authorization_base_urltokenc                 C   s   t |j| _dS )z4Update token expiry time using shared util function.N)r'   
expires_inrj   )rT   rq   r:   r:   r;   update_token_expiry}   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.)boolri   access_tokenrj   timerS   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.)rt   ri   refresh_tokenrZ   rS   r:   r:   r;   can_refresh_token   s   zOAuthContext.can_refresh_tokenc                 C   s   d| _ d| _dS )zClear current tokens.N)ri   rj   rS   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)   r]   re   resourcerN   r(   )rT   r}   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)re   )rT   rh   r:   r:   r;   should_include_resource_param   s
   
z*OAuthContext.should_include_resource_paramdataheadersc                 C   s   |du ri }| j s||fS | j j}|dkrQ| j jrQ| j jrQt| j jdd}t| j jdd}| d| }t|  }d| |d< dd	 |	 D }||fS |d
kr_| j jr_| j j|d< ||fS )zPrepare authentication for token requests.

        Args:
            data: The form data to send
            headers: Optional headers dict to update

        Returns:
            Tuple of (updated_data, updated_headers)
        Nclient_secret_basicr2   )safe:zBasic Authorizationc                 S   s   i | ]\}}|d kr||qS )client_secretr:   )r8   kvr:   r:   r;   
<dictcomp>   s    z3OAuthContext.prepare_token_auth.<locals>.<dictcomp>client_secret_postr   )
rZ   token_endpoint_auth_method	client_idr   r	   rD   	b64encoderB   rF   items)rT   r   r   auth_method
encoded_idencoded_secretcredentialsencoded_credentialsr:   r:   r;   prepare_token_auth   s    zOAuthContext.prepare_token_authr1   NN)'rJ   rK   rL   rM   rN   rO   r#   rQ   r   r   tuplerc   floatrd   re   r&   rf   r$   rg   rh   rZ   r"   ri   r%   rj   r   anyioLockrl   rp   rs   rt   rw   ry   rz   r   r   dictr   r:   r:   r:   r;   r\   \   s@   
 $

r\   c                   @   sv  e Zd ZdZdZ				d-dedededeege	d f dB d	eg e	e
eedB f  f dB d
ededB fddZdejdefddZdejfddZde
eef fddZdefddZi ddededeeef dB 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d.d$d%Zd&ejddfd'd(Zdejddfd)d*Zd&ejdeejejf fd+d,ZdS )/OAuthClientProviderzw
    OAuth2 authentication for httpx.
    Handles OAuth flow with automatic client registration and token storage.
    TNrb   r]   r^   r_   r`   ra   rc   rd   c              	   C   s@   |durt |std| t|||||||d| _d| _dS )al  Initialize OAuth2 authentication.

        Args:
            server_url: The MCP server URL.
            client_metadata: OAuth client metadata for registration.
            storage: Token storage implementation.
            redirect_handler: Handler for authorization redirects.
            callback_handler: Handler for authorization callbacks.
            timeout: Timeout for the OAuth flow.
            client_metadata_url: URL-based client ID. When provided and the server
                advertises client_id_metadata_document_supported=true, this URL will be
                used as the client_id instead of performing dynamic client registration.
                Must be a valid HTTPS URL with a non-root pathname.

        Raises:
            ValueError: If client_metadata_url is provided but not a valid HTTPS URL
                with a non-root pathname.
        NzMclient_metadata_url must be a valid HTTPS URL with a non-root pathname, got: )r]   r^   r_   r`   ra   rc   rd   F)r   
ValueErrorr\   context_initialized)rT   r]   r^   r_   r`   ra   rc   rd   r:   r:   r;   __init__   s   
	zOAuthClientProvider.__init__responser1   c                    s   |j dkr:z| I dH }t|}|| j_|jr#t|jd | j_W dS  t	y9   t
d|jj  Y dS w |j dkrLt
d|jj d	 dS td
|j  )z
        Handle protected resource metadata discovery response.

        Per SEP-985, supports fallback when discovery fails at one URL.

        Returns:
            True if metadata was successfully discovered, False if we should try next URL
           Nr   Tz'Invalid protected resource metadata at Fi  z)Protected resource metadata not found at z, trying next URLz,Protected Resource Metadata request failed: )status_codeareadr&   model_validate_jsonr   re   authorization_serversrN   rg   r   loggerwarningrequesturldebugr   rT   r   contentmetadatar:   r:   r;   #_handle_protected_resource_response  s&   
	


z7OAuthClientProvider._handle_protected_resource_responsec                    s*   |   I dH \}}| ||I dH }|S )zPerform the authorization flow.N)!_perform_authorization_code_grant"_exchange_token_authorization_code)rT   	auth_coder/   token_requestr:   r:   r;   _perform_authorization.  s   z*OAuthClientProvider._perform_authorizationc           	         sj  | j jjdu rtd| 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}| j jsBtdt }td}d| j jjt| j jjd	 ||jd
d}| j | j jrn| j  |d< | j jjrz| 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.N6No redirect URIs provided for authorization code grantz9No redirect handler provided for authorization code grantz9No callback handler provided for authorization code grantz
/authorizez*No client info available for authorization    coder   S256)response_typer   redirect_uristater0   code_challenge_methodr}   scope?zState parameter mismatch: z != zNo authorization code received)r   r^   redirect_urisr   r`   ra   rf   authorization_endpointrN   rp   r]   r   rZ   r*   rI   r3   token_urlsafer   r0   r   rh   r   r   r
   compare_digestr/   )	rT   auth_endpointauth_base_urlpkce_paramsr   auth_paramsauthorization_urlr   returned_stater:   r:   r;   r   4  sD   




z5OAuthClientProvider._perform_authorization_code_grantc                 C   sB   | j jr| j jjrt| j jj}|S | j | j j}t|d}|S )N/token)r   rf   token_endpointrN   rp   r]   r   )rT   	token_urlr   r:   r:   r;   _get_token_endpointi  s   
z'OAuthClientProvider._get_token_endpoint)
token_datar   r/   r   c                   s   | j jjdu rtd| j jstd|  }|pi }|d|t| j jjd | j jj|d | j 	| j j
r@| j  |d< dd	i}| j ||\}}tjd
|||dS )z9Build token exchange request for authorization_code flow.Nr   zMissing client infoauthorization_coder   )
grant_typer   r   r   r/   r}   Content-Type!application/x-www-form-urlencodedPOSTr   r   )r   r^   r   r   rZ   r   updaterN   r   r   rh   r   r   httpxRequest)rT   r   r/   r   r   r   r:   r:   r;   r   q  s(   z6OAuthClientProvider._exchange_token_authorization_codec                    st   |j dkr| I dH }|d}td|j  d| t|I dH }|| j_| j| | jj	|I dH  dS )zHandle token exchange response.r   Nzutf-8zToken exchange failed (z): )
r   r   rF   r   r   r   ri   rs   r_   rX   )rT   r   body	body_texttoken_responser:   r:   r;   _handle_token_response  s   

z*OAuthClientProvider._handle_token_responsec                    s   | j jr
| j jjstd| j jr| j 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rT| j  |d< ddi}| j ||\}}tjd	|||d
S )zBuild token refresh request.zNo refresh token availablezNo client info availabler   rx   )r   rx   r   r}   r   r   r   r   )r   ri   rx   r   rZ   r   rf   r   rN   rp   r]   r   r   rh   r   r   r   r   )rT   r   r   refresh_datar   r:   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   r   r   r   rz   r   r%   r   ri   rs   r_   rX   r   	exception)rT   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   r_   rU   ri   rY   rZ   r   rS   r:   r:   r;   _initialize  s   
zOAuthClientProvider._initializer   c                 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 r   N)r   ri   ru   r   )rT   r   r:   r:   r;   _add_auth_header  s   z$OAuthClientProvider._add_auth_headerc                    s&   |  I d H }t|}|| j_d S r   )r   r$   r   r   rf   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  rF| | |V }|jdkrWzt|}t|| j j}|D ]2}t|}|V }	t|	I dH }
|
r|
| j _t|
jdks|J t|
jd | j _ n	td|  q]t| j j| j j}|D ](}t|}|V }t|I dH \}}|s n|r|r|| j _ n	td|  qtt|| j j| j j| j j_ | j j!s.t"| j j| j j#rtd| j j#  t$| j j#| j jj%d}|| j _!| j j&'|I dH  n(t(| j j| j j| j )| j j}|V }t*|I dH }|| j _!| j j&'|I dH  | + I dH V }| ,|I dH  W n t-yM   t.d	  w | | |V  n[|jd
krt/|d}|dkrztt|| j j| j j_ | + 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 W d  I dH  dS 1 I dH sw   Y  dS )zHTTPX auth flow integration.NFi  r   z.Protected resource metadata discovery failed: z!OAuth metadata discovery failed: z"Using URL-based client ID (CIMD): )r   zOAuth flow errori  errorinsufficient_scope)0r   rl   r   r   r   getr!   rh   rw   ry   r   r   r   r   r   r   r]   r   r   re   lenr   rN   rg   r   r   r   r   rf   r   r   r^   r   rZ   r    rd   r   r   r_   r[   r   rp   r   r   r   	Exceptionr   r   )rT   r   refresh_requestrefresh_responser   www_auth_resource_metadata_urlprm_discovery_urlsr   discovery_requestdiscovery_responseprmasm_discovery_urlsoauth_metadata_requestoauth_metadata_responseokasmclient_informationregistration_requestregistration_responser   r   r:   r:   r;   async_auth_flow  s   










 on0z#OAuthClientProvider.async_auth_flow)NNrb   Nr   ) rJ   rK   rL   rM   requires_response_bodyrN   r#   rQ   r   r   r   r   r   r   Responsert   r   r   r   r   r   r   r   r   r   r   r   r   r   r   r   r   r:   r:   r:   r;   r      sV    
- 5	

$r   )CrM   rD   r@   loggingr3   r5   rv   collections.abcr   r   r   dataclassesr   r   typingr   r   urllib.parser	   r
   r   r   r   r   pydanticr   r   r   mcp.client.auth.exceptionsr   r   mcp.client.auth.utilsr   r   r   r   r   r   r   r   r   r   r   r   r   r   r    mcp.client.streamable_httpr!   mcp.shared.authr"   r#   r$   r%   r&   mcp.shared.auth_utilsr'   r(   r)   	getLoggerrJ   r   r*   rQ   r\   Authr   r:   r:   r:   r;   <module>   s2    D
|