
    "<i>Q                       d Z ddlmZ 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 ddlmZ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dl#m$Z$ ddl%m&Z&m'Z'  e$e(          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- G d de          Z. G d  d!e          Z/dS )"z*TokenVerifier implementations for FastMCP.    )annotationsN)	dataclass)Anycast)
JsonWebKeyJsonWebToken)	JoseError)serialization)rsa)
AnyHttpUrl	SecretStrfield_validator)BaseSettingsSettingsConfigDict)	TypedDict)AccessTokenTokenVerifier)ENV_FILEparse_scopes)
get_logger)NotSetNotSetTc                  d    e Zd ZU dZded<   ded<   ded<   ded<   ded<   ded<   d	ed
<   ded<   dS )JWKDatazJSON Web Key data structure.strktykidusealgne	list[str]x5cx5tN__name__
__module____qualname____doc____annotations__     /Users/kimhansen/Desktop/03 Workspace/ceo-agents/chl-effectiveness/mcp-servers/whoop/.venv/lib/python3.11/site-packages/fastmcp/server/auth/providers/jwt.pyr   r      s^         &&HHHHHHHHHHHH
FFF
FFFNNNHHHHHr-   r   F)totalc                      e Zd ZU dZded<   dS )JWKSDataz JSON Web Key Set data structure.zlist[JWKData]keysNr&   r,   r-   r.   r1   r1   (   s$         **r-   r1   T)frozenkw_onlyreprc                  X    e Zd ZU dZded<   ded<   edd            Z	 	 	 	 	 	 	 dddZd
S )
RSAKeyPairzRSA key pair for JWT testing.r   private_keyr   
public_keyreturnc                   t          j        dd          }|                    t          j        j        t          j        j        t          j                              	                    d          }|
                                                    t          j        j        t          j        j                  	                    d          } | t          |          |          S )zt
        Generate an RSA key pair for testing.

        Returns:
            RSAKeyPair: Generated key pair
        i  i   )public_exponentkey_size)encodingformatencryption_algorithmutf-8)r>   r?   )r8   r9   )r   generate_private_keyprivate_bytesr
   EncodingPEMPrivateFormatPKCS8NoEncryptiondecoder9   public_bytesPublicFormatSubjectPublicKeyInfor   )clsr8   private_pem
public_pems       r.   generatezRSAKeyPair.generate5   s     .!
 
 
 "//"+/ .4!.!;!=!= 0 
 
 &//	 	 ""$$\&/3$1F    VG__ 	 s!+..!
 
 
 	
