
    kKjI                        d Z ddlmZ ddlZddlmZ ddlZddlmZ ddl	m
Z
 ddlmZ ddlmZ dd	lmZ dd
lmZ ddlmZ  ee      Z G d de      Z G d de      Zy)a  Clerk OAuth provider for FastMCP.

This module provides a complete Clerk OAuth integration that's ready to use
with a Clerk domain, client ID, and client secret. It handles all the complexity
of Clerk's OAuth/OIDC flow, token validation, and user management.

Clerk uses standard OIDC endpoints derived from the instance domain
(e.g., ``https://<instance>.clerk.accounts.dev``). Token verification is
performed via the introspection endpoint (RFC 7662) for security-critical
checks (active status, audience, scopes), followed by the userinfo endpoint
for profile enrichment. Userinfo failure is non-fatal.

Example:
    ```python
    from fastmcp import FastMCP
    from fastmcp.server.auth.providers.clerk import ClerkProvider

    auth = ClerkProvider(
        domain="saving-primate-16.clerk.accounts.dev",
        client_id="your-clerk-client-id",
        client_secret="your-clerk-client-secret",
        base_url="https://my-server.com",
    )

    mcp = FastMCP("My Protected Server", auth=auth)
    ```
    )annotationsN)Literal)AsyncKeyValue)
AnyHttpUrl)TokenVerifier)AccessToken)
OAuthProxy)parse_scopes)
get_loggerc                  P     e Zd ZdZdddddd	 	 	 	 	 	 	 	 	 	 	 d fdZddZ xZS )	ClerkTokenVerifieru  Token verifier for Clerk OAuth tokens.

    Clerk issues standard OIDC tokens. Verification uses the introspection
    endpoint (RFC 7662) as the primary security gate — it confirms the token
    is active and provides metadata (scopes, expiry, audience). The userinfo
    endpoint is called second for profile enrichment (name, email, picture)
    and its failure is non-fatal.

    When a ``client_id`` is configured, the audience from introspection is
    validated against it. When ``required_scopes`` are configured,
    introspection must return the token's scopes — the verifier will not
    assume scopes when introspection is unavailable.
    N
   )	client_idclient_secretrequired_scopestimeout_secondshttp_clientc                   t         |   |       |j                  d      | _        || _        || _        || _        || _        d| j                   d| _        d| j                   d| _	        y)a  Initialize the Clerk token verifier.

        Args:
            domain: Clerk instance domain (e.g., "saving-primate-16.clerk.accounts.dev")
            client_id: Clerk OAuth client ID, used for introspection endpoint authentication
            client_secret: Clerk OAuth client secret, used for introspection endpoint authentication
            required_scopes: Required OAuth scopes (e.g., ["openid", "email", "profile"])
            timeout_seconds: HTTP request timeout
            http_client: Optional httpx.AsyncClient for connection pooling. When provided,
                the client is reused across calls and the caller is responsible for its
                lifecycle. When None (default), a fresh client is created per call.
        )r   /https://z/oauth/userinfoz/oauth/token_infoN)
