o
    i6q                     @   sn  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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Z0G dd deZ1eG dd dZ2G dd dej3Z4d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)	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   \/var/www/html/karishye-ai-python/venv/lib/python3.10/site-packages/mcp/client/auth/oauth2.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*   classmethodrA   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                    rF   )zStore tokens.Nr   )rH   rK   r   r   r    
set_tokensG   rJ   zTokenStorage.set_tokensc                    rF   )zGet stored client information.Nr   rG   r   r   r    get_client_infoK   rJ   zTokenStorage.get_client_infoclient_infoc                    rF   )zStore client information.Nr   )rH   rN   r   r   r    set_client_infoO   rJ   zTokenStorage.set_client_info)
r   r   r   r   r   rI   rL   r   rM   rO   r   r   r   r    rE   @   s    rE   c                   @   sj  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< 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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_versionrN   current_tokenstoken_expiry_time)default_factorylockr+   c                 C   s   t |}|j d|j S )z,Extract base URL by removing path component.://)r   schemenetloc)rH   rQ   parsedr   r   r    get_authorization_base_urlo   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]   )rH   re   r   r   r    update_token_expiryt   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]   rg   rG   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.)ri   r\   refresh_tokenrN   rG   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]   rG   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   rQ   rX   resourcerB   r   )rH   rq   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)rX   )rH   r[   r   r   r    should_include_resource_param   s
   
z*OAuthContext.should_include_resource_paramr+   NN)$r   r   r   r   rB   rC   r   rE   r   r   tuplerW   floatrX   r   rY   r   rZ   r[   rN   r   r\   r   r]   r   anyioLockr_   rd   rh   ri   rk   rm   rn   rs   rt   r   r   r   r    rP   T   s.   
 $
rP   c                   @   s2  e Zd ZdZdZ			dAd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fddZdejdee fddZdejdededB fddZdejdedB fddZdejdedB fddZdejde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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/d0Z!dejfd1d2Z"dejdefd3d4Z#dBd5d6Z$d7ejddfd8d9Z%d:edejfd;d<Z&dejddfd=d>Z'd7ejde(ejejf fd?d@Z)dS )COAuthClientProviderzw
    OAuth2 authentication for httpx.
    Handles OAuth flow with automatic client registration and token storage.
    TNrV   rQ   rR   rS   rT   rU   rW   c                 C   s    t ||||||d| _d| _dS )z!Initialize OAuth2 authentication.)rQ   rR   rS   rT   rU   rW   FN)rP   context_initialized)rH   rQ   rR   rS   rT   rU   rW   r   r   r    __init__   s   

zOAuthClientProvider.__init__init_responser+   c                 C   s~   g }|  |}|r|| t| jj}|j d|j }|jr3|jdkr3t|d|j }|| t|d}|| |S )a$  
        Build ordered list of URLs to try for protected resource metadata discovery.

        Per SEP-985, the client MUST:
        1. Try resource_metadata from WWW-Authenticate header (if present)
        2. Fall back to path-based well-known URI: /.well-known/oauth-protected-resource/{path}
        3. Fall back to root-based well-known URI: /.well-known/oauth-protected-resource

        Args:
            init_response: The initial 401 response from the server

        Returns:
            Ordered list of URLs to try for discovery
        r`   /z%/.well-known/oauth-protected-resource)	(_extract_resource_metadata_from_www_authappendr   r|   rQ   ra   rb   pathr
   )rH   r   urlswww_auth_urlrc   base_urlpath_based_urlroot_based_urlr   r   r    (_build_protected_resource_discovery_urls   s   




z<OAuthClientProvider._build_protected_resource_discovery_urls
field_namec                 C   sF   |j d}|s
dS | d}t||}|r!|dp |dS dS )z
        Extract field from WWW-Authenticate header.

        Returns:
            Field value if found in WWW-Authenticate header, None otherwise
        zWWW-AuthenticateNz=(?:"([^"]+)"|([^\s,]+))      )headersgetresearchgroup)rH   r   r   www_auth_headerpatternmatchr   r   r    _extract_field_from_www_auth   s   
z0OAuthClientProvider._extract_field_from_www_authc                 C   s   |r|j dkr	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
          Nresource_metadata)status_coder   rH   r   r   r   r    r     s   z<OAuthClientProvider._extract_resource_metadata_from_www_authc                 C   s   |  |dS )z
        Extract scope parameter from WWW-Authenticate header as per RFC6750.

        Returns:
            Scope string if found in WWW-Authenticate header, None otherwise
        scope)r   r   r   r   r    _extract_scope_from_www_auth  s   z0OAuthClientProvider._extract_scope_from_www_authresponsec                    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: )r   areadr   model_validate_jsonr|   rX   authorization_serversrB   rZ   r   loggerwarningrequesturldebugr   rH   r   contentmetadatar   r   r    #_handle_protected_resource_response  s"   
	

z7OAuthClientProvider._handle_protected_resource_responsec                 C   sb   |  |}|dur|| jj_dS | jjdur*| jjjdur*d| jjj| jj_dS d| jj_dS )zKSelect scopes as outlined in the 'Scope Selection Strategy in the MCP spec.N )r   r|   rR   r   rX   scopes_supportedr6   )rH   r   www_authenticate_scoper   r   r    _select_scopes8  s   
z"OAuthClientProvider._select_scopesc                 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.r`   r   z'/.well-known/oauth-authorization-serverz!/.well-known/openid-configuration)
r|   rZ   rQ   r   ra   rb   r   r?   r   r
   )rH   r   rZ   rc   r   
