msgraph-sdk-python icon indicating copy to clipboard operation
msgraph-sdk-python copied to clipboard

Proxy is not respected

Open max-wittig opened this issue 2 years ago • 3 comments

I followed the steps here and its not using the proxy at all. My requests just time out: https://learn.microsoft.com/en-us/graph/sdks/customize-client?tabs=python#configuring-the-http-proxy-for-the-client

While normal requests using httx work, the requests to Azure do not. I'm guessing that this new client is not accepted somewhere down the chain

import asyncio
from azure.identity.aio import ClientSecretCredential
from django.conf import settings
from httpx import AsyncClient
from kiota_authentication_azure.azure_identity_authentication_provider import (
    AzureIdentityAuthenticationProvider,
)
from msgraph import GraphRequestAdapter, GraphServiceClient
from msgraph.generated.users.users_request_builder import UsersRequestBuilder
from msgraph_core import GraphClientFactory


def get_azure_client() -> tuple[GraphServiceClient, AsyncClient]:
    credential = ClientSecretCredential(
        settings.AZURE_TENANT_ID, settings.AZURE_CLIENT_ID, settings.AZURE_CLIENT_SECRET  # type: ignore
    )
    auth_provider = AzureIdentityAuthenticationProvider(
        credential, scopes=["https://graph.microsoft.com/.default"]
    )
    # see: https://learn.microsoft.com/en-us/graph/sdks/customize-client?tabs=python#configuring-the-http-proxy-for-the-client
    httpx_proxies = {
        "http://": settings.INTERNET_PROXY,
        "https://": settings.INTERNET_PROXY,
    }
    http_client = AsyncClient(proxies=httpx_proxies)  # type: ignore
    http_client = GraphClientFactory.create_with_default_middleware(client=http_client)
    adapter = GraphRequestAdapter(auth_provider, http_client)
    return GraphServiceClient(request_adapter=adapter), http_client


async def amain() -> None:
    client, http_client = get_azure_client()
    await http_client.get("https://microsoft.com") # this works
    await client.groups.by_group_id(  # this times out
        settings.AZURE_GROUP_ID
    ).transitive_members.get()


if __name__ == "__main__":
    loop = asyncio.new_event_loop()
    asyncio.set_event_loop(loop)
    try:
        loop.run_until_complete(amain())
    except KeyboardInterrupt:
        pass

/cc @nejch

max-wittig avatar Dec 20 '23 16:12 max-wittig

Can you confirm if attempting to disable ssl verification via this approach is also a symptom of this problem?

from msgraph import GraphRequestAdapter
from msgraph_core import GraphClientFactory

http_client = GraphClientFactory.create_with_default_middleware(client=httpx.AsyncClient(verify=False))
request_adapter = GraphRequestAdapter(auth_provider, http_client)
client = GraphServiceClient(request_adapter=request_adapter)

gives error of:

ClientSecretCredential.get_token failed: Cannot connect to host login.microsoftonline.com:443 ssl:True [SSLCertVerificationError: (5, '[SSL: CERTIFICATE_VERIFY_FAILED] certificate verify failed: self-signed certificate in certificate chain (_ssl.c:1006)')]
---------------------------------------------------------------------------
SSLCertVerificationError                  Traceback (most recent call last)
File /opt/conda/lib/python3.11/site-packages/aiohttp/connector.py:992, in TCPConnector._wrap_create_connection(self, req, timeout, client_error, *args, **kwargs)
    989     async with ceil_timeout(
    990         timeout.sock_connect, ceil_threshold=timeout.ceil_threshold
    991     ):
--> 992         return await self._loop.create_connection(*args, **kwargs)
    993 except cert_errors as exc:

Update: A total hack to get around it, for those who can do this - find the aiohttp python module connector.py, and find where the default value for verify_ssl is set to True, and simply set it to false. Works for me, as I have nothing else doing aio.

carnak avatar Mar 07 '24 20:03 carnak

Hey there; ran into the same issue these past few days.

What solved it for us was specifying proxies=proxies in the Credential class, as per the tutorial:

# Proxy URLs
proxies = {
    'http': 'http://proxy-url',
    'https': 'http://proxy-url',
}

# Create a token credential with the proxies. It can be any
# of the credential classes from azure.identity
credential = DeviceCodeCredential(
    "client_id", tenant_id = "tenant_id", proxies = proxies)

FYI, in our case we are using CertificateCredential class, but it shouldn't change much.

It seems that this proxies parameter is passed through **kwargs in the Credential parent class, MsalCredential from azure.identity. The furthest I got is here, where a proxies keyword argument can be accepted.

Both proxies parameters are needed. Using only the proxies=proxies argument (without building httpx_proxies in a specific request adapter) does not work. Not specifying proxies=proxies in the Credential class arguments does not work for me either (as shown in your case).

Since proxies & httpx_proxies are different objects, it seems HTTPX is used in the msgraph library (which needs httpx_proxies), and another adapter is used in azure.identity (which need the proxies parameter).

nitneuqr avatar Apr 05 '24 08:04 nitneuqr

Thanks a lot for sharing @nitneuqr, we just tested your solution and it also works for us. 🙏🏻

Our currently-working solution (a bit anonymized) with ClientSecretCredential is the following:

proxies = {
    "http": "http://proxy-url",
    "https": "http://proxy-url",
    "no_proxy": "no-proxy.com",
}
credential = ClientSecretCredential("tenant_id", "client_id", "client_secret", proxies=proxies )
auth_provider = AzureIdentityAuthenticationProvider(credential, scopes=["https://graph.microsoft.com/.default"])

httpx_proxies = {
    "http://": "http://proxy-url",
    "https://": "http://proxy-url",
}
http_client = AsyncClient(proxies=httpx_proxies, timeout=300) 

/cc @max-wittig

antoineauger avatar Apr 16 '24 14:04 antoineauger