
    Ki>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/y)"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                  b    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<   y)JWKDatazJSON Web Key data structure.strktykidusealgne	list[str]x5cx5tN__name__
__module____qualname____doc____annotations__     m/home/jay/workspace/scripts/.codegraph-venv/lib/python3.12/site-packages/fastmcp/server/auth/providers/jwt.pyr   r      s-    &	H	H	H	H
F
F	N	Hr-   r   F)totalc                      e Zd ZU dZded<   y)JWKSDataz JSON Web Key Set data structure.zlist[JWKData]keysNr&   r,   r-   r.   r1   r1   (   s    *
r-   r1   T)frozenkw_onlyreprc                  n    e Zd ZU dZded<   ded<   ed	d       Z	 	 	 	 	 	 	 d
	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 ddZy)
RSAKeyPairzRSA key pair for JWT testing.r   private_keyr   
public_keyc                   t        j                  dd      }|j                  t        j                  j
                  t        j                  j                  t        j                               j                  d      }|j                         j                  t        j                  j
                  t        j                  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4!.!;!;!= 0 
 &/	 	 ""$\&//33$11FF   VG_ 	 !+.!
 	
r-   Nc                   ddi}|r||d<   ||t        t        j                               t        t        j                               |z   d}	|r||	d<   |rdj                  |      |	d<   |r|	j                  |       t	        dg      }
|
j                  ||	| j                  j                               }|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 scoper@   )	inttimejoinupdater   encoder8   get_secret_valuerH   )selfsubjectissueraudiencescopesexpires_in_secondsadditional_claimsr   headerpayloadjwt_libtoken_bytess               r.   create_tokenzRSAKeyPair.create_tokenY   s    . !F5M tyy{#tyy{#&88	
 %GEN"xx/GGNN,- y)nnGT-->>@
 !!'**r-   )returnr7   )zfastmcp-userzhttps://fastmcp.example.comNN  NN)r`   r   ra   r   rb   str | list[str] | Nonerc   list[str] | Nonerd   rY   re   zdict[str, Any] | Noner   
str | Nonerk   r   )r'   r(   r)   r*   r+   classmethodrO   rj   r,   r-   r.   r7   r7   .   s    'O!
 !
J &3+/#'"&372+2+ 2+ )	2+
 !2+  2+ 1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y)JWTVerifierSettingsz$Settings for JWT token verification.FASTMCP_SERVER_AUTH_JWT_ignore)
env_prefixenv_fileextraNro   r9   jwks_urirm   ra   	algorithmrb   rn   required_scopeszAnyHttpUrl | str | Nonebase_urlbefore)modec                    t        |      S Nr   )rL   vs     r.   _parse_scopesz!JWTVerifierSettings._parse_scopes   s     Ar-   )r'   r(   r)   r*   r   r   model_configr9   r+   rx   ra   ry   rb   rz   r{   r   rp   r   r,   r-   r.   rr   rr      s    .%-L "J
!Hj%)F") Iz '+H$+(,O%,(,H%,&X6  7r-   rr   c                  x     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   rx   ra   rb   ry   rz   r{   c          
        t         j                  |||||||dj                         D 	ci c]  \  }}	|	t        ur||	 c}	}      }
|
j                  s|
j
                  st        d      |
j                  r|
j
                  rt        d      |
j                  xs d}|dvrt        d| d      t        | %  |
j                  |
j                         || _        |
j                  | _        |
j                  | _        |
j                  | _        |
j
                  | _        t        | j                  g      | _        t!        t"              | _        i | _        d	| _        d
| _        yc c}	}w )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.
        r   z.Either public_key or jwks_uri must be providedz/Provide either public_key or jwks_uri, not bothrQ   >   ES256ES384ES512HS256HS384HS512PS256PS384PS512rQ   RS384RS512zUnsupported algorithm: .)r{   rz   r   rl   N)rr   model_validateitemsr   r9   rx   
ValueErrorry   super__init__r{   rz   ra   rb   r   jwtr   r'   logger_jwks_cache_jwks_cache_time
_cache_ttl)r_   r9   rx   ra   rb   ry   rz   r{   kr   settings	__class__s              r.   r   zJWTVerifier.__init__   sm   4 '55 #- ($ (!*'6 ( %'Aq F? 1
  ""8+<+<MNN8#4#4NOO&&1'	 
 
 6ykCDD 	&&$44 	 	

 #oo ))"-- )) 01 * ,.'(os   E'
c                z  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   Nr   =   r   z%Failed to extract key ID from token: )r9   base64jsonsplitlenloadsurlsafe_b64decodeget_get_jwks_key	Exceptionr   )r_   tokenr   r   
header_b64rf   r   r"   s           r.   _get_verification_keyz!JWTVerifier._get_verification_key  s     ????"	QS)!,J#S_q%8!899JZZ 8 8 DEF**U#C++C0000 	QDQCHIqP	Qs;   B;A8B BB B;B 	B8$B33B88B;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<   [ || _        |rH|| j
                  vr+| j$                  j'                  d|       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 7 p7 C# 1 d{  7  sw Y   TxY w# t        j(                  $ r}
t        d|
       |
d}
~
wt*        $ r2}
| j$                  j'                  d|
        t        d|
       |