oauth_path	oidc_pathoidc_fallbackr   r   r    _get_discovery_urlsM  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|   rN   rY   registration_endpointrB   rd   rQ   r
   rR   
model_dumphttpxRequest)rH   registration_urlauth_base_urlregistration_datar   r   r    _register_clienth  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: r   zInvalid registration response: )r   r   r#   textr   r   r|   rN   rS   rO   r   )rH   r   r   rN   er   r   r    _handle_registration_responsey  s   

z1OAuthClientProvider._handle_registration_responsec                    s*   |   I dH \}}| ||I dH }|S )zPerform the authorization flow.N)!_perform_authorization_code_grant"_exchange_token_authorization_code)rH   	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_type	client_idredirect_uristater*   code_challenge_methodrq   r   ?zState parameter mismatch: z != zNo authorization code received)r|   rR   redirect_urisr   rT   rU   rY   authorization_endpointrB   rd   rQ   r
   rN   r$   rA   r-   token_urlsafer   r*   rt   r[   rs   r   r	   compare_digestr)   )	rH   auth_endpointr   pkce_paramsr   auth_paramsauthorization_urlr   returned_stater   r   r    r     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|   rY   token_endpointrB   rd   rQ   r
   )rH   	token_urlr   r   r   r    _get_token_endpoint  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< | j jjrL| j jj|d< tjd	||d
didS )z9Build token exchange request for authorization_code flow.Nr   zMissing client infoauthorization_coder   )
grant_typer   r   r   r)   rq   client_secretr   r   !application/x-www-form-urlencodeddatar   )r|   rR   r   r   rN   r   updaterB   r   rt   r[   rs   r   r   r   )rH   r   r)   r   r   r   r   r    r     s,   
z6OAuthClientProvider._exchange_token_authorization_codec           	   
      s   |j dkr| I dH }|d}td|j  d| zH| I dH }t|}|jrO| jjjrOt	| jjj
 }t	|j
 }|| }|rOtd| || j_| j| | jj|I dH  W dS  tyx } ztd| d}~ww )zHandle token exchange response.r   Nzutf-8zToken exchange failed (z): z$Server granted unauthorized scopes: zInvalid token response: )r   r   r>   r"   r   r   r   r|   rR   setsplitr\   rh   rS   rL   r   )	rH   r   bodyr   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   rl   )r   rl   r   rq   r   r   r   r   r   )r|   r\   rl   r"   rN   rY   r   rB   rd   rQ   r
   r   rt   r[   rs   r   r   r   )rH   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   r   r   r|   rn   r   r   r   r\   rh   rS   rL   r   	exception)rH   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|   rS   rI   r\   rM   rN   r}   rG   r   r   r    _initialize7  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 AuthorizationN)r|   r\   rj   r   )rH   r   r   r   r    _add_auth_header=  s   z$OAuthClientProvider._add_auth_headerr   c                 C   s   t jd|ttidS )NGETr   )r   r   r   r   )rH   r   r   r   r    _create_oauth_metadata_requestB  s   z2OAuthClientProvider._create_oauth_metadata_requestc                    s&   |  I d H }t|}|| j_d S rv   )r   r   r   r|   rY   r   r   r   r    _handle_oauth_metadata_responseE  s   
z3OAuthClientProvider._handle_oauth_metadata_responsec              
   C  s  | j j4 I dH 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rz| |}d}|D ]}tjd|ttid}|V }	| |	I dH }|rs nqX|sztd| | |  }|D ]0}| |}
|
V }|jdkrz| |I dH  W  n ty   Y qw |jdk s|jd	kr nq|  I dH }|r|V }| |I dH  |  I dH V }| |I dH  W n ty   td
  w | | |V  nU|jdkr<|  |d}|dkr)z| | |  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 sYw   Y  dS )zHTTPX auth flow integration.NFr   r   r   zEProtected resource metadata discovery failed: no valid metadata foundr   i  i  zOAuth flow errori  errorinsufficient_scope)!r|   r_   r}   r   r   r   r   r[   rk   rm   r   r   r   r   r   r   r   r   r   r   r   r   r   r   r   r   r   r   r   	Exceptionr   r   r   )rH   r   refresh_requestrefresh_responser   discovery_urlsdiscovery_successr   discovery_requestdiscovery_responseoauth_metadata_requestoauth_metadata_responseregistration_requestregistration_responser   r   r   r   r    async_auth_flowJ  s   













JI0z#OAuthClientProvider.async_auth_flow)NNrV   ru   )*r   r   r   r   requires_response_bodyrB   r   rE   r   r   rw   rx   r~   r   Responselistr   r   r   r   ri   r   r   r   r   r   r   r   r   r   dictr   r   r   r   r   r   r   r   r   r   r  r   r   r   r    r{      sb    
%	5	
 
$r{   )5r   r<   r8   loggingr   r-   r/   rg   collections.abcr   r   r   dataclassesr   r   typingr   r   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$   rE   rP   Authr{   r   r   r   r    <module>   s8    
Z