o
    i0                     @  s@  d dl 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
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mZm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gZ(e'e)Z*G dd de+Z,	d d!ddZ-G dd deZ.G dd deZ/dS )"    )annotationsN)AsyncGenerator)Any)urlparse)PydanticAdapter)AsyncKeyValue)MemoryStore)OAuthClientProviderTokenStorage)McpHttpClientFactory)OAuthClientInformationFullOAuthClientMetadata
OAuthToken)
AnyHttpUrl)override)Server)OAuthCallbackResultcreate_oauth_callback_server)find_available_port)
get_loggerOAuthc                   @  s   e Zd ZdZdS )ClientNotFoundErrorzARaised when OAuth client credentials are not found on the server.N)__name__
__module____qualname____doc__ r   r   _/var/www/html/karishye-ai-python/venv/lib/python3.10/site-packages/fastmcp/client/auth/oauth.pyr   %   s    r   mcp_urlstrhttpx_kwargsdict[str, Any] | Nonereturnboolc              	     s   t jdi |pi 4 I dH N}z8|j| ddI dH }|jdv r,W W d  I dH  dS d|jv r=W W d  I dH  dS W W d  I dH  dS  t jy\   Y W d  I dH  dS w 1 I dH scw   Y  dS )	z
    Check if the MCP endpoint requires authentication by making a test request.

    Returns:
        True if auth appears to be required, False otherwise
    Ng      @)timeout)i  i  TzWWW-AuthenticateFr   )httpxAsyncClientgetstatus_codeheadersRequestError)r   r    clientresponser   r   r   check_if_auth_required)   s"   	


r-   c                   @  s   e Zd ZU ded< ded< ded< ded< d$ddZd%ddZd%ddZd&ddZed'ddZ	ed(ddZ
ed)ddZed*d!d"Zd#S )+TokenStorageAdapterr   _server_urlr   _key_value_storezPydanticAdapter[OAuthToken]_storage_oauth_tokenz+PydanticAdapter[OAuthClientInformationFull]_storage_client_infoasync_key_value
server_urlc                 C  s<   || _ || _tt d|tdd| _tt d|tdd| _d S )Nzmcp-oauth-tokenT)default_collection	key_valuepydantic_modelraise_on_validation_errorzmcp-oauth-client-info)r/   r0   r   r   r1   r   r2   )selfr3   r4   r   r   r   __init__M   s   zTokenStorageAdapter.__init__r"   c                 C     | j  dS )Nz/tokensr/   r9   r   r   r   _get_token_cache_key]      z(TokenStorageAdapter._get_token_cache_keyc                 C  r;   )Nz/client_infor<   r=   r   r   r   _get_client_info_cache_key`   r?   z.TokenStorageAdapter._get_client_info_cache_keyNonec                   s6   | j j|  dI d H  | jj|  dI d H  d S N)key)r1   deleter>   r2   r@   r=   r   r   r   clearc   s   zTokenStorageAdapter.clearOAuthToken | Nonec                      | j j|  dI d H S rB   )r1   r'   r>   r=   r   r   r   
get_tokensg   s   zTokenStorageAdapter.get_tokenstokensr   c                   s$   | j j|  ||jdI d H  d S N)rC   valuettl)r1   putr>   
expires_in)r9   rI   r   r   r   
set_tokensk   s   zTokenStorageAdapter.set_tokens!OAuthClientInformationFull | Nonec                   rG   rB   )r2   r'   r@   r=   r   r   r   get_client_infos   s   z#TokenStorageAdapter.get_client_infoclient_infor   c                   s>   d }|j r|j tt  }| jj|  ||dI d H  d S rJ   )client_secret_expires_atinttimer2   rM   r@   )r9   rR   rL   r   r   r   set_client_infoy   s   z#TokenStorageAdapter.set_client_infoN)r3   r   r4   r   )r"   r   r"   rA   )r"   rF   )rI   r   r"   rA   )r"   rP   )rR   r   r"   rA   )r   r   r   __annotations__r:   r>   r@   rE   r   rH   rO   rQ   rV   r   r   r   r   r.   G   s"   
 