r-   fastmcp-userhttps://fastmcp.example.comN  subjectissueraudiencestr | list[str] | Nonescopeslist[str] | Noneexpires_in_secondsintadditional_claimsdict[str, Any] | Noner   
str | Nonec                   ddi}|r||d<   ||t          t          j                              t          t          j                              |z   d}	|r||	d<   |rd                    |          |	d<   |r|	                    |           t	          dg          }
|
                    ||	| j                                                  }|                    d          S )	a  
        Generate a test JWT token for testing purposes.

        Args:
            subject: Subject claim (usually user ID)
            issuer: Issuer claim
            audience: Audience claim - can be a string or list of strings (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 to include in header
        r    RS256r   )subissiatexpaud scoperA   )	r[   timejoinupdater   encoder8   get_secret_valuerI   )selfrT   rU   rV   rX   rZ   r\   r   headerpayloadjwt_libtoken_bytess               r.   create_tokenzRSAKeyPair.create_tokenY   s    . ! 	 F5M ty{{##ty{{##&88	
 
  	&%GEN 	0"xx//GG 	.NN,--- y))nnGT->>@@
 
 !!'***r-   )r:   r7   )rQ   rR   NNrS   NN)rT   r   rU   r   rV   rW   rX   rY   rZ   r[   r\   r]   r   r^   r:   r   )r'   r(   r)   r*   r+   classmethodrP   rr   r,   r-   r.   r7   r7   .   s         ''OOO!
 !
 !
 [!
J &3+/#'"&372+ 2+ 2+ 2+ 2+ 2+ 2+r-   r7   c                      e Zd ZU dZ eded          ZdZded<   dZ	ded<   dZ
d	ed
<   dZded<   dZd	ed<   dZded<   dZded<    edd          ed                         ZdS )JWTVerifierSettingsz$Settings for JWT token verification.FASTMCP_SERVER_AUTH_JWT_ignore)
env_prefixenv_fileextraNr^   r9   jwks_urirW   rU   	algorithmrV   rY   required_scopeszAnyHttpUrl | str | Nonebase_urlbefore)modec                     t          |          S Nr   )rM   vs     r.   _parse_scopesz!JWTVerifierSettings._parse_scopes   s     Ar-   )r'   r(   r)   r*   r   r   model_configr9   r+   r{   rU   r|   rV   r}   r~   r   rs   r   r,   r-   r.   ru   ru      s         ..%%-  L "J!!!!H%)F)))) I    '+H++++(,O,,,,(,H,,,,_&X666  [ 76  r-   ru   c                  ^     e Zd ZdZeeeeeeedd fdZddZddZd dZd!dZ	d!dZ
 xZS )"JWTVerifiera  
    JWT token verifier supporting both asymmetric (RSA/ECDSA) and symmetric (HMAC) algorithms.

    This verifier validates JWT tokens using various signing algorithms:
    - **Asymmetric algorithms** (RS256/384/512, ES256/384/512, PS256/384/512):
      Uses public/private key pairs. Ideal for external clients and services where
      only the authorization server has the private key.
    - **Symmetric algorithms** (HS256/384/512): Uses a shared secret for both
      signing and verification. Perfect for internal microservices and trusted
      environments where the secret can be securely shared.

    Use this when:
    - You have JWT tokens issued by an external service (asymmetric)
    - You need JWKS support for automatic key rotation (asymmetric)
    - You have internal microservices sharing a secret key (symmetric)
    - Your tokens contain standard OAuth scopes and claims
    r9   r{   rU   rV   r|   r}   r~   r9   str | NotSetT | Noner{   rU    str | list[str] | NotSetT | NonerV   r|   r}   list[str] | NotSetT | Noner~   !AnyHttpUrl | str | NotSetT | Nonec                  t                               d |||||||d                                D                       }|j        s|j        st          d          |j        r|j        rt          d          |j        pd}|dvrt          d| d          t                                          |j	        |j
        	           || _        |j        | _        |j        | _        |j        | _        |j        | _        t          | j        g          | _        t          t                     | _        i | _        d
| _        d| _        dS )a  
        Initialize a JWTVerifier configured to validate JWTs using either a static key or a JWKS endpoint.

        Parameters:
            public_key (str | NotSetT | None): PEM-encoded public key for asymmetric algorithms or shared secret for symmetric algorithms.
            jwks_uri (str | NotSetT | None): URI to fetch a JSON Web Key Set; used when verifying tokens with remote JWKS.
            issuer (str | list[str] | NotSetT | None): Expected issuer claim value or list of allowed issuer values.
            audience (str | list[str] | NotSetT | None): Expected audience claim value or list of allowed audience values.
            algorithm (str | NotSetT | None): JWT signing algorithm to accept (default: "RS256"). Supported: HS256/384/512, RS256/384/512, ES256/384/512, PS256/384/512.
            required_scopes (list[str] | NotSetT | None): Scopes that must be present in validated tokens.
            base_url (AnyHttpUrl | str | NotSetT | None): Base URL passed to the parent TokenVerifier.

        Raises:
            ValueError: If neither or both of `public_key` and `jwks_uri` are provided, or if `algorithm` is unsupported.
        c                ,    i | ]\  }}|t           u||S r,   )r   ).0kr   s      r.   
