
    kKj֛                    d   d Z ddlmZ ddlZddlmZ ddlmZ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 dd	lmZ dd
lmZ ddlmZmZ ddlmZ er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% e&h d      Z' G d de      Z( G d de      Z)ddZ*ddZ+ G d dee,         Z-ddZ.y)zAzure (Microsoft Entra) OAuth provider for FastMCP.

This provider implements Azure/Microsoft Entra ID OAuth authentication
using the OAuth Proxy pattern for non-DCR OAuth flows.
    )annotationsN)OrderedDict)TYPE_CHECKINGAnyLiteralcast)AsyncKeyValue)
Dependency)	MultiAuth)
OAuthProxy)JWTVerifier)decode_jwt_payloadparse_scopes)
get_loggerOnBehalfOfCredential)AuthorizationParams)OAuthClientInformationFull)
AnyHttpUrl)AuthProvider>   emailopenidprofileoffline_accessc                  d    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eddddd	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 dd	       Z	 	 	 	 	 	 d fd
ZddZddZ		 	 	 	 	 	 d fdZ
ddZddZ	 	 	 	 ddZddZddZ xZS )AzureProvideru
  Azure (Microsoft Entra) OAuth provider for FastMCP.

    This provider implements Azure/Microsoft Entra ID authentication using the
    OAuth Proxy pattern. It supports both organizational accounts and personal
    Microsoft accounts depending on the tenant configuration.

    Scope Handling:
    - required_scopes: Provide unprefixed scope names (e.g., ["read", "write"])
      → Automatically prefixed with identifier_uri during initialization
      → Validated on all tokens and advertised to MCP clients
    - additional_authorize_scopes: Provide full format (e.g., ["User.Read"])
      → NOT prefixed, NOT validated, NOT advertised to clients
      → Used to request Microsoft Graph or other upstream API permissions

    Features:
    - OAuth proxy to Azure/Microsoft identity platform
    - JWT validation using tenant issuer and JWKS
    - Supports tenant configurations: specific tenant ID, "organizations", or "consumers"
    - Custom API scopes and Microsoft Graph scopes in a single provider

    Setup:
    1. Create an App registration in Azure Portal
    2. Configure Web platform redirect URI: http://localhost:8000/auth/callback (or your custom path)
    3. Add an Application ID URI under "Expose an API" (defaults to api://{client_id})
    4. Add custom scopes (e.g., "read", "write") under "Expose an API"
    5. Set access token version to 2 in the App manifest: "requestedAccessTokenVersion": 2
    6. Create a client secret
    7. Get Application (client) ID, Directory (tenant) ID, and client secret

    Example:
        ```python
        from fastmcp import FastMCP
        from fastmcp.server.auth.providers.azure import AzureProvider

        # Standard Azure (Public Cloud)
        auth = AzureProvider(
            client_id="your-client-id",
            client_secret="your-client-secret",
            tenant_id="your-tenant-id",
            required_scopes=["read", "write"],  # Unprefixed scope names
            additional_authorize_scopes=["User.Read", "Mail.Read"],  # Optional Graph scopes
            base_url="http://localhost:8000",
            # identifier_uri defaults to api://{client_id}
        )

        # Azure Government
        auth_gov = AzureProvider(
            client_id="your-client-id",
            client_secret="your-client-secret",
            tenant_id="your-tenant-id",
            required_scopes=["read", "write"],
            base_authority="login.microsoftonline.us",  # Override for Azure Gov
            base_url="http://localhost:8000",
        )

        mcp = FastMCP("My App", auth=auth)
        ```
    NTr   login.microsoftonline.com)client_secretresource_base_urlidentifier_uri
issuer_urlredirect_pathadditional_authorize_scopes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base_authoritytoken_issuerhttp_clientenable_cimdc                  t        |      }|
rt        |
      xs g ng }d|vrg |d}|| _        || _        t               | _        d| _        d| _        |xs d| | _        || _        |xs	 d| d| d}d| d| d}|xs g D cg c]  }|t        vs| }}|st        d	      t        |||| j                  gd