r.   c                      s`   e Zd ZdZ						d"d# fddZd$ fddZd%ddZd&ddZd' fd d!Z  Z	S )(r   z
    OAuth client provider for MCP servers with browser-based authentication.

    This class provides OAuth authentication for FastMCP clients by opening
    a browser for user authorization and running a local callback server.
    NFastMCP Clientr   r   scopesstr | list[str] | Noneclient_nametoken_storageAsyncKeyValue | Noneadditional_client_metadatar!   callback_port
int | Nonehttpx_client_factoryMcpHttpClientFactory | Nonec                   s   t |}|j d|j }	|ptj| _|pt | _d| j d}
t|t	r+d
|}n|dur4t|}nd}td|t|
gddgd	g|d
|pHi }|pOt }t|traddlm} |ddd t||	d| _|	| _t j|	|| j| j| jd dS )al  
        Initialize OAuth client provider for an MCP server.

        Args:
            mcp_url: Full URL to the MCP endpoint (e.g. "http://host/mcp/sse/")
            scopes: OAuth scopes to request. Can be a
            space-separated string or a list of strings.
            client_name: Name for this client during registration
            token_storage: An AsyncKeyValue-compatible token store, tokens are stored in memory if not provided
            additional_client_metadata: Extra fields for OAuthClientMetadata
            callback_port: Fixed port for OAuth callback (default: random available port)
        z://zhttp://localhost:z	/callback N authorization_coderefresh_tokencode)r\   redirect_urisgrant_typesresponse_typesscoper   )warnzUsing in-memory token storage -- tokens will be lost when the client restarts. For persistent storage across multiple MCP servers, provide an encrypted AsyncKeyValue backend. See https://gofastmcp.com/clients/auth/oauth#token-storage for details.   )message
stacklevel)r3   r4   )r4   client_metadatastorageredirect_handlercallback_handlerr   )r   schemenetlocr%   r&   rb   r   redirect_port
isinstancelistjoinr   r   r   r   warningsrm   r.   token_storage_adapterserver_base_urlsuperr:   rs   rt   )r9   r   rZ   r\   r]   r_   r`   rb   
parsed_urlr}   redirect_uri
scopes_strrq   rm   	__class__r   r   r:      sJ   




zOAuth.__init__r"   rA   c                   s@   t   I dH  | jjr| jjjr| j| jj dS dS dS )zBLoad stored tokens and client info, properly setting token expiry.N)r~   _initializecontextcurrent_tokensrN   update_token_expiryr=   r   r   r   r      s
   zOAuth._initializeauthorization_urlc              	     s   |   4 I dH +}|j|ddI dH }|jdkrtd|jdvr*td|j W d  I dH  n1 I dH s:w   Y  td|  t| dS )	zIOpen browser for authorization, with pre-flight check for invalid client.NF)follow_redirectsi  z8OAuth client not found - cached credentials may be stale)   i.  i/  i3  i4  z#Unexpected authorization response: zOAuth authorization URL: )	rb   r'   r(   r   RuntimeErrorloggerinfo
webbrowseropen)r9   r   r+   r,   r   r   r   rs      s   


(zOAuth.redirect_handlertuple[str, str | None]c                   s  t  }t }t| j| j||d}t 4 I dH }||j t	
d| j  d}znzFt|7 | I dH  |jrA|j|j|jfW  d   W W d|_tdI dH  |j  W  d  I dH  S 1 smw   Y  W n ty } z	td| d|d}~ww W d|_tdI dH  |j  nd|_tdI dH  |j  w W d  I dH  td	1 I dH sw   Y  td	)
z4Handle OAuth callback and return (auth_code, state).)portr4   result_containerresult_readyNu7   🎧 OAuth callback server started on http://localhost:g     r@Tg?zOAuth callback timed out after z secondsz+OAuth callback handler could not be started)r   anyioEventr   rw   r}   create_task_group
start_soonserver   r   
fail_afterwaiterrorrh   stateshould_exitsleepcancel_scopecancelTimeoutErrorr   )r9   resultr   servertgTIMEOUTer   r   r   rt      s\   





zOAuth.callback_handlerrequesthttpx.Request-AsyncGenerator[httpx.Request, httpx.Response]c                  s   z#t  |}d}	 z||I dH }|V }W n ty#   Y W dS w q ty^   td d| _| j	 I dH  t  |}d}	 z||I dH }|V }W n ty\   Y Y dS w qDw )zHTTPX auth flow with automatic retry on stale cached credentials.

        If the OAuth flow fails due to invalid/stale client credentials,
        clears the cache and retries once with fresh registration.
        NTz@OAuth client not found on server, clearing cache and retrying...F)
r~   async_auth_flowasendStopAsyncIterationr   r   debug_initializedr|   rE   )r9   r   genr,   yielded_requestr   r   r   r   !  s:   

zOAuth.async_auth_flow)NrY   NNNN)r   r   rZ   r[   r\   r   r]   r^   r_   r!   r`   ra   rb   rc   rW   )r   r   r"   rA   )r"   r   )r   r   r"   r   )
r   r   r   r   r:   r   rs   rt   r   __classcell__r   r   r   r   r      s    
M
	
')N)r   r   r    r!   r"   r#   )0
__future__r   rU   r   collections.abcr   typingr   urllib.parser   r   r%   key_value.aio.adapters.pydanticr   key_value.aio.protocolsr   key_value.aio.stores.memoryr   mcp.client.authr	   r
   mcp.shared._httpx_utilsr   mcp.shared.authr   r   r   pydanticr   typing_extensionsr   uvicorn.serverr   fastmcp.client.oauth_callbackr   r   fastmcp.utilities.httpr   fastmcp.utilities.loggingr   __all__r   r   	Exceptionr   r-   r.   r   r   r   r   r   <module>   s6    @