<dictcomp>z(JWTVerifier.__init__.<locals>.<dictcomp>   s3       Aq F?? 1 #??r-   r   z.Either public_key or jwks_uri must be providedz/Provide either public_key or jwks_uri, not bothr`   >   ES256ES384ES512HS256HS384HS512PS256PS384PS512r`   RS384RS512zUnsupported algorithm: .)r~   r}   r   rS   N)ru   model_validateitemsr9   r{   
ValueErrorr|   super__init__r~   r}   rU   rV   r   jwtr   r'   logger_jwks_cache_jwks_cache_time
_cache_ttl)
rm   r9   r{   rU   rV   r|   r}   r~   settings	__class__s
            r.   r   zJWTVerifier.__init__   s   4 '55  #- ($ (!*'6 (  %''  
 
  " 	O8+< 	OMNNN 	P8#4 	PNOOO&1'	 
 
 
 CyCCCDDD 	&$4 	 	
 	
 	

 #o )"- ) 011 ** ,.'(r-   tokenr   r:   c                  K   | j         r| j         S 	 ddl}ddl}|                    d          d         }|ddt	          |          dz  z
  z  z  }|                    |                    |                    }|                    d          }|                     |           d{V S # t          $ r}t          d|           |d}~ww xY w)z'Get the verification key for the token.r   Nr   =   r   z%Failed to extract key ID from token: )r9   base64jsonsplitlenloadsurlsafe_b64decodeget_get_jwks_key	Exceptionr   )rm   r   r   r   
header_b64rn   r   r"   s           r.   _get_verification_keyz!JWTVerifier._get_verification_key  s      ? 	#?"	QMMMKKKS))!,J#S__q%8!899JZZ 8 8 D DEEF**U##C++C000000000 	Q 	Q 	QHQHHIIqP	Qs   BB( (
C
2CC
r   r^   c                  K   | j         st          d          t          j                    }|| j        z
  | j        k     re|r|| j        v r| j        |         S |sKt          | j                  dk    r3t          t          | j        	                                                    S 	 t          j                    4 d{V }|                    | j                    d{V }|                                 |                                }ddd          d{V  n# 1 d{V swxY w Y   i | _        |                    dg           D ]V}|                    d          }t          j        |          }|                                }	|r|	| j        |<   L|	| j        d<   W|| _        |rD|| j        vr.| j                            d|           t          d| d	          | j        |         S t          | j                  dk    r3t          t          | j        	                                                    S t          | j                  dk    rt          d
          t          d          # t          j        $ r}
t          d|
           |
d}
~
wt*          $ r5}
| j                            d|
            t          d|
           |
d}
~
ww xY w)z(Fetch key from JWKS with simple caching.zJWKS URI not configured   Nr2   r   _defaultz-JWKS key lookup failed: key ID '%s' not foundz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: zJWKS fetch failed: )r{   r   rh   r   r   r   r   nextitervalueshttpxAsyncClientr   raise_for_statusr   r   
import_keyget_public_keyr   debug	HTTPErrorr   )rm   r   current_timeclientresponse	jwks_datakey_datakey_kidjwkr9   r"   s              r.   r   zJWTVerifier._get_jwks_key   s     } 	86777y{{ $//$/AA =sd...',, =S!122a77D!1!8!8!:!:;;<<<,	B(** , , , , , , ,f!'DM!:!:::::::))+++$MMOO	, , , , , , , , , , , , , , , , , , , , , , , , , , ,  "D%MM&"55 	> 	>",,u-- +H55 //11
 >0:D$W-- 4>D$Z00$0D!  >d...K%%G   %%H%H%H%HIII',, t'((A--T%5%<%<%>%> ? ?@@@)**Q..$L   %%<=== 	B 	B 	B9a99::A 	B 	B 	BK7A778889a99::A	BsW   %I, >A	DI, 
D##I, &D#'CI, +A
I, 66I, ,K;JK0KKclaimsdict[str, Any]r#   c                    dD ]b}||v r\t          ||         t                    r||                                         c S t          ||         t                    r
||         c S cg S )z
        Extract scopes from JWT claims. Supports both 'scope' and 'scp'
        claims.

        Checks the `scope` claim first (standard OAuth2 claim), then the `scp`
        claim (used by some Identity Providers).
        )rg   scp)