||      }d| d| d}d| d| d} t        "| 5  || ||||||	|xs ||||||||||||       d}!|dk7  rd| }!t        j                  d||| j                  rd| j                   |!       yd|!       yc c}w )u4  Initialize Azure OAuth provider.

        Args:
            client_id: Azure application (client) ID from your App registration
            client_secret: Azure client secret from your App registration. Optional when
                using alternative credentials (e.g., managed identity with a custom
                _create_upstream_oauth_client override). When omitted, jwt_signing_key
                must be provided.
            tenant_id: Azure tenant ID (specific tenant GUID, "organizations", or "consumers")
            identifier_uri: Optional Application ID URI for your custom API (defaults to api://{client_id}).
                This URI is automatically prefixed to all required_scopes during initialization.
                Example: identifier_uri="api://my-api" + required_scopes=["read"]
                → tokens validated for "api://my-api/read"
            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 Azure App registration (defaults to "/auth/callback")
            base_authority: Azure authority base URL (defaults to "login.microsoftonline.com").
                For Azure Government, use "login.microsoftonline.us".
            token_issuer: Override the expected `iss` claim value for JWT validation.
                Defaults to the standard Entra ID issuer derived from `base_authority`
                and `tenant_id`. Pass an explicit string to enforce a specific issuer.
            required_scopes: Custom API scope names WITHOUT prefix (e.g., ["read", "write"]).
                - Automatically prefixed with identifier_uri during initialization
                - Validated on all tokens
                - Advertised in Protected Resource Metadata
                - Must match scope names defined in Azure Portal under "Expose an API"
                Example: ["read", "write"] → validates tokens containing ["api://xxx/read", "api://xxx/write"]
            additional_authorize_scopes: Microsoft Graph or other upstream scopes in full format.
                - NOT prefixed with identifier_uri
                - NOT validated on tokens
                - NOT advertised to MCP clients
                - Used to request additional permissions from Azure (e.g., Graph API access)
                Example: ["User.Read", "Mail.Read"]
                These scopes allow your FastMCP server to call Microsoft Graph APIs using the
                upstream Azure token, but MCP clients are unaware of them.
                Note: "offline_access" is automatically included to obtain refresh tokens.
            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 True, users see a consent screen before being redirected to Azure.
                When False, authorization proceeds directly without user confirmation.
                When "external", the built-in consent screen is skipped but no warning is
                logged, indicating that consent is handled externally (e.g. by the upstream IdP).
                SECURITY WARNING: Only set to False for local development or testing environments.
            http_client: Optional httpx.AsyncClient for connection pooling in JWKS fetches.
                When provided, the client is reused for JWT key fetches and the caller
                is responsible for its lifecycle. When None (default), a fresh client is created per fetch.
            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      Tapi://https:////v2.0/discovery/v2.0/keyszAzureProvider requires at least one non-OIDC scope in required_scopes (e.g., 'read', 'write'). OIDC scopes like 'openid', 'profile', 'email', and 'offline_access' are not included in Azure access token claims and cannot be used for scope enforcement.RS256)jwks_uriissueraudience	algorithmrequired_scopesr/   z/oauth2/v2.0/authorizez/oauth2/v2.0/token)upstream_authorization_endpointupstream_token_endpointupstream_client_idupstream_client_secrettoken_verifierbase_urlr   r"   r!   r$   r%   r&   r'   r(   r)   r*   r+   r,   valid_scopesr0    r   z using authority zAInitialized Azure OAuth provider for client %s with tenant %s%s%sz and identifier_uri N)r   
_tenant_id_base_authorityr   _obo_credentials_obo_max_credentials_obo_supportedr    r#   OIDC_SCOPES
ValueErrorr   super__init__loggerinfo)#self	client_idr   	tenant_idr=   rC   r   r    r!   r"   r#   r$   r%   r&   r'   r(   r)   r*   r+   r,   r-   r.   r/   r0   parsed_required_scopesparsed_additional_scopesr:   r9   svalidation_scopesrB   authorization_endpointtoken_endpointauthority_info	__class__s#                                     t/Users/ahmed/devFolder/Ultron/claude-voice/.venv/lib/python3.12/site-packages/fastmcp/server/auth/providers/azure.pyrN   zAzureProvider.__init__c   s$   D ".o!> + 45; 	! #;;'T)A'TCS'T$ $-
 IT),!" -D&0D6N( M8N+;1YKu!Mn-Qyk9MN /4"4