super__init__rstripdomain
_client_id_client_secretr   _http_client_userinfo_url_introspection_url)selfr   r   r   r   r   r   	__class__s          t/Users/ahmed/devFolder/Ultron/claude-voice/.venv/lib/python3.12/site-packages/fastmcp/server/auth/providers/clerk.pyr   zClerkTokenVerifier.__init__>   so    , 	9mmC(#+.''}OD$,T[[M9J"K    c                	  K   	 | j                   t        j                  | j                         nt        j                  | j
                        4 d{   }d|i}|ddid}| j                  r(| j                  r| j                  | j                  f|d<   n| j                  r| j                  |d<    |j                  | j                  fi | d{   }|j                  d	k7  r2t        j                  d
|j                         	 ddd      d{    y|j                         }d|vs|d   s't        j                  d       	 ddd      d{    y|j                  dd      }|r|j                         ng }|j                  d      xs |j                  d      }	d}
|j                  d      }|2t        j                   t"        t$              5  t'        |      }
ddd       | j                  rB|	| j                  k7  r3t        j                  d|	| j                         	 ddd      d{    y| j(                  r|s2t        j                  d| j(                         	 ddd      d{    yt+        |      }t+        | j(                        }|j-                  |      s)t        j                  d||       	 ddd      d{    y|j                  d      }i }	 |j                  | j.                  d| dd       d{   }|j                  d	k(  r#|j                         }|s|j                  d      }|s't        j                  d       	 ddd      d{    yt3        ||	xs |||
||	|j                  d      |j                  d      |j                  d      |j                  d      |j                  d      |j                  d      |j                  d       |j                  d!      |xs dd"#      }t        j                  d$|       |cddd      d{    S 7 7 a7 $7 # 1 sw Y   ]xY w7 7 7 7 G# t0        $ r!}t        j                  d|       Y d}~8d}~ww xY w7 7 Z# 1 d{  7  sw Y   yxY w# t        j4                  $ r }t        j                  d%|       Y d}~yd}~wt0        $ r }t        j                  d&|       Y d}~yd}~ww xY ww)'aG  Verify a Clerk OAuth token via introspection and userinfo.

        Calls the introspection endpoint first to validate the token and
        retrieve auth metadata (active status, scopes, expiry, audience).
        If the token passes security checks, the userinfo endpoint is called
        for profile enrichment. Userinfo failure is non-fatal.

        When a ``client_id`` is configured, the token's audience must match it.
        When ``required_scopes`` are configured, introspection must confirm
        them; tokens are rejected if scope information is unavailable.
        N)timeouttoken
User-AgentzFastMCP-Clerk-OAuth)dataheadersauthr      zClerk introspection failed: %dactivez=Clerk introspection: token inactive or missing 'active' fieldscope audexpz2Clerk token audience mismatch: got %s, expected %szGClerk token missing scope information; cannot verify required scopes %sz5Clerk token missing required scopes. Has %s, needs %ssubzBearer )Authorizationr'   )r)   zClerk userinfo call failed: %szClerk token missing 'sub' claimemailemail_verifiednamepicture
given_namefamily_namepreferred_usernameiss)r1   r/   r3   r4   r5   r6   r7   r8   r9   r:   clerk_user_data)r&   r   scopes
expires_atclaimsz,Clerk token verified successfully for sub=%sz Failed to verify Clerk token: %sz"Clerk token verification error: %s)r   
contextlibnullcontexthttpxAsyncClientr   r   r   postr   status_codeloggerdebugjsongetsplitsuppress
ValueError	TypeErrorintr   setissubsetr   	Exceptionr   RequestError)r    r&   clientintrospect_data_payloadintrospect_kwargsintrospect_responseintrospect_data	scope_strtoken_scopesr/   r=   r0   token_scopes_setrequired_scopes_setr1   	user_datauserinfo_responseeaccess_tokens                      r"   verify_tokenzClerkTokenVerifier.verify_token^   s    C	 $$0 &&t'8'89&&t/C/CDE E  29%0@'3 ,.CD+!
 ??t':':++1%f- __;???+K8,7FKK++-'- '#
 '22c9LL8+77  =E E E@ #6":":"< ?2/(:SLLW  QE E ET ,//<	4=y02%))%0TO4G4G4T)-
