XjndZddlZddlZddlZddlZddlZddlZddlmZm Z m Z ddl m Z m Z ddlmZmZddlmZmZmZmZddlZddlZddlmZmZmZddlmZmZdd lm 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.dd l/m0Z0dd l1m2Z2m3Z3m4Z4m5Z5m6Z6dd l7m8Z8m9Z9m:Z:ejve<Z=Gd deZ>GddeZ?e GddZ@GddejZBy)z| OAuth2 Authentication implementation for HTTPX. Implements authorization code flow with PKCE and automatic token refresh. N)AsyncGenerator AwaitableCallable) dataclassfield)AnyProtocol)quote urlencodeurljoinurlparse) BaseModelFieldValidationError)OAuthFlowErrorOAuthTokenError)8build_oauth_authorization_server_metadata_discovery_urls0build_protected_resource_metadata_discovery_urls$create_client_info_from_metadata_url"create_client_registration_requestcreate_oauth_metadata_requestextract_field_from_www_auth'extract_resource_metadata_from_www_authextract_scope_from_www_authget_client_metadata_scopeshandle_auth_metadata_response"handle_protected_resource_responsehandle_registration_responsehandle_token_response_scopesis_valid_client_metadata_urlshould_use_client_metadata_url)MCP_PROTOCOL_VERSION)OAuthClientInformationFullOAuthClientMetadata OAuthMetadata OAuthTokenProtectedResourceMetadata)calculate_token_expirycheck_resource_allowedresource_url_from_server_urlcdeZdZUdZedddZeed<edddZeed<e d dZ y ) PKCEParametersz.PKCE (Proof Key for Code Exchange) parameters..+) min_length max_length code_verifiercode_challengecdjdtdD}tj|j j }t j|jjd}|||S)zGenerate new PKCE parameters.c3K|];}tjtjtjzdz=yw)z-._~N)secretschoicestring ascii_lettersdigits).0_s j/mnt/ssd/data/Dropbox/adrian/vault-secondbrain/venv/lib/python3.12/site-packages/mcp/client/auth/oauth2.py z*PKCEParameters.generate..Bs/rbcv/C/Cfmm/SV\/\ ]rsAAr.=)r1r2) joinrangehashlibsha256encodedigestbase64urlsafe_b64encodedecoderstrip)clsr1rEr2s r=generatezPKCEParameters.generate?sprglmpgqrr  4 4 67>>@11&9@@BII#N~NNN)returnr,) __name__ __module__ __qualname____doc__rr1str__annotations__r2 classmethodrKrLr=r,r,9s?8srcBM3BsCNCCOOrLr,cTeZdZdZdedzfdZdeddfdZdedzfdZdeddfd Z y) TokenStoragez+Protocol for token storage implementations.rMNc Kyw)zGet stored tokens.NrUselfs r= get_tokenszTokenStorage.get_tokensK  tokensc Kyw)z Store tokens.NrU)rZr^s r= set_tokenszTokenStorage.set_tokensOr\r]c Kyw)zGet stored client information.NrUrYs r=get_client_infozTokenStorage.get_client_infoSr\r] client_infoc Kyw)zStore client information.NrU)rZrcs r=set_client_infozTokenStorage.set_client_infoWr\r]) rNrOrPrQr&r[r`r#rbrerUrLr=rWrWHsP5 *t"3  z d  'AD'H  1K PT rLrWc (eZdZUdZeed<eed<eed<eege dfdzed<ege e eedzffdzed<dZ e ed <dZ edzed <dZedzed <dZedzed <dZedzed <dZedzed<dZedzed<dZedzed<dZe dzed<eej6Zej6ed<dedefdZdeddfdZdefdZ defdZ!d dZ"defdZ#d!dedzdefdZ$ d!de%eefde%eefdzde e%eefe%eefffdZ&y)" OAuthContextzOAuth flow context. server_urlclient_metadatastorageNredirect_handlercallback_handlerr@timeoutclient_metadata_urlprotected_resource_metadataoauth_metadataauth_server_urlprotocol_versionrccurrent_tokenstoken_expiry_time)default_factorylockrMcNt|}|jd|jS)z,Extract base URL by removing path component.z://)r schemenetloc)rZrhparseds r=get_authorization_base_urlz'OAuthContext.get_authorization_base_urlxs%*%--FMM?33rLtokenc8t|j|_y)z4Update token expiry time using shared util function.N)r( expires_inru)rZr}s r=update_token_expiryz OAuthContext.update_token_expiry}s!78H8H!IrLct|jxrH|jjxr0|j xs!t j|jkS)z Check if current token is valid.)boolrt access_tokenrutimerYs r=is_token_validzOAuthContext.is_token_validsU    V##00 V+++Ttyy{d>T>T/T  rLcxt|jxr$|jjxr |jS)z Check if token can be refreshed.)rrt refresh_tokenrcrYs r=can_refresh_tokenzOAuthContext.can_refresh_tokens0D''bD,?,?,M,MbRVRbRbccrLc d|_d|_y)zClear current tokens.N)rtrurYs r= clear_tokenszOAuthContext.clear_tokenss"!%rLct|j}|jrD|jjr.t |jj}t ||r|}|S)zGet resource URL for RFC 8707. Uses PRM resource if it's a valid parent, otherwise uses canonical server URL. requested_resourceconfigured_resource)r*rhrpresourcerRr))rZr prm_resources r=get_resource_urlzOAuthContext.get_resource_urlsV 0@  + +0P0P0Y0Yt??HHIL%Wcd'rLc,|jy|sy|dk\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 TFz 2025-06-18)rp)rZrss r=should_include_resource_paramz*OAuthContext.should_include_resource_params(  + + 7  <//rLdataheadersc|i}|js||fS|jj}|dk(r|jjr|jjrt |jjd}t |jjd}|d|}t j |jj}d||d<|jD cic]\}} |dk7s || }}} ||fS|dk(r/|jjr|jj|d<||fScc} }w) 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) client_secret_basicr4)safe:zBasic Authorization client_secretclient_secret_post) rctoken_endpoint_auth_method client_idrr rF b64encoderDrHitems) rZrr auth_method encoded_idencoded_secret credentialsencoded_credentialskvs r=prepare_token_authzOAuthContext.prepare_token_authsD ?G= &&AA / /D4D4D4N4NSWScScSqSqt//99CJ"4#3#3#A#AKN'L.)9:K"("2"2;3E3E3G"H"O"O"Q )/0C/D'EGO $%)ZZ\JTQQ/5IAqDJDJ W} 0 0T5E5E5S5S$($4$4$B$BD !W} Ks 1 E?ErMNN)'rNrOrPrQrRrSr$rWrrtuplernfloatrorpr'rqr%rrrsrcr#rtr&ruranyioLockrwr|rrrrrrrdictrrUrLr=rgrg\sO(( uio56==r9U3d ?-C#DDELLGU&*t*EI!:T!AH+/NMD(/"&OS4Z&#'cDj'6:K+d29)-NJ%,&*ut|*UZZ8D%**84S4S4 JJJ  d4d& # 0cDj0TX0(FJ"cN"-1#s(^d-B" tCH~tCH~- ."rLrgcLeZdZdZdZ d!dedededeege dfdzdege e eedzffdzd e d edzfd Z d e d dfdZdej d efdZd ej&fdZd e eeffdZd efdZiddededeeefdzd ej&fdZdej d dfdZd ej&fdZdej d efdZd"dZdej&d dfdZdej d dfdZdej&d e ej&ej ffd Z!y)#OAuthClientProviderzw OAuth2 authentication for httpx. Handles OAuth flow with automatic client registration and token storage. TNrhrirjrkrlrnroc v|t|std|t||||||||_d|_y)alInitialize 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: )rhrirjrkrlrnroF)r ValueErrorrgcontext _initialized)rZrhrirjrkrlrnros r=__init__zOAuthClientProvider.__init__sY:  *3OPc3d_`s_tu $!+-- 3  "rLprmrMcK|jrt|jnd}|syt|jj}t ||st d|d|yw)z?Validate that PRM resource matches the server URL per RFC 8707.NrzProtected resource z does not match expected )rrRr*rrhr)r)rZrrdefault_resources r=_validate_resource_matchz,OAuthClientProvider._validate_resource_matchse,/LLs3<<(d  7 8O8OP%9I_kl #6|nD]^n]o!pq qmsA%A'responsec2K|jdk(rs |jd{}tj|}||j_|j r't|j d|j_y|jdk(r.tjd|jjd ytd |j7#t$r/tjd|jjYywxYww) 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 NrTz'Invalid protected resource metadata at Fiz)Protected resource metadata not found at z, trying next URLz,Protected Resource Metadata request failed: ) status_codeareadr'model_validate_jsonrrpauthorization_serversrRrrrloggerwarningrequesturldebugrrZrcontentmetadatas r=#_handle_protected_resource_responsez7OAuthClientProvider._handle_protected_resource_responses   3 &  ( 004HHQ;C 81136x7U7UVW7X3YDLL0  ! !S ( LLDXEUEUEYEYDZZkl m!>x?S?S>TU #1# !HIYIYI]I]H^_` s:DCCACADC5DDDDcK|jd{\}}|j||d{}|S7#7w)zPerform the authorization flow.N)!_perform_authorization_code_grant"_exchange_token_authorization_code)rZ auth_coder1 token_requests r=_perform_authorizationz*OAuthClientProvider._perform_authorization7sB)-)O)O)Q#Q ="EEiQ^__ $R_s>:><>>cK|jjj td|jjs td|jj s td|jj rJ|jj jr*t|jj j}n;|jj|jj}t|d}|jjs tdtj}tj d}d|jjj"t|jjjd ||j$d d }|jj'|jj(r|jj+|d <|jjj,r#|jjj,|d <|dt/|}|jj |d{|jj d{\}}|tj0||std|d||s td||j2fS7m7Mw)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 coderS256) response_typer redirect_uristater2code_challenge_methodrscope?zState parameter mismatch: z != zNo authorization code received)rri redirect_urisrrkrlrqauthorization_endpointrRr|rhr rcr,rKr6 token_urlsaferr2rrsrrr compare_digestr1) rZ auth_endpoint auth_base_url pkce_paramsr auth_paramsauthorization_urlrreturned_states r=rz5OAuthClientProvider._perform_authorization_code_grant=sO << ' ' 5 5 = !YZ Z||,, !\] ]||,, !\] ] << & &4<<+F+F+]+] ; ; R RSM LLCCDLLD[D[\M#M<@M||'' !MN N%--/ %%b)$11;; < < J J1 MN)88%+   << 5 5dll6S6S T&*ll&C&C&EK # << ' ' - -#'<<#?#?#E#EK ,oQy/E.FGll++,=>>>+/,,*G*G*I$I! >  !)?)?PU)V #=n=MTRWQX!YZ Z !AB B+3333 ?%Js%I+K-K.!KKA KKc>|jjrK|jjjr+t|jjj}|S|jj |jj }t |d}|S)N/token)rrqtoken_endpointrRr|rhr )rZ token_urlrs r=_get_token_endpointz'OAuthClientProvider._get_token_endpointrsu << & &4<<+F+F+U+UDLL77FFGI!LLCCDLLD[D[\M x8IrL) token_datarr1rcK|jjj td|jjs td|j }|xsi}|j d|t|jjjd|jjj|d|jj|jjr|jj|d<ddi}|jj||\}}tjd ||| Sw) z9Build token exchange request for authorization_code flow.rzMissing client infoauthorization_coder) grant_typerrrr1r Content-Type!application/x-www-form-urlencodedPOSTrr)rrirrrcrupdaterRrrrsrrhttpxRequest)rZrr1rrrs r=rz6OAuthClientProvider._exchange_token_authorization_codezs << ' ' 5 5 = !YZ Z||'' !67 7,,. %2 2! #DLL$@$@$N$Nq$Q R!\\55??!.    << 5 5dll6S6S T%)\\%B%B%DJz ""#FG"ll==j'R G}}VYZQQsE E cK|jdk7rD|jd{}|jd}td|jd|t |d{}||j _|j j||j jj|d{y77`7 w)zHandle token exchange response.rNzutf-8zToken exchange failed (z): ) rrrHrrrrtrrjr`)rZrbody body_texttoken_responses r=_handle_token_responsez*OAuthClientProvider._handle_token_responses   3 &!))D G,I!$;Hs4#C C>C $C%AC =C>C C C cK|jjr |jjjs td|jjr |jjj s td|jj rJ|jj jr*t|jj j}n;|jj|jj}t|d}d|jjj|jjj d}|jj|jjr|jj|d<ddi}|jj||\}}t!j"d ||| Sw) zBuild token refresh request.zNo refresh token availablezNo client info availablerr)rrrrrrrr)rrtrrrcrrqrrRr|rhr rrsrrrr)rZrr refresh_datars r=_refresh_tokenz"OAuthClientProvider._refresh_tokensc||**$,,2M2M2[2[!">? ?||''t||/G/G/Q/Q!"<= = << & &4<<+F+F+U+UDLL77FFGI LLCCDLLD[D[\M x8I*!\\88FF11;;(  << 5 5dll6S6S T'+||'D'D'FL $"#FG $ ? ? g V g}}VY\7SSsG(G*c6K|jdk7r=tjd|j|jj y |j d{}t j|}||j_|jj||jjj|d{y7s7#t$r2tjd|jj YywxYww)z:Handle token refresh response. Returns True if successful.rzToken refresh failed: FNTzInvalid refresh response)rrrrrrr&rrtrrjr`r exception)rZrrrs r=_handle_refresh_responsez,OAuthClientProvider._handle_refresh_responses   3 & NN3H4H4H3IJ K LL % % ' $NN,,G';;GDN*8DLL ' LL , ,^ <,,&&11.A A A- B    7 8 LL % % ' sOA DC#C$A-CCCDCC8DDDDcK|jjjd{|j_|jjj d{|j_d|_y7V7w)z#Load stored tokens and client info.NT)rrjr[rtrbrcrrYs r= _initializezOAuthClientProvider._initializes\,0LL,@,@,K,K,M&M #)-)=)=)M)M)O#O   'N#Os!(BB:B%B&BBrc|jjrR|jjjr1d|jjj|jd<yyy)z B B 44: 6:5P5P5R/R)R"99.III %%g. MF F F (#>M2[E,#_8[2kZ,OE $$%78(0SI$(();<AF F F F seX'V#X' XV&A9X;V)<XV,A X&AW4V/5WV2C WV5FWV8A:WV;=WV>W-W.W W W7XAW-W)W-7W+8W-<X X'XX'&X)X,X/W2W5W8W;W>WWW W&&X)W-+W-- X  XX'X$X X$ X')NNrmNr)"rNrOrPrQrequires_response_bodyrRr$rWrrrrrr'rrResponserrrrrrrrrrrrrrrrrrUrLr=rrs "EISW*.+"+"-+" +" #C5)D/#9:TA +" #2ysC$J1G'H#HIDP +"+"!4Z+"Zr2KrPTr%..UY@emm 34sCx34jSZ\RR-0RAEc3hRVAVR R> >U^^ > >TemmT<u~~$*! d d$d /enn/QU/ HU]]H~emm]b]k]kNk?lHrLr)CrQrFrBloggingr6r8rcollections.abcrrr dataclassesrrtypingrr urllib.parser r r r rrpydanticrrrmcp.client.auth.exceptionsrrmcp.client.auth.utilsrrrrrrrrrrrrrr r!mcp.client.streamable_httpr"mcp.shared.authr#r$r%r&r'mcp.shared.auth_utilsr(r)r* getLoggerrNrr,rWrgAuthrrUrLr=r+s  ??( << 66F"<   8 $ OY O 8 ( yy yxZ%**ZrL