4!+9MA4 	 
 !%  %!4!45-#
 ~&a	{2HI 	 $N#31YK?QR
 	,B$2(#0)/'!-X)E)+*G1-2W0S+I/#) 	 	
. 8800@ANO<@<O<O"4#6#6"78	
 VX	
q
s   EE)r   custom_domainr    r.   c       	        J   d|v rt        d|      |/|j                  d      j                  d      j                  d      }|xs | d}| d| }|xs d| d| } | d
||||||||	d|
}t        |j                  t
              r|	|j                  _        d	|_        |S )a  Create an AzureProvider pre-configured for Azure AD B2C.

        Derives authority host, tenant path, and identifier URI from
        `tenant_name` and `policy_name`, then delegates to the standard
        constructor. Returns a plain `AzureProvider` instance.

        B2C issuer validation is disabled by default (`token_issuer=None`)
        because B2C issuers embed the tenant GUID. Pass an explicit
        `token_issuer` string once you know the real `iss` value.

        Azure AD B2C does **not** support OBO.

        Args:
            tenant_name: Short B2C tenant name without `.onmicrosoft.com`
                (e.g. `"mytenant"`).
            policy_name: User-flow or custom-policy name
                (e.g. `"B2C_1_susi"`).
            client_id: Application (client) ID from the B2C app registration.
            client_secret: Client secret from the B2C app registration.
            required_scopes: Custom API scope names without prefix
                (e.g. `["mcp-access"]`).
            base_url: Public base URL of this server.
            custom_domain: Custom domain for the B2C authority
                (e.g. `"auth.mycompany.com"`). Defaults to
                `{tenant_name}.b2clogin.com`.
            identifier_uri: Application ID URI. Defaults to
                `https://{tenant_name}.onmicrosoft.com/{client_id}`.
            token_issuer: Expected `iss` claim. `None` (default) disables
                issuer validation.
            **kwargs: Forwarded to `AzureProvider.__init__`.
        z.onmicrosoft.comz`tenant_name should be the short name without the .onmicrosoft.com suffix (e.g. 'mytenant'), got r4   zhttp://r5   z.b2clogin.comz.onmicrosoft.com/)rR   r   rS   r=   rC   r-   r    r.   F )rL   removeprefixrstrip
isinstance_token_validatorr   r:   rJ   )clstenant_namepolicy_namerR   r   r=   rC   r]   r    r.   kwargs	authoritytenant_pathuriproviders                  r\   from_b2czAzureProvider.from_b2c'  s    \ ,BBMR 
 $**:6i(  "B}M%B	$%6{mDT(;-7H T 

'!+$%

 

 h//=/;H%%,"'    c                   K   |}t        |d      r;t        |dd      }|,|j                  ddi      }|rt        j	                  d|       t
        |   ||       d{   }d|v rdnd}| | dS 7 w)a  Start OAuth transaction and redirect to Azure AD.

        Override parent's authorize method to filter out the 'resource' parameter
        which is not supported by Azure AD v2.0 endpoints. The v2.0 endpoints use
        scopes to determine the resource/audience instead of a separate parameter.

        Args:
            client: OAuth client information
            params: Authorization parameters from the client

        Returns:
            Authorization URL to redirect the user to Azure AD
        resourceN)updatezNFiltering out 'resource' parameter '%s' for Azure AD v2.0 (use scopes instead)?&zprompt=select_account)hasattrgetattr
model_copyrO   debugrM   	authorize)rQ   clientparamsparams_to_useoriginal_resourceauth_url	separatorr[   s          r\   rw   zAzureProvider.authorizev  s     * 6:& '
D A , & 1 1*d9K 1 L$LLh) *6=AA(?C	I;&;<< Bs   AA6 A4!A6c                    g }|D ]V  }|t         v r|j                  |       d|v sd|v r|j                  |       7|j                  | j                   d|        X |S )a	  Prefix unprefixed custom API scopes with identifier_uri for Azure.

        This helper centralizes the scope prefixing logic used in both
        authorization and token refresh flows.

        Scopes that are NOT prefixed:
        - Standard OIDC scopes (openid, profile, email, offline_access)
        - Fully-qualified URIs (contain "://")
        - Scopes with path component (contain "/")

        Note: Microsoft Graph scopes (e.g., User.Read) should be passed via
        `additional_authorize_scopes` or use fully-qualified format
        (e.g., https://graph.microsoft.com/User.Read).

        Args:
            scopes: List of scopes, may be prefixed or unprefixed

        Returns:
            List of scopes with identifier_uri prefix applied where needed
        ://r5   )rK   appendr    )rQ   scopesprefixedscopes       r\   _prefix_scopes_for_azurez&AzureProvider._prefix_scopes_for_azure  si    * E#&%3%< & 4#6#6"7q @A  rm   c                h    | j                    d}|D cg c]  }|j                  |       c}S c c}w )ux  Strip ``{identifier_uri}/`` from custom API scopes Azure echoes back.

        Inverse of :meth:`_prefix_scopes_for_azure`. Azure echoes the prefixed
        form (``api://{client_id}/read``) in its token response's ``scope``
        field, while MCP clients request and recognize the short form
        (``read``) — the same form advertised on
        ``/.well-known/oauth-authorization-server`` via ``valid_scopes``. Without
        this translation, strict clients compare requested vs. granted scopes
        and surface a "permissions not granted" warning (e.g. ChatGPT) even
        when nothing is actually wrong.

        OIDC scopes (``openid``, ``profile``, ``email``, ``offline_access``) and
        external resource URIs (Microsoft Graph, etc.) never carry the prefix,
        so :meth:`str.removeprefix` is a no-op on them and they pass through
        unchanged.
        r5   )r    r`   )rQ   r   prefixrV   s       r\   _translate_scopes_from_idpz(AzureProvider._translate_scopes_from_idp  s8    " ''(*0671v&777s   /c                   |j                  d      xs | j                  xs g }| j                  |      }| j                  r|j	                  | j                         |j                         }||d<   t        |   ||      S )zBuild Azure authorization URL with prefixed scopes.

        Overrides parent to prefix scopes with identifier_uri before sending to Azure,
        while keeping unprefixed scopes in the transaction for MCP clients.
        r   )getr=   r   r#   extendcopyrM   _build_upstream_authorize_url)rQ   txn_idtransactionunprefixed_scopesprefixed_scopesmodified_transactionr[   s         r\   r   z+AzureProvider._build_upstream_authorize_url  s     (OOH5S9M9MSQS 778IJ ++""4#C#CD  +//1)8X& w4V=QRRrm   c                    | j                  |xs g       }| j                  r"|j                  d | j                  D               t        t        j                  |            }t        j                  d|       |S )a  Prepare scopes for Azure authorization code exchange.

        Azure requires scopes during token exchange (AADSTS28003 error if missing).
        Azure only allows ONE resource per token request (AADSTS28000), so we only
        include scopes for this API plus OIDC scopes.

        Args:
            scopes: Scopes from the authorization request (unprefixed)

        Returns:
            List of scopes for Azure token endpoint
        c              3  2   K   | ]  }|t         v s|  y wNrK   .0rV   s     r\   	<genexpr>zCAzureProvider._prepare_scopes_for_token_exchange.<locals>.<genexpr>        #;aqK?O;   zToken exchange scopes: %s)r   r#   r   listdictfromkeysrO   rv   )rQ   r   r   deduplicateds       r\   "_prepare_scopes_for_token_exchangez0AzureProvider._prepare_scopes_for_token_exchange  sn     77"E ++"" #;;#  DMM/:;0,?rm   c                |   t         j                  d|       t        | j                  xs g       }|D cg c]	  }||vs| }}| j	                  |      }| j                  r"|j                  d | j                  D               t        t        j                  |            }t         j                  d|       |S c c}w )a  Prepare scopes for Azure token refresh.

        Azure requires fully-qualified scopes and only allows ONE resource per
        token request (AADSTS28000). We include scopes for this API plus OIDC scopes.

        Args:
            scopes: Base scopes from RefreshToken (unprefixed, e.g., ["read"])

        Returns:
            Deduplicated list of scopes formatted for Azure token endpoint
        zBase scopes from storage: %sc              3  2   K   | ]  }|t         v s|  y wr   r   r   s     r\   r   zEAzureProvider._prepare_scopes_for_upstream_refresh.<locals>.<genexpr>  r   r   z#Scopes for Azure token endpoint: %s)	rO   rv   setr#   r   r   r   r   r   )rQ   r   additional_scopes_setrV   base_scopesr   deduplicated_scopess          r\   $_prepare_scopes_for_upstream_refreshz2AzureProvider._prepare_scopes_for_upstream_refresh  s     	3V< !$D$D$D$J K"(K&QA5J,Jq&K 77D ++"" #;;#  #4==#AB:<OP"" Ls
   	B9B9c                  K   |j                  d      }|sy	 t        |      }i }g d}|D ]  }||v s||   ||<    |r!t        j                  dt	        |             |S y# t
        $ r }t        j                  d|       Y d}~yd}~ww xY ww)a  Extract claims from Azure token response to embed in FastMCP JWT.

        Decodes the Azure access token (which is a JWT) to extract user identity
        claims. This allows gateways to inspect upstream identity information by
        decoding the FastMCP JWT without needing server-side storage lookups.

        Azure access tokens contain claims like:
        - sub: Subject identifier (unique per user per application)
        - oid: Object ID (unique user identifier across Azure AD)
        - tid: Tenant ID
        - azp: Authorized party (client ID that requested the token)
        - name: Display name
        - given_name: First name
        - family_name: Last name
        - preferred_username: User principal name (email format)
        - upn: User Principal Name
        - email: Email address (if available)
        - roles: Application roles assigned to the user
        - groups: Group memberships (if configured)

        Args:
            idp_tokens: Full token response from Azure, containing access_token
                and potentially id_token.

        Returns:
            Dict of extracted claims, or None if extraction fails.
        access_tokenN)suboidtidazpname