%))%0?#,,ZC%(X
 D ??sdoo'=LLL
  wE E Ez '''? 00
  $IE E EJ (+<'8$*-d.B.B*C'.778HIS,/
  $[E E Eb &))%0"$	F.4jj**/6ug->*?! /9 / )% )44;$5$:$:$<	""+--"6C LL!BCIE E EL  +!jS')""!*w!7*3--8H*I )f 5#,==#;&/mmL&A'0}}]'C.7mm<P.Q(}}U3+4+< & KSQ#uE E E(')E Ed DCeE E Eh) ! FLL!A1EEFE E E E Ex !! 	LL;Q? 	LL=qA	s  S9AR P0R A;RP34RR P6R S9/RR P9R S9A=RP<!AR&R 1Q	2R 6S97/R&R 1Q2R 6S97A	R R QR S9R%&QQ6QRR %R&R *S9+B3RR *R+R /S90R 3R6R 9R <Q	R	R R R Q	Q?Q:4R:Q??RR R RRRR S9R S6/S
S9
S6S1,S91S66S9)r   strr   
str | Noner   ra   r   list[str] | Noner   rM   r   httpx.AsyncClient | None)r&   r`   returnzAccessToken | None)__name__
__module____qualname____doc__r   r_   __classcell__r!   s   @r"   r   r   /   sh    $ !%$(,0!04L L 	L
 "L *L L .L@Or#   r   c                       e Zd ZdZdddddddddddddddddddd	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 d fdZ xZS )	ClerkProvidera?  Complete Clerk OAuth provider for FastMCP.

    This provider makes it trivial to add Clerk OAuth protection to any
    FastMCP server. Provide your Clerk instance domain, OAuth app credentials,
    and a base URL, and you're ready to go.

    Clerk uses standard OIDC endpoints derived from the instance domain.
    All endpoint URLs are constructed automatically from the domain parameter.

    Features:
    - Transparent OAuth proxy to Clerk
    - Automatic token validation via Clerk's userinfo & introspection APIs
    - User information extraction from Clerk's OIDC claims
    - PKCE support (S256)
    - Minimal configuration required

    Example:
        ```python
        from fastmcp import FastMCP
        from fastmcp.server.auth.providers.clerk import ClerkProvider

        auth = ClerkProvider(
            domain="saving-primate-16.clerk.accounts.dev",
            client_id="your-clerk-client-id",
            client_secret="your-clerk-client-secret",
            base_url="https://my-server.com",
        )

        mcp = FastMCP("My App", auth=auth)
        ```
    Nr   Tr   )r   resource_base_url
issuer_urlredirect_pathr   valid_scopesr   allowed_client_redirect_urisclient_storagejwt_signing_keyrequire_authorization_consentconsent_csp_policyforward_resource%fallback_refresh_token_expiry_seconds#fastmcp_access_token_expiry_secondstoken_expiry_threshold_secondsextra_authorize_paramsr   enable_cimdc               >   |j                  d      }|t        |      ng d}|	t        |	      nd}t        |||||
|      }|rt        |      ni }t        |   d| dd| d|||||||xs |||||||||||xs d||       t        j                  d	||       y)
a[  Initialize Clerk OAuth provider.

        Args:
            domain: Clerk instance domain (e.g., "saving-primate-16.clerk.accounts.dev").
                This is used to derive all OAuth/OIDC endpoint URLs.
            client_id: Clerk OAuth application client ID
            client_secret: Clerk OAuth application client secret.
                Optional for PKCE public clients. When omitted, jwt_signing_key must be provided.
            base_url: Public URL where OAuth endpoints will be accessible (includes any mount path)
            resource_base_url: Optional public base URL for the protected resource metadata
                and token audience. Defaults to ``base_url``.
            issuer_url: Issuer URL for OAuth metadata (defaults to base_url). Use root-level URL
                to avoid 404s during discovery when mounting under a path.
            redirect_path: Redirect path configured in Clerk OAuth app (defaults to "/auth/callback")
            required_scopes: Required Clerk scopes (defaults to ["openid", "email", "profile"]).
                Clerk supports: "openid", "email", "profile", "public_metadata",
                "private_metadata", "offline_access".
            valid_scopes: All scopes that clients are allowed to request, advertised through
                well-known endpoints. Defaults to required_scopes if not provided.
            timeout_seconds: HTTP request timeout for Clerk API calls (defaults to 10)
            allowed_client_redirect_uris: List of allowed redirect URI patterns for MCP clients.
                If None (default), all URIs are allowed. If empty list, no URIs are allowed.
            client_storage: Storage backend for OAuth state (client registrations, encrypted tokens).
                If None, an encrypted file store will be created in the data directory
                (derived from ``platformdirs``).
            jwt_signing_key: Secret for signing FastMCP JWT tokens (any string or bytes). If bytes
                are provided, they will be used as is. If a string is provided, it will be derived
                into a 32-byte key. If not provided, the upstream client secret will be used to
                derive a 32-byte key using PBKDF2.
            require_authorization_consent: Whether to require user consent before authorizing
                clients (default True). When "external", the built-in consent screen is skipped
                but no warning is logged, indicating that consent is handled externally by Clerk.
            consent_csp_policy: Custom CSP policy for the consent page.
            extra_authorize_params: Additional parameters to forward to Clerk's authorization
                endpoint. Example: {"prompt": "login"} to force re-authentication.
            http_client: Optional httpx.AsyncClient for connection pooling in token verification.
                When provided, the client is reused across verify_token calls and the caller
                is responsible for its lifecycle. When None (default), a fresh client is created
                per call.
            enable_cimd: Enable CIMD (Client ID Metadata Document) support for URL-based
                client IDs (default True). Set to False to disable.
            fallback_refresh_token_expiry_seconds: Lifetime for the FastMCP-issued
                refresh token when the upstream provider omits `refresh_expires_in`
                (e.g. Cognito, GitHub, many OIDC IdPs). Defaults to 1 year. The upstream
                refresh remains the source of truth. See `OAuthProxy` for details.
            fastmcp_access_token_expiry_seconds: Lifetime for the FastMCP-issued access
                token, decoupling it from the upstream provider's `expires_in`. Defaults
                to None (mirror the upstream lifetime). Set this for bridges whose
                upstream issues short-lived access tokens that some MCP clients can't
                refresh gracefully (e.g. `mcp-remote`). See `OAuthProxy` for details.
            token_expiry_threshold_seconds: Number of seconds before actual expiry to
                treat a token as expired, refreshing early to avoid races. Defaults to 0.
        r   N)openidr3   profile)r   r   r   r   r   r   r   z/oauth/authorizez/oauth/token)upstream_authorization_endpointupstream_token_endpointupstream_client_idupstream_client_secrettoken_verifierbase_urlrm   ro   rn   rq   rr   rs   rt   ru   rv   rw   rx   ry   rz   rp   r{   z>Initialized Clerk OAuth provider for domain %s with scopes: %s)r   r
   r   dictr   r   rE   rF   )r    r   r   r   r   rm   rn   ro   r   rp   r   rq   rr   rs   rt   ru   rv   rw   rx   ry   rz   r   r{   required_scopes_finalparsed_valid_scopesr   extra_authorize_params_finalr!   s                              r"   r   zClerkProvider.__init__  s    ^ s# * )/ 	 +7*BL& 	 ,'1+#
 -CD'( 	% 	.6vh>N,O&.vhl$C(#0)/'!-X)E)+*G1-2W0S+I#?#G4,#+ 	 	
0 	L!	
r#   ),r   r`   r   r`   r   ra   r   zAnyHttpUrl | strrm   AnyHttpUrl | str | Nonern   r   ro   ra   r   rb   rp   rb   r   rM   rq   rb   rr   zAsyncKeyValue | Noners   zstr | bytes | Nonert   z&bool | Literal['remember', 'external']ru   ra   rv   boolrw   
int | Nonerx   r   ry   rM   rz   zdict[str, str] | Noner   rc   r{   r   )re   rf   rg   rh   r   ri   rj   s   @r"   rl   rl      sC   J %)59.2$(,0)-!9=/3.2PT)-!%<@:>./8<04 1D
 D
 	D

 "D
 #D
 3D
 ,D
 "D
 *D
 'D
 D
 '7D
 -D
 ,D
  (N!D
" '#D
$ %D
& 0:'D
( .8)D
* ),+D
, !6-D
. ./D
0 1D
 D
r#   rl   )rh   
__future__r   r?   typingr   rA   key_value.aio.protocolsr   pydanticr   fastmcp.server.authr   fastmcp.server.auth.authr   fastmcp.server.auth.oauth_proxyr	   fastmcp.utilities.authr
   fastmcp.utilities.loggingr   re   rE   r   rl    r#   r"   <module>r      sT   8 #    1  - 0 6 / 0	H	~ ~Be
J e
r#   