isinstancer   r   list)rm   r   claims      r.   _extract_scopeszJWTVerifier._extract_scopes^  sz     & 	) 	)EfUmS11 )!%=..00000ut44 )!%=(((	r-   AccessToken | Nonec                  K   	 |                      |           d{V }| j                            ||          }|                    d          p+|                    d          p|                    d          pd}|                    d          }|rO|t	          j                    k     r8| j                            d|           | j                            d|           dS | j        r|                    d	          }d
}t          | j        t                    r
|| j        v }n|| j        k    }|s8| j                            d|           | j                            d|           dS | j        r|                    d          d
}t          | j        t                    rSt          t                    r!t          fd| j        D                       }nGt          t          | j                  v }n*t          t                    r
| j        v }n| j        k    }|s8| j                            d|           | j                            d|           dS |                     |          }	| j        rqt!          |	          }
t!          | j                  }|                    |
          s9| j                            d|
|           | j                            d|           dS t%          |t'          |          |	|rt)          |          nd|          S # t*          $ r | j                            d           Y dS t,          $ r3}| j                            dt'          |                     Y d}~dS d}~ww xY w)a  
        Validate a JWT bearer token and return an AccessToken when the token is valid.

        Parameters:
            token (str): The JWT bearer token string to validate.

        Returns:
            AccessToken | None: An AccessToken populated from token claims if the token is valid; `None` if the token is expired, has an invalid signature or format, fails issuer/audience/scope validation, or any other validation error occurs.
        N	client_idazpra   unknownrd   z4Token validation failed: expired token for client %sz#Bearer token rejected for client %srb   Fz6Token validation failed: issuer mismatch for client %sre   c              3      K   | ]}|v V  	d S r   r,   )r   expectedre   s     r.   	<genexpr>z0JWTVerifier.load_access_token.<locals>.<genexpr>  s8       - -08HO- - - - - -r-   z8Token validation failed: audience mismatch for client %sz4Token missing required scopes. Has: %s, Required: %sr   r   rX   
expires_atr   z5Token validation failed: JWT signature/format invalidzToken validation failed: %s)r   r   rI   r   rh   r   r   inforU   r   r   rV   anyr   r   r}   setissubsetr   r   r[   r	   r   )rm   r   verification_keyr   r   rd   rb   issuer_validaudience_validrX   token_scopesr}   r"   re   s                @r.   load_access_tokenzJWTVerifier.load_access_tokeno  s     j	%)%?%?%F%FFFFFFF X__U,<==F 

