
    ~h1                     "   d dl Z d dlmZ d dlmZmZ d dlZd dl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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  G d ded      Z G d de      Z eddd       G d d             Z  G d de      Z!y)    N)	dataclass)Any	TypedDict)
JsonWebKeyJsonWebToken)	JoseError)serialization)rsa)AccessTokenAuthorizationCodeAuthorizationParamsRefreshToken)OAuthClientInformationFull
OAuthToken)	SecretStr)ClientRegistrationOptionsOAuthProviderRevocationOptionsc                   h    e Zd ZU dZeed<   eed<   eed<   eed<   eed<   eed<   ee   ed<   eed	<   y
)JWKDatazJSON Web Key data structure.ktykidusealgnex5cx5tN)__name__
__module____qualname____doc__str__annotations__list     `/opt/mcp/mcp-sentiment/venv/lib/python3.12/site-packages/fastmcp/server/auth/providers/bearer.pyr   r      s1    &	H	H	H	H
F
F	cN	Hr'   r   F)totalc                   "    e Zd ZU dZee   ed<   y)JWKSDataz JSON Web Key Set data structure.keysN)r   r    r!   r"   r%   r   r$   r&   r'   r(   r+   r+   *   s    *
w-r'   r+   T)frozenkw_onlyreprc                       e Zd ZU eed<   eed<   edd       Z	 	 	 	 	 	 	 ddedededz  d	ee   dz  d
e	de
eef   dz  dedz  defdZy)
RSAKeyPairprivate_key
public_keyreturnc                    t        j                  dd      }|j                         }|j                  t        j
                  j                  t        j                  j                  t	        j                               j                  d      }|j                  t        j
                  j                  t        j                  j                        j                  d      } | t        |      |      S )z~
        Generate an RSA key pair for testing.

        Returns:
            tuple: (private_key_pem, public_key_pem)
        i  i   )public_exponentkey_size)encodingformatencryption_algorithmutf-8)r8   r9   )r2   r3   )r
   generate_private_keyr3   private_bytesr	   EncodingPEMPrivateFormatPKCS8NoEncryptiondecodepublic_bytesPublicFormatSubjectPublicKeyInfor   )clsr2   r3   private_pem
public_pems        r(   generatezRSAKeyPair.generate5   s     ..!
 !++-
 "//"++// ..44!.!;!;!= 0 
 &/	 	  ,,"++// --BB - 
 &/ 	
 !+.!
 	
r'   Nsubjectissueraudiencescopesexpires_in_secondsadditional_claimsr   c                 X   t        dg      }t        t        j                               }	|||	|	|z   d}
|r||
d<   |rdj                  |      |
d<   |r|
j	                  |       ddi}|r||d<   |j                  ||
| j                  j                               }|j                  d	      S )
a)  
        Generate a test JWT token for testing purposes.

        Args:
            private_key_pem: RSA private key in PEM format
            subject: Subject claim (usually user ID)
            issuer: Issuer claim
            audience: Audience claim (optional)
            scopes: List of scopes to include
            expires_in_seconds: Token expiration time in seconds
            additional_claims: Any additional claims to include
            kid: Key ID for JWKS lookup (optional)

        Returns:
            Signed JWT token string
        RS256)isssubiatexpaud scoper   r   )keyr;   )	r   inttimejoinupdateencoder2   get_secret_valuerC   )selfrK   rL   rM   rN   rO   rP   r   jwtnowpayloadheadertoken_bytess                r(   create_tokenzRSAKeyPair.create_tokenX   s    4 G9%$))+ ++	
 %GEN"xx/GGNN,- !F5M jj  113 ! 

 !!'**r'   )r4   r1   )zfastmcp-userhttps://fastmcp.example.comNN  NN)r   r    r!   r   r$   r#   classmethodrJ   r%   r[   dictr   rg   r&   r'   r(   r1   r1   0   s    O 
  
H &3##'"&37:+:+ :+ *	:+
 S	D :+  :+  S>D0:+ 4Z:+ 
:+r'   r1   c                   z    e Zd ZdZ	 	 	 	 	 d dedz  dedz  dedz  dedz  dee   dz  f
 fdZd	ed
efdZdedz  d
efdZd	ed
e	dz  fdZ
deeef   d
ee   fdZded
edz  fdZded
dfdZdeded
efdZdeded
edz  fdZdeded
efdZdeded
edz  fdZdededee   d
efdZd	e	ez  d
dfdZ xZS )!BearerAuthProviderae  
    Simple JWT Bearer Token validator for hosted MCP servers.
    Uses RS256 asymmetric encryption. Supports either static public key
    or JWKS URI for key rotation.

    Note that this provider DOES NOT permit client registration or revocation, or any OAuth flows.
    It is intended to be used with a control plane that manages clients and tokens.
    Nr3   jwks_urirL   rM   required_scopesc                    |s|st        d      |r|rt        d      t        | 	  |xs dt        d      t	        d      |       || _        || _        || _        || _        t        dg      | _
        i | _        d| _        d	| _        y
)a  
        Initialize the provider. Either public_key or jwks_uri must be provided.

        Args:
            public_key: RSA public key in PEM format (for static key)
            jwks_uri: URI to fetch keys from (for key rotation)
            issuer: Expected issuer claim (optional)
            audience: Expected audience claim (optional)
            required_scopes: List of required scopes for access (optional)
        z.Either public_key or jwks_uri must be providedz/Provide either public_key or jwks_uri, not bothrh   F)enabled)
issuer_urlclient_registration_optionsrevocation_optionsro   rR   r   ri   N)
ValueErrorsuper__init__r   r   rL   rM   r3   rn   r   rb   _jwks_cache_jwks_cache_time
_cache_ttl)ra   r3   rn   rL   rM   ro   	__class__s         r(   rw   zBearerAuthProvider.__init__   s    $ hMNN(NOO>!>(A%(P0?+	 	 	
  $ 	* ,.'(r'   tokenr4   c                 x  K   | j                   r| j                   S 	 ddl}ddl}|j                  d      d   }|ddt	        |      dz  z
  z  z  }|j                  |j                  |            }|j                  d      }| j                  |       d{   S 7 # t        $ r}t        d|       d}~ww xY ww)z'Get the verification key for the token.r   N.=   r   z%Failed to extract key ID from token: )r3   base64jsonsplitlenloadsurlsafe_b64decodeget_get_jwks_key	Exceptionru   )ra   r|   r   r   
header_b64re   r   r   s           r(   _get_verification_keyz(BearerAuthProvider._get_verification_key   s     ????"	JS)!,J#S_q%8!899JZZ 8 8 DEF**U#C++C0000 	JDQCHII	Js;   B:A8B BB B:B 	B7$B22B77B:r   c                   K   | j                   st        d      t        j                         }|| j                  z
  | j                  k  re|r|| j
                  v r| j
                  |   S |sDt        | j
                        dk(  r,t        t        | j
                  j                                     S 	 t        j                         4 d{   }|j                  | j                          d{   }|j                          |j                         }ddd      d{    i | _        j                  dg       D ]Y  }|j                  d      }t        j                   |      }|j#                         }	|r|	| j
                  |<   K|	| j
                  d<   [ || _        |r,|| j
                  vrt        d| d      | j
                  |   S t        | j
                        dk(  r,t        t        | j
                  j                                     S t        | j
                        dkD  rt        d	      t        d
      7 t7 T7 '# 1 d{  7  sw Y   8xY w# t$        $ r}
t        d|
       d}
~
ww xY ww)z(Fetch key from JWKS with simple caching.zJWKS URI not configured   Nr,   r   _defaultzKey ID 'z' not found in JWKSz2Multiple keys in JWKS but no key ID (kid) in tokenzNo keys found in JWKSzFailed to fetch JWKS: )rn   ru   r\   ry   rz   rx   r   nextitervalueshttpxAsyncClientr   raise_for_statusr   r   
import_keyget_public_keyr   )ra   r   current_timeclientresponse	jwks_datakey_datakey_kidjwkr3   r   s              r(   r   z BearerAuthProvider._get_jwks_key   sG    }}677yy{ $///$//Asd...'',,S!1!12a7D!1!1!8!8!:;<<&	;((* , ,f!'DMM!::))+$MMO	, ,  "D%MM&"5 	>",,u- ++H5 //1
0:D$$W- 4>D$$Z0	> %1D! d...$xu4G%HII'',, t''(A-T%5%5%<%<%> ?@@))*Q.$L  %%<==E,:, , , ,H  	;5aS9::	;s   B-I:0I H<	I I+H?,$II IB-I 	I:
AI I:/I ?II IIII 	I7$I22I77I:c                   K   	 | j                  |       d{   }| j                  j                  ||      }|j                  d      }|r|t	        j                         k  ry| j
                  r|j                  d      | j
                  k7  ry| j                  r@|j                  d      }t        |t              r| j                  |vry|| j                  k7  ry|j                  d      xs |j                  d      xs d}| j                  |      }t        |t        |      ||rt        |            S d      S 7 &# t        $ r Y yt        $ r Y yw xY ww)	z
        Validates the provided JWT bearer token.

        Args:
            token: The JWT token string to validate

        Returns:
            AccessToken object if valid, None if invalid or expired
        NrV   rS   rW   	client_idrT   unknown)r|   r   rN   
expires_at)r   rb   rC   r   r\   rL   rM   
isinstancer%   _extract_scopesr   r#   r[   r   r   )ra   r|   verification_keyclaimsrV   rW   r   rN   s           r(   load_access_tokenz$BearerAuthProvider.load_access_token  sC    )	%)%?%?%FF XX__U,<=F **U#CsTYY[( {{::e$3 }}jj'c4(}}C/#DMM) 

;/Q6::e3DQ	I))&1Fi.'*3s8	  15	 ;  GH  	 		s   EE D>A
E #E$*E E;E 
EE EAE 6E7E =E>E 	E
EEEEEr   c                     |j                  dd      }t        |t              r|j                         S t        |t              r|S g S )zExtract scopes from JWT claims.rY    )r   r   r#   r   r%   )ra   r   scope_claims      r(   r   z"BearerAuthProvider._extract_scopesI  s@    jj"-k3'$$&&T*	r'   r   c                     K   t        d      w)NzClient management not supportedNotImplementedError)ra   r   s     r(   
get_clientzBearerAuthProvider.get_clientS  s     !"CDD   client_infoc                     K   t        d      w)Nz!Client registration not supportedr   )ra   r   s     r(   register_clientz"BearerAuthProvider.register_clientV  s     !"EFFr   r   paramsc                     K   t        d      w)Nz Authorization flow not supportedr   )ra   r   r   s      r(   	authorizezBearerAuthProvider.authorizeY        ""DEEr   authorization_codec                     K   t        d      w)Nz%Authorization code flow not supportedr   ra   r   r   s      r(   load_authorization_codez*BearerAuthProvider.load_authorization_code^  s      ""IJJr   c                     K   t        d      w)Nz)Authorization code exchange not supportedr   r   s      r(   exchange_authorization_codez.BearerAuthProvider.exchange_authorization_codec  s      ""MNNr   refresh_tokenc                     K   t        d      w)Nz Refresh token flow not supportedr   )ra   r   r   s      r(   load_refresh_tokenz%BearerAuthProvider.load_refresh_tokenh  r   r   rN   c                     K   t        d      w)Nz$Refresh token exchange not supportedr   )ra   r   r   rN   s       r(   exchange_refresh_tokenz)BearerAuthProvider.exchange_refresh_tokenm  s      ""HIIr   c                     K   t        d      w)NzToken revocation not supportedr   )ra   r|   s     r(   revoke_tokenzBearerAuthProvider.revoke_tokenu  s      ""BCCr   )NNNNN)r   r    r!   r"   r#   r%   rw   r   r   r   r   rk   r   r   r   r   r   r   r   r   r   r   r   r   r   r   r   __classcell__)r{   s   @r(   rm   rm      s    "&#!#,0'$J' *' d
	'
 *' cT)'RJ J J(6;sTz 6;c 6;p3S 3[45G 3jd38n c E# E2Lt2S EG1K GPT GF0F:MF	F
K0KFIK	T	!K
O0OFWO	O
F0FADF		F
J*J $J S		J
 
JD\)D 
Dr'   rm   )"r\   dataclassesr   typingr   r   r   authlib.joser   r   authlib.jose.errorsr   cryptography.hazmat.primitivesr	   )cryptography.hazmat.primitives.asymmetricr
   mcp.server.auth.providerr   r   r   r   mcp.shared.authr   r   pydanticr   fastmcp.server.auth.authr   r   r   r   r+   r1   rm   r&   r'   r(   <module>r      s     ! !  1 ) 8 9   
iu 
y  $51a+ a+ 2a+HdD dDr'   