given_namefamily_namepreferred_usernameupnr   rolesgroupsz6Extracted %d Azure claims for embedding in FastMCP JWTz"Failed to extract Azure claims: %s)r   r   rO   rv   len	Exception)rQ   
idp_tokensr   payloadclaims
claim_keysclaimes           r\   _extract_upstream_claimsz&AzureProvider._extract_upstream_claims$  s     < "~~n5$	 )6G &(FJ $G#$+ENF5M $ LK  	LL=qA	s3   BA! ,A! B!	B
*B BB

Bc                  K   | j                   st        d      t        d       ddlm} t        j                  |j                               j                         }|| j                  v r*| j                  j                  |       | j                  |   S | j                  | j                  |d| j                   d}| j                  | j                  j                         |d<   nt!        d	       |di |}|| j                  |<   t#        | j                        | j$                  kD  rZ| j                  j'                  d
      \  }}|j)                          d{    t#        | j                        | j$                  kD  rZ|S 7 )w)a~  Get a cached or new OnBehalfOfCredential for OBO token exchange.

        Credentials are cached by user assertion so the Azure SDK's internal
        token cache can avoid redundant OBO exchanges when the same user
        calls multiple tools with the same scopes.

        Args:
            user_assertion: The user's access token to exchange via OBO.

        Returns:
            A configured OnBehalfOfCredential ready for get_token() calls.

        Raises:
            NotImplementedError: If OBO is not supported (e.g. Azure AD B2C).
            ImportError: If azure-identity is not installed (requires fastmcp[azure]).
        zvAzure AD B2C does not support the On-Behalf-Of (OBO) flow. Use AzureProvider with standard Entra ID for OBO scenarios.zOBO token exchanger   r   r4   )rS   rR   user_assertionrh   Nr   zOBO token exchange requires either a client_secret or a subclass that overrides get_obo_credential() to provide alternative credentials (e.g., client_assertion_func for managed identity).F)lastr_   )rJ   NotImplementedError_require_azure_identityazure.identity.aior   hashlibsha256encode	hexdigestrH   move_to_endrF   _upstream_client_idrG   _upstream_client_secretget_secret_valuerL   r   rI   popitemclose)rQ   r   r   key