d}
~
ww xY w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: )rx   r   rZ   r   r   r   r   nextitervalueshttpxAsyncClientr   raise_for_statusr   r   
import_keyget_public_keyr   debug	HTTPErrorr   )r_   r   current_timeclientresponse	jwks_datakey_datakey_kidjwkr9   r"   s              r.   r   zJWTVerifier._get_jwks_key   s    }}677yy{ $///$//Asd...'',,S!1!12a7D!1!1!8!8!:;<<,	B((* , ,f!'DMM!::))+$MMO	, ,  "D%MM&"5 	>",,u- ++H5 //1
0:D$$W- 4>D$$Z0	> %1D! d...KK%%G %xu4G%HII'',, t''(A-T%5%5%<%<%> ?@@))*Q.$L  %%<==K,:, , , ,N  	B5aS9:A 	BKK 3A3785aS9:A	Bs   B-K0I7 I	I7 I!+I,$I!I7 IC	I7 %K&AI7 )K*/I7 I!I7 !I4'I*(I4/I7 7K
JK%-KKKc                    dD ]I  }||v st        ||   t              r||   j                         c S t        ||   t              sD||   c S  g 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).
        )rX   scp)
isinstancer   r   list)r_   claimsclaims      r.   _extract_scopeszJWTVerifier._extract_scopes^  sZ     & 	)EfUmS1!%=..00ut4!%=(	) 	r-   c                  K   	 | j                  |       d{   }| j                  j                  ||      }|j                  d      xs( |j                  d      xs |j                  d      xs d}|j                  d      }|rP|t	        j                         k  r9| j
                  j                  d|       | j
                  j                  d|       y| j                  r|j                  d	      }d
}t        | j                  t              r|| j                  v }n|| j                  k(  }|s9| j
                  j                  d|       | j
                  j                  d|       y| j                  r|j                  d      d
}t        | j                  t              rLt        t              rt        fd| j                  D              }nKt        t        | j                        v }n.t        t              r| j                  v }n| j                  k(  }|s9| j
                  j                  d|       | j
                  j                  d|       y| j                  |      }	| j                  rkt!        |	      }
t!        | j                        }|j#                  |
      s:| j
                  j                  d|
|       | j
                  j                  d|       yt%        |t'        |      |	|rt)        |      nd|      S 7 # t*        $ r | j
                  j                  d       Y yt,        $ r/}| j
                  j                  dt'        |             Y d}~yd}~ww xY w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azprR   unknownrU   z4Token validation failed: expired token for client %sz#Bearer token rejected for client %srS   Fz6Token validation failed: issuer mismatch for client %srV   c              3  &   K   | ]  }|v  
 y wr   r,   ).0expectedrV   s     r.   	<genexpr>z0JWTVerifier.load_access_token.<locals>.<genexpr>  s      -08HO-s   z8Token validation failed: audience mismatch for client %sz4Token missing required scopes. Has: %s, Required: %sr   r   rc   
expires_atr   z5Token validation failed: JWT signature/format invalidzToken validation failed: %s)r   r   rH   r   rZ   r   r   infora   r   r   rb   anyr   r   rz   setissubsetr   r   rY   r	   r   )r_   r   verification_keyr   r   rU   rS   issuer_validaudience_validrc   token_scopesrz   r"   rV   s                @r.   load_access_tokenzJWTVerifier.load_access_tokeno  s     j	%)%?%?%FF XX__U,<=F 

;' ::e$::e$ 	  **U#CsTYY[(!!JI   !F	R {{jj'  %dkk40#&$++#5L $'$++#5L#KK%%P! KK$$%JIV }}jj' "'dmmT2!#t,), -<@MM- *
 *-T4==0I)I "#t,)-#)=),)=%KK%%R! KK$$%JIV ))&1F ##"6{"%d&:&:";&//=KK%%N$'
 KK$$%JIVi.'*3s8 w  GF  	KKUV 	KK;SVD	s~   M)L	 LB=L	 M)BL	 )M)*C-L	 M)BL	 M) %L	 M)L	 	$M&-M)/M&7%M!M)!M&&M)c                @   K   | j                  |       d{   S 7 w)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   )r_   r   s     r.   verify_tokenzJWTVerifier.verify_token  s      ++E2222s   )r9   str | NotSetT | Nonerx   r   ra    str | list[str] | NotSetT | Nonerb   r   ry   r   rz   zlist[str] | NotSetT | Noner{   z!AnyHttpUrl | str | NotSetT | None)r   r   rk   r   )r   ro   rk   r   )r   zdict[str, Any]rk   r#   r   r   rk   zAccessToken | None)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
 1R 3R (R 4R 4RhQ(<B|"tl3r-   r   c                  6     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!
    c                4    t         |   |       || _        y)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
        )rz   N)r   r   tokens)r_   r   rz   r   s      r.   r   zStaticTokenVerifier.__init__  s     	9r-   c                  K   | j                   j                  |      }|sy|j                  d      }||t        j                         k  ry|j                  dg       }| j                  rMt	        |      }t	        | j                        }|j                  |      st        j                  d| d|        yt        ||d   |||      S w)z-Verify token against static token dictionary.Nr   rc   z$Token missing required scopes. Has: z, Required: r   r   )	r   r   rZ   rz   r   r   r   r   r   )r_   r   
token_datar   rc   r   rz   s          r.   r   z StaticTokenVerifier.verify_token  s     [[__U+
  ^^L1
!j499;&>"- v;L!$"6"67O"++L9:<.UdTef  -!
 	
s   CC
r   )r   zdict[str, dict[str, Any]]rz   rn   r   )r'   r(   r)   r*   r   r   r   r   s   @r.   r   r     s)    ( -1) * 
r-   r   )0r*   
__future__r   rZ   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   rr   r   r   r,   r-   r.   <module>r      s    0 "  !   1 ) 8 9 ; ; > ' : % / 0 3	H	
iu 
y  $51\+ \+ 2\+~, .M3- M3`
?
- ?
r-   