;'' ::e$$::e$$ 	  **U##C sTY[[((!!JI     !F	RRRt {  jj''  %dk400 6#&$+#5LL $'$+#5L#  K%%P!   K$$%JIVVV4 }  jj'' "'dmT22 >!#t,, J), - - - -<@M- - - * *
 *-T4=0I0I)I "#t,, >)-#)=),)=%  K%%R!   K$$%JIVVV4 ))&11F # 
 "6{{"%d&:";";&//==  K%%N$'  
 K$$%JIVVV4i..'*43s888     	 	 	KUVVV44 	 	 	K;SVVDDD44444	s9   CL "BL )C-L BL %1L $M:>	M:(M55M:c                <   K   |                      |           d{V S )a\  
        Verify a bearer token and return access info if valid.

        This method implements the TokenVerifier protocol by delegating
        to our existing load_access_token method.

        Args:
            token: The JWT token string to validate

        Returns:
            AccessToken object if valid, None if invalid or expired
        N)r   )rm   r   s     r.   verify_tokenzJWTVerifier.verify_token  s.       ++E222222222r-   )r9   r   r{   r   rU   r   rV   r   r|   r   r}   r   r~   r   )r   r   r:   r   )r   r^   r:   r   )r   r   r:   r#   r   r   r:   r   )r'   r(   r)   r*   r   r   r   r   r   r   r   __classcell__r   s   @r.   r   r      s         * ,2)/395;*06<6<R R R R R R R RhQ Q Q Q(<B <B <B <B|   "t t t tl3 3 3 3 3 3 3 3r-   r   c                  0     e Zd ZdZ	 dd fdZddZ xZS )StaticTokenVerifiera  
    Simple static token verifier for testing and development.

    This verifier validates tokens against a predefined dictionary of valid token
    strings and their associated claims. When a token string matches a key in the
    dictionary, the verifier returns the corresponding claims as if the token was
    validated by a real authorization server.

    Use this when:
    - You're developing or testing locally without a real OAuth server
    - You need predictable tokens for automated testing
    - You want to simulate different users/scopes without complex setup
    - You're prototyping and need simple API key-style authentication

    WARNING: Never use this in production - tokens are stored in plain text!
    Ntokensdict[str, dict[str, Any]]r}   rY   c                Z    t                                          |           || _        dS )a  
        Initialize the static token verifier.

        Args:
            tokens: Dict mapping token strings to token metadata
                   Each token should have: client_id, scopes, expires_at (optional)
            required_scopes: Required scopes for all tokens
        )r}   N)r   r   r   )rm   r   r}   r   s      r.   r   zStaticTokenVerifier.__init__  s+     	999r-   r   r   r:   r   c                  K   | j                             |          }|sdS |                    d          }||t          j                    k     rdS |                    dg           }| j        rZt	          |          }t	          | j                  }|                    |          s"t                              d| d|            dS t          ||d         |||          S )z-Verify token against static token dictionary.Nr   rX   z$Token missing required scopes. Has: z, Required: r   r   )	r   r   rh   r}   r   r   r   r   r   )rm   r   
token_datar   rX   r   r}   s          r.   r   z StaticTokenVerifier.verify_token  s     [__U++
 	4  ^^L11
!j49;;&>&>4"--  	v;;L!$"677O"++L99 f<ffUdff   t -!
 
 
 	
r-   r   )r   r   r}   rY   r   )r'   r(   r)   r*   r   r   r   r   s   @r.   r   r     se         ( -1       
 
 
 
 
 
 
 
r-   r   )0r*   
__future__r   rh   dataclassesr   typingr   r   r   authlib.joser   r   authlib.jose.errorsr	   cryptography.hazmat.primitivesr
   )cryptography.hazmat.primitives.asymmetricr   pydanticr   r   r   pydantic_settingsr   r   typing_extensionsr   fastmcp.server.authr   r   fastmcp.settingsr   fastmcp.utilities.authr   fastmcp.utilities.loggingr   fastmcp.utilities.typesr   r   r'   r   r   r1   r7   ru   r   r   r,   r-   r.   <module>r     s   0 0 " " " " " "  ! ! ! ! ! !          1 1 1 1 1 1 1 1 ) ) ) ) ) ) 8 8 8 8 8 8 9 9 9 9 9 9 ; ; ; ; ; ; ; ; ; ; > > > > > > > > ' ' ' ' ' ' : : : : : : : : % % % % % % / / / / / / 0 0 0 0 0 0 3 3 3 3 3 3 3 3	H		
 
 
 
 
iu 
 
 
 
    y    $5111\+ \+ \+ \+ \+ \+ \+ 21\+~    ,   .M3 M3 M3 M3 M3- M3 M3 M3`
?
 ?
 ?
 ?
 ?
- ?
 ?
 ?
 ?
 ?
r-   