obo_kwargs
credential_evicteds           r\   get_obo_credentialz AzureProvider.get_obo_credentiall  s    " ""%N  	  45;nn^2245??A$'''!!--c2((-- 11,#D$8$8#9:	&

 ''3,,==? ' F 
 *7J7
%/c" $''(4+D+DD..66E6BJAw--/!! $''(4+D+DD  "s   EFF&FFc                  K   t        | j                  j                               }| j                  j                          |D ]  }	 |j	                          d{     y7 # t
        $ r t        j                  dd       Y Cw xY ww)z!Close all cached OBO credentials.NzError closing OBO credentialT)exc_info)r   rH   valuesclearr   r   rO   rv   )rQ   credentialsr   s      r\   close_obo_credentialsz#AzureProvider.close_obo_credentials  sw     400779:##%%JL &&((( &( L;dKLs<   AB	A#A!A#B	!A## BB	BB	)0rR   strr   
str | NonerS   r   r=   	list[str]rC   r   r   zAnyHttpUrl | str | Noner    r   r!   r   r"   r   r#   list[str] | Noner$   r   r%   zAsyncKeyValue | Noner&   zstr | bytes | Noner'   z&bool | Literal['remember', 'external']r(   r   r)   boolr*   
int | Noner+   r   r,   intr-   r   r.   r   r/   zhttpx.AsyncClient | Noner0   r   returnNone)re   r   rf   r   rR   r   r   r   r=   r   rC   r   r]   r   r    r   r.   r   rg   r   r   r   )rx   r   ry   r   r   r   )r   r   r   r   )r   r   r   dict[str, Any]r   r   )r   r   r   zdict[str, Any] | None)r   r   r   r   )r   r   )__name__
__module____qualname____doc__rN   classmethodrl   rw   r   r   r   r   r   r   r   r   __classcell__r[   s   @r\   r   r   '   ss   9~ %) 6:%)!%$(8<9=/3.2PT)-!%<@:>./9#'04 3B
 B
 "	B

 B
 #B
 B
 3B
 #B
 B
 "B
 &6B
 '7B
 -B
 ,B
  (N!B
" '#B
$ %B
& 0:'B
( .8)B
* ),+B
, -B
. !/B
0 .1B
2 3B
4 
5B
H  %) %)%)#'L L 	L
 L "L #L L "L #L !L L 
L L\#=*#= $#= 
	#=J!F8(SS(6S	S24#>F(F	FP7rLrm   r   c                  R     e Zd ZdZdddd	 	 	 	 	 	 	 	 	 d fdZedd       Z xZS )	AzureJWTVerifiera  JWT verifier pre-configured for Azure AD / Microsoft Entra ID.

    Auto-configures JWKS URI, issuer, audience, and scope handling from your
    Azure app registration details. Designed for Managed Identity and other
    token-verification-only scenarios where AzureProvider's full OAuth proxy
    isn't needed.

    Handles Azure's scope format automatically:
    - Validates tokens using short-form scopes (what Azure puts in ``scp`` claims)
    - Advertises full-URI scopes in OAuth metadata (what clients need to request)

    Example::

        from fastmcp.server.auth import RemoteAuthProvider
        from fastmcp.server.auth.providers.azure import AzureJWTVerifier
        from pydantic import AnyHttpUrl

        verifier = AzureJWTVerifier(
            client_id="your-client-id",
            tenant_id="your-tenant-id",
            required_scopes=["access_as_user"],
        )

        auth = RemoteAuthProvider(
            token_verifier=verifier,
            authorization_servers=[
                AnyHttpUrl("https://login.microsoftonline.com/your-tenant-id/v2.0")
            ],
            base_url="https://my-server.com",
        )
    Nr   )r=   r    r-   c                   |xs d| | _         h d}||v rdnd| d| d}t        | 	  d| d| d||| j                   gd|	       y)
