
    kKjd`                       U d Z ddlmZ ddlZddlZddlZddlmZ ddlm	Z	m
Z
mZ ddl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dl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#m$Z$m%Z% ddl&m'Z'm(Z( ddl)m*Z*  e*e+      Z,e-e.e.e/e.   z  f   Z0de1d<    e2e      Z3d&dZ4d'dZ5d(dZ6 G d ded      Z7 G d de      Z8 eddd       G d d              Z9d)d!Z: G d" d#e!      Z; G d$ d%e!      Z<y)*z*TokenVerifier implementations for FastMCP.    )annotationsN)	dataclass)Any	TypeAliascast)serialization)rsa)jwkjwt)	JoseError)JWSRegistry)JWS_HEADER_REGISTRY)
AnyHttpUrl	SecretStr)	TypedDict)AccessTokenTokenVerifier)	SSRFErrorSSRFFetchErrorssrf_safe_fetch)decode_jwt_headerparse_scopes)
get_loggerr   
JWKKeyDatac                
   |j                  d      rt        j                  | d      S |j                  d      rt        j                  | d      S |j                  d      rt        j                  | d      S t        d| d      )	NHSoct)RSPSRSAESECUnsupported algorithm: .)
startswithr
   
import_key
ValueError)key	algorithms     r/Users/ahmed/devFolder/Ultron/claude-voice/.venv/lib/python3.12/site-packages/fastmcp/server/auth/providers/jwt.py_import_key_for_algorithmr+       st    D!~~c5))L)~~c5))D!~~c4((
.yk;
<<    c                    | j                  d      }|dk(  r3t        j                  | d      j                         j	                  d      S |dk(  r3t        j                  | d      j                         j	                  d      S t        d|      )Nktyr    utf-8r"   zUnsupported JWK key type: )getr
   r&   as_pemdecoder'   )key_datakey_types     r*   _jwk_to_pemr5   *   s}    ||E"H5~~h.557>>wGG4~~h-446==gFF
1(>
??r,   c                v      j                  d      }|yt        |t              syt         fd|D              S )NcritFTc              3  `   K   | ]%  }t        |t               xs |vxs |t        v ' y wN)
isinstancestrSUPPORTED_JWS_HEADER_FIELDS).0header_nameheaders     r*   	<genexpr>z4_has_unsupported_critical_headers.<locals>.<genexpr>:   sG        K {C(( 	:f$	:99	:  s   +.)r0   r:   listany)r?   r7   s   ` r*   !_has_unsupported_critical_headersrC   3   sB    ::fD|dD!   	  r,   c                  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.r;   r.   kidusealgne	list[str]x5cx5tN__name__
__module____qualname____doc____annotations__ r,   r*   rE   rE   B   s-    &	H	H	H	H
F
F	N	Hr,   rE   F)totalc                      e Zd ZU dZded<   y)JWKSDataz JSON Web Key Set data structure.zlist[JWKData]keysNrN   rT   r,   r*   rW   rW   O   s    *
r,   rW   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_algorithmr/   )rc   rd   )r^   r_   )r	   generate_private_keyprivate_bytesr   EncodingPEMPrivateFormatPKCS8NoEncryptionr2   r_   public_bytesPublicFormatSubjectPublicKeyInfor   )clsr^   private_pem
public_pems       r*   generatezRSAKeyPair.generate\   s     ..!
 "//"++// ..44!.!;!;!= 0 
 &/	 	 ""$\&//33$11FF   VG_ 	 !+.!
 	
r,   Nc                x   ddi}|r||d<   ||t        t        j                               t        t        j                               |z   d}	|r||	d<   |rdj                  |      |	d<   |r|	j                  |       t	        | j
                  j                         d      }
t        j                  ||	|
dg      }|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
        rH   RS256rF   )subissiatexpaud scope)
algorithms)	inttimejoinupdater+   r^   get_secret_valuer   encode)selfsubjectissueraudiencescopesexpires_in_secondsadditional_claimsrF   r?   payloadsigning_keytokens               r*   create_tokenzRSAKeyPair.create_token   s    . !F5M tyy{#tyy{#&88	5
 %GEN"xx/GGNN,- 0--/
 

67KWINr,   )returnr]   )zfastmcp-userzhttps://fastmcp.example.comNN  NN)r   r;   r   r;   r   str | list[str] | Noner   list[str] | Noner   r~   r   zdict[str, Any] | NonerF   
str | Noner   r;   )rO   rP   rQ   rR   rS   classmethodrs   r   rT   r,   r*   r]   r]   U   s    'O!
 !
J &3+/#'"&3722 2 )	2
 !2  2 12 2 
2r,   r]   c                    t        | t              r| j                  dd      } | j                         d}t	        fd|D              S )zLReturn True when key text appears to be PEM-encoded asymmetric key material.r/   replace)errors)z-----BEGIN PUBLIC KEY-----z-----BEGIN RSA PUBLIC KEY-----z-----BEGIN EC PUBLIC KEY-----z-----BEGIN CERTIFICATE-----c              3  &   K   | ]  }|v  
 y wr9   rT   )r=   markerkey_texts     r*   r@   z-_looks_like_pem_public_key.<locals>.<genexpr>   s     <fv!   )r:   bytesr2   striprB   )r(   pem_markersr   s     @r*   _looks_like_pem_public_keyr      sD    #ujjj3yy{HK <<<<r,   c            
           e Zd ZdZdddddddddd		 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 d fdZd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
    NF)	r_   jwks_urir   r   r)   required_scopesbase_url	ssrf_safehttp_clientc       	           |s|st        d      |r|rt        d      |r|r|	t        d      |xs d}|dvrt        d| d      |j                  d	      r%|rt        d
      |rt        |      rt        d      |t        |      nd}
t        |   ||
       || _        || _        || _        || _	        || _
        || _        |	| _        t        t              | _        i | _        d| _        d| _        y)ax  
        Initialize a JWTVerifier configured to validate JWTs using either a static key or a JWKS endpoint.

        Parameters:
            public_key: PEM-encoded public key for asymmetric algorithms or shared secret for symmetric algorithms.
            jwks_uri: URI to fetch a JSON Web Key Set; used when verifying tokens with remote JWKS.
            issuer: Expected issuer claim value or list of allowed issuer values.
            audience: Expected audience claim value or list of allowed audience values.
            algorithm: JWT signing algorithm to accept (default: "RS256"). Supported: HS256/384/512, RS256/384/512, ES256/384/512, PS256/384/512.
            required_scopes: Scopes that must be present in validated tokens.
            base_url: Base URL passed to the parent TokenVerifier.
            ssrf_safe: If True, JWKS fetches use SSRF protection (HTTPS-only,
                public IPs, DNS pinning). Enable when the JWKS URI comes from
                untrusted input (e.g. CIMD documents). Defaults to False so
                operator-configured JWKS URIs (including localhost) work normally.
            http_client: Optional httpx.AsyncClient for connection pooling. When provided,
                the client is reused for JWKS fetches and the caller is responsible for
                its lifecycle. When None (default), a fresh client is created per fetch.
                Cannot be used with ssrf_safe=True.

        Raises:
            ValueError: If neither or both of `public_key` and `jwks_uri` are provided,
                if `algorithm` is unsupported, or if `http_client` is provided with `ssrf_safe=True`.
        z.Either public_key or jwks_uri must be providedz/Provide either public_key or jwks_uri, not bothNzbhttp_client cannot be used with ssrf_safe=True; SSRF-safe mode requires its own hardened transportru   >   ES256ES384ES512HS256HS384HS512PS256PS384PS512ru   RS384RS512r#   r$   r   zhSymmetric HS* algorithms cannot be used with jwks_uri; configure a shared secret via public_key instead.zCSymmetric HS* algorithms require a shared secret, not a public key.)r   r   r   r   )r'   r%   r   r   super__init__r)   r   r   r_   r   r   _http_clientr   rO   logger_jwks_cache_jwks_cache_time
_cache_ttl)r   r_   r   r   r   r)   r   r   r   r   parsed_required_scopes	__class__s              r*   r   zJWTVerifier.__init__   sA   J (MNN(NOO 	k&=E 
 (	 
 
 6ykCDD% H  8D Y  .=-HL)d 	
 	2 	 	

 # $ "' * ,.'(r,   c                  K   | j                   r| j                   S 	 t        |      }|j                  d      }| j                  |       d{   S 7 # t        t
        t        t        j                  f$ r}t	        d|       |d}~ww xY ww)z'Get the verification key for the token.rF   Nz%Failed to extract key ID from token: )	r_   r   r0   _get_jwks_keyr'   KeyError
IndexErrorjsonJSONDecodeError)r   r   r?   rF   rJ   s        r*   _get_verification_keyz!JWTVerifier._get_verification_key=  s     ????"	Q&u-F**U#C++C0000Hj$2F2FG 	QDQCHIqP	Qs:   B0A AA BA #B
6BB

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 	 | j                          d{   }i | _        |j                  dg       D ]?  }|j                  d      }t        |      }|r|| j
                  |<   1|| j
                  d<   A || _        |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 ## t        t         f$ r0}| j                  j                  d|       t        d|       |d}~wt"        j$                  $ r}t        d|       |d}~wt&        j(                  $ r}t        d|       |d}~wt*        t,        t.        t        f$ r0}| j                  j                  d|       t        d|       |d}~ww xY ww)z<Fetch key from JWKS with simple caching and SSRF protection.JWKS URI not configured   NrX   rF   _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)JWKS fetch blocked by SSRF protection: %szFailed to fetch JWKS: zInvalid JWKS JSON: zJWKS key processing failed: %szFailed to process JWKS: )r   r'   r   r   r   r   lennextitervalues_fetch_jwksr0   r5   r   debugr   r   httpx	HTTPErrorr   r   r   	TypeErrorr   )r   rF   current_time	jwks_datar3   key_kidr_   rJ   s           r*   r   zJWTVerifier._get_jwks_keyK  s    }}677yy{ $///$//Asd...'',,S!1!12a7D!1!1!8!8!:;<<-	D"..00I  "D%MM&"5",,u-(2
0:D$$W- 4>D$$Z0 6 %1D! d...KK%%G %xu4G%HII'',, t''(A-T%5%5%<%<%> ?@@))*Q.$L  %%<==C 1F >* 	BKKI1M5aS9:A 	B5aS9:A## 	?21#67Q>9h
; 	DKK>B7s;<!C	Dsn   B-J<0G) G&B/G) 3J<4AG) 7J<8/G) )J98+H##J99IJ9I--J9	+J44J99J<c                X  K   | j                   st        d      | j                  r6t        | j                   ddd       d{   }t	        j
                  |      S | j                  t        j                  | j                        n(t        j                  t        j                  d            4 d{   }|j                  | j                          d{   }|j                          |j	                         cddd      d{    S 7 7 Z7 97 # 1 d{  7  sw Y   yxY ww)zCFetch JWKS data, using SSRF-safe or standard fetch based on config.r   i   g      $@g      >@)max_sizetimeoutoverall_timeoutN)r   )r   r'   r   r   r   loadsr   
contextlibnullcontextr   AsyncClientTimeoutr0   raise_for_status)r   contentclientresponses       r*   r   zJWTVerifier._fetch_jwks  s    }}677>>+ $	 G ::g&& $$0 &&t'8'89&&u}}T/BCD D !'DMM!::))+}}D D DD ;	D D D Dsm   A D*DA1D*4D5D*8DD#D;D*DD*D*DD*D'DD'#D*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).
        )r|   scp)r:   r;   splitrA   )r   claimsclaims      r*   _extract_scopeszJWTVerifier._extract_scopes  sU     &EfUmS1!%=..00ut4!%=( & 	r,   c           	     L  K   	 | j                  |       d{   }t        || j                        }t        |      }t	        |      r| j
                  j                  d       yt        j                  ||| j                  gt        | j                  gd            j                  }|j                  d      xs( |j                  d      xs |j                  d      xs d	}|j                  d
      }|4|t        j                         k  r| j
                  j                  d|       y| j                  rv|j                  d      }d}	t        | j                  t               r|| j                  v }	n|| j                  k(  }	|	s)| j
                  j#                  d||| j                         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(  }
|
s)| j
                  j#                  d|| j$                         y| j+                  |      }| j,                  rbt/        |      }t/        | j,                        }|j1                  |      s1| j
                  j#                  d|t3        |      t3        |             yt5        |t7        |      ||t9        |      nd|      S 7 # t:        $ r | j
                  j                  d       Y yt<        t>        t@        tB        f$ r/}| j
                  j                  dt7        |             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.
        Nz8Token validation failed: unsupported critical JWT headerF)r}   strict_check_header)r}   registry	client_idazprv   unknownry   z2Bearer token rejected for client %s: token expiredrw   zJBearer token rejected for client %s: issuer mismatch (got %r, expected %r)rz   c              3  &   K   | ]  }|v  
 y wr9   rT   )r=   expectedrz   s     r*   r@   z0JWTVerifier.load_access_token.<locals>.<genexpr>	  s      -<IHOMr   zLBearer token rejected for client %s: audience mismatch (got %r, expected %r)zRBearer token rejected for client %s: missing required scopes (has %s, requires %s)r   r   r   
expires_atr   z5Token validation failed: JWT signature/format invalidzToken validation failed: %s)"r   r+   r)   r   rC   r   r   r   r2   r   r   r0   r   infor   r:   rA   warningr   rB   r   r   r   setissubsetsortedr   r;   r~   r   r'   r   r   AttributeError)r   r   verification_keyr(   r?   r   r   ry   rw   issuer_validaudience_validr   token_scopesr   rJ   rz   s                  @r*   load_access_tokenzJWTVerifier.load_access_token  sF    B	%)%?%?%FF ,,<dnnMC&u-F08!!N ZZ NN+$ $/(-	 f  

;' ::e$::e$ 	  **U#C3#4  H  {{jj'  %dkk40#&$++#5L $'$++#5L#KK''0!   }}jj' "'dmmT2!#t,), -<@MM- *
 *-T4==0I)I "#t,)-#)=),)=%KK''0!   ))&1F ##"6{"%d&:&:";&//=KK''7!|,/  i.'*3s8D g  Gv  	KKUVIx@ 	KK;SVD	s   N$L4 L1AL4 %N$&CL4 +N$,BL4 -N$.CL4 N$A>L4 
N$%L4 0N$1L4 4$N!N$N!2%NN$N!!N$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   )r_   zstr | bytes | Noner   r   r   r   r   r   r)   r   r   r   r   zAnyHttpUrl | str | Noner   boolr   zhttpx.AsyncClient | None)r   r;   r   str | bytes)rF   r   r   r;   )r   dict[str, Any])r   r   r   rK   r   r;   r   zAccessToken | None)rO   rP   rQ   rR   r   r   r   r   r   r   r   __classcell__r   s   @r*   r   r      s    * *.#)-+/ $,0,004e 'e 	e
 'e )e e *e *e e .eNQ=D~'."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
        )r   N)r   r   tokens)r   r  r   r   s      r*   r   zStaticTokenVerifier.__init__b  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   r   z$Token missing required scopes. Has: z, Required: r   r   )	r  r0   r   r   r   r   r   r   r   )r   r   
token_datar   r   r   r   s          r*   r   z StaticTokenVerifier.verify_tokenr  s     [[__U+
  ^^L1
!j499;&>"- v;L!$"6"67O"++L9:<.UdTef  -!
 	
s   CC
r9   )r  zdict[str, dict[str, Any]]r   r   r   )rO   rP   rQ   rR   r   r   r  r  s   @r*   r  r  P  s)    ( -1) * 
r,   r  )r(   zstr | bytes | JWKKeyDatar)   r;   )r3   r   r   r;   )r?   r   r   r   )r(   r   r   r   )=rR   
__future__r   r   r   r   dataclassesr   typingr   r   r   r   cryptography.hazmat.primitivesr   )cryptography.hazmat.primitives.asymmetricr	   joserfcr
   r   joserfc.errorsr   joserfc.jwsr   joserfc.registryr   pydanticr   r   typing_extensionsr   fastmcp.server.authr   r   fastmcp.server.auth.ssrfr   r   r   fastmcp.utilities.authr   r   fastmcp.utilities.loggingr   rO   r   dictr;   rA   r   rS   	frozensetr<   r+   r5   rC   rE   rW   r]   r   r   r  rT   r,   r*   <module>r     s    0 "    ! ' '  8 9  $ # 0 * ' : O O B 0	H	S#S	/12
I 2'(;< =@
iu 
y  $51\ \ 2\~=J3- J3Z?
- ?
r,   