aS  Initialize Azure JWT verifier.

        Args:
            client_id: Azure application (client) ID from your App registration
            tenant_id: Azure tenant ID (specific tenant GUID, "organizations", or "consumers").
                For multi-tenant apps ("organizations" or "consumers"), issuer validation
                is skipped since Azure tokens carry the actual tenant GUID as issuer.
            required_scopes: Scope names as they appear in Azure Portal under "Expose an API"
                (e.g., ["access_as_user", "read"]). These are validated against
                the short-form scopes in token ``scp`` claims, and automatically
                prefixed with identifier_uri for OAuth metadata.
            identifier_uri: Application ID URI (defaults to ``api://{client_id}``).
                Used to prefix scopes in OAuth metadata so clients know the full
                scope URIs to request from Azure.
            base_authority: Azure authority base URL (defaults to "login.microsoftonline.com").
                For Azure Government, use "login.microsoftonline.us".
        r3   >   common	consumersorganizationsNr4   r5   r6   r7   r8   )r9   r:   r;   r<   r=   )_identifier_urirM   rN   )	rQ   rR   rS   r=   r    r-   multi_tenant_valuesr:   r[   s	           r\   rN   zAzureJWTVerifier.__init__  s    4  .E6)1E
 G // N+1YKu= 	 	/q;OP!5!56+ 	 	
rm   c                    | j                   sg S g }| j                   D ]D  }|t        v sd|v sd|v r|j                  |       %|j                  | j                   d|        F |S )a  Return scopes with Azure URI prefix for OAuth metadata.

        Azure tokens contain short-form scopes (e.g., ``read``) in the ``scp``
        claim, but clients must request full URI scopes (e.g.,
        ``api://client-id/read``) from the Azure authorization endpoint. This
        property returns the full-URI form for OAuth metadata while
        ``required_scopes`` retains the short form for token validation.
        r   r5   )r=   rK   r   r   )rQ   r   r   s      r\   scopes_supportedz!AzureJWTVerifier.scopes_supported  sl     ##I))E#u~&4#7#7"8% AB	 *
 rm   )
rR   r   rS   r   r=   r   r    r   r-   r   )r   r   )r   r   r   r   rN   propertyr   r   r   s   @r\   r   r     s`    J -1%)9,
 ,
 	,

 *,
 #,
 ,
\  rm   r   c                N    	 ddl }y# t        $ r}t        |  d      |d}~ww xY w)zORaise ImportError with install instructions if azure-identity is not available.r   NzG requires the `azure` extra. Install with: pip install 'fastmcp[azure]')azure.identityImportError)featureazurer   s      r\   r   r     s;     i 9 9
 	s    	$$c                    t        | t              r| S t        | t              r&t        | j                  t              r| j                  S y)zOExtract an AzureProvider from an auth provider, unwrapping MultiAuth if needed.N)rb   r   r   server)auths    r\   _find_azure_providerr   #  s5    $&$	"z$++}'M{{rm   c                       e Zd ZdZddZddZy)_EntraOBOTokenaV  Dependency that performs OBO token exchange for Microsoft Entra.

    Uses azure.identity's OnBehalfOfCredential for async-native OBO,
    with automatic token caching and refresh. Credentials are cached on
    the AzureProvider so repeated tool calls reuse existing credentials
    and benefit from the Azure SDK's internal token cache.
    c                    || _         y r   r   )rQ   r   s     r\   rN   z_EntraOBOToken.__init__7  s	    rm   c                  K   t        d       ddlm}m}  |       }|t	        d       |       }t        |j                        }|+t	        dt        |j                        j                         |j                  |j                         d {   } |j                  | j                    d {   }|j                  S 7 17 w)NEntraOBOTokenr   )get_access_token
get_serverz7No access token available. Cannot perform OBO exchange.zPEntraOBOToken requires an AzureProvider as the auth provider. Current provider: )r   )r   fastmcp.server.dependenciesr  r  RuntimeErrorr   r   typer   r   token	get_tokenr   )rQ   r  r  r   r   azure_providerr   results           r\   
__aenter__z_EntraOBOToken.__aenter__:  s     0L')I  -fkk:!%%)&++%6%?%?$@B 
 *<<'-- = 
 

 ,z++T[[99||
 :s$   BCC C3C4CCN)r   r   )r   r   )r   r   r   r   rN   r  r_   rm   r\   r   r   .  s    rm   r   c                4    t        t        t        |             S )a(  Exchange the user's Entra token for a downstream API token via OBO.

    This dependency performs a Microsoft Entra On-Behalf-Of (OBO) token exchange,
    allowing your MCP server to call downstream APIs (like Microsoft Graph) on
    behalf of the authenticated user.

    Args:
        scopes: The scopes to request for the downstream API. For Microsoft Graph,
            use scopes like ["https://graph.microsoft.com/Mail.Read"] or
            ["https://graph.microsoft.com/.default"].

    Returns:
        A dependency that resolves to the downstream API access token string

    Raises:
        ImportError: If fastmcp[azure] is not installed
        RuntimeError: If no access token is available, provider is not Azure,
            or OBO exchange fails

    Example:
        ```python
        from fastmcp.server.auth.providers.azure import EntraOBOToken
        import httpx

        @mcp.tool()
        async def get_my_emails(
            graph_token: str = EntraOBOToken(["https://graph.microsoft.com/Mail.Read"])
        ):
            async with httpx.AsyncClient() as client:
                resp = await client.get(
                    "https://graph.microsoft.com/v1.0/me/messages",
                    headers={"Authorization": f"Bearer {graph_token}"}
                )
                return resp.json()
        ```

    Note:
        For OBO to work, ensure the scopes are included in the AzureProvider's
        `additional_authorize_scopes` parameter, and that admin consent has been
        granted for those scopes in your Entra app registration.
    )r   r   r   r  s    r\   r  r  U  s    T ^F+,,rm   )r   r   r   r   )r   zAuthProvider | Noner   zAzureProvider | None)r   r   r   r   )/r   
__future__r   r   collectionsr   typingr   r   r   r   httpxkey_value.aio.protocolsr	   fastmcp.dependenciesr
   fastmcp.server.auth.authr   fastmcp.server.auth.oauth_proxyr   !fastmcp.server.auth.providers.jwtr   fastmcp.utilities.authr   r   fastmcp.utilities.loggingr   r   r   mcp.server.auth.providerr   mcp.shared.authr   pydanticr   r   r   rO   	frozensetrK   r   r   r   r   r   r   r  r_   rm   r\   <module>r     s    #  # 4 4  1 + . 6 9 C 07<:#5	H	
 HIF
LJ F
LRa{ aP$Z_ $N*-rm   