Skip to content
New issue

Have a question about this project? # for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “#”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? # to your account

GraphServiceClient proxy is ignored #421

Closed
gguillen opened this issue Mar 30, 2019 · 8 comments
Closed

GraphServiceClient proxy is ignored #421

gguillen opened this issue Mar 30, 2019 · 8 comments

Comments

@gguillen
Copy link

When creating a new GraphServiceClient in .NET Core 2.2, passing in an HttpProvider that has the HttpClientHandler Proxy property set is not honored for requests. This previously performed as expected in our ASP.NET Core netcoreapp2.1 API, but fails when upgraded to netcoreapp2.2.

Expected behavior

When creating a GraphServiceClient by passing in a HttpProvider composed of a HttpClientHandler with a proxy set, the proxy will be used for requests to resources.

Actual behavior

An OperationCanceledException is thrown when the default request timeout elapses. Troubleshooting with our infrastructure team shows that our requests are hitting our outbound firewall and are being blocked. Our proxy admins do not see any logged connections to the proxy. I have tested the exact same proxy configuration by creating a simple netcoreapp2.2 console application and running it on the server to verify the proxy configuration using an HttpClient - it works. Then I created a File->New ASP.NET netcoreapp2.2 project, also testing the proxy settings using HttpClient - it works. Add the Graph Client with the same proxy settings, the Graph API requests time out.

Steps to recreate the issue

Create a vanilla ASP.NET Core 2.2 API. Add the following nuget packages:

<PackageReference Include="Microsoft.Graph" Version="1.14.0" />
<PackageReference Include="Microsoft.IdentityModel.Clients.ActiveDirectory" Version="4.5.1" />

Create a test controller with the following code and execute the controller method with a browser:

using Microsoft.AspNetCore.Mvc;
using Microsoft.Graph;
using Microsoft.IdentityModel.Clients.ActiveDirectory;
using System.Linq;
using System.Net;
using System.Net.Http;
using System.Net.Http.Headers;
using System.Security.Cryptography.X509Certificates;
using System.Threading.Tasks;

namespace AspNetCoreGraphApiTest.Controllers
{
    [Route("api/[controller]")]
    [ApiController]
    public class TestsController : ControllerBase
    {
        private static GraphServiceClient _client;

        // GET api/values
        [HttpGet]
        public async Task<IActionResult> Get()
        {
            var httpClientHandler = new HttpClientHandler
            {
                Proxy = new WebProxy("http://myproxy.mydomain.tld:3128"),
                UseDefaultCredentials = true
            };

            var httpProvider = new HttpProvider(httpClientHandler, false); // Setting disposeHandler to true does not affect the behavior

            var client = new GraphServiceClient(new DelegateAuthenticationProvider(async requestMessage =>
            {
                var authenticationContext = new AuthenticationContext("myauthority", false);

                var certificate = GetCertificate();
                var clientAssertionCertificate = new ClientAssertionCertificate("myclientid", certificate);

                var authenticationResult = await authenticationContext.AcquireTokenAsync("https://graph.microsoft.com/", clientAssertionCertificate);
                var accessToken = authenticationResult.AccessToken;

                // Append the access token to the request
                requestMessage.Headers.Authorization = new AuthenticationHeaderValue("Bearer", accessToken);

            }), httpProvider);

            var result = await client
                .Drives["mydriveid"]
                .Root
                .Children
                .Request().GetAsync(HttpContext.RequestAborted);

            var fileList = result.Select(f => f.Name).ToList();

            return Ok(fileList);
        }

        private X509Certificate2 GetCertificate()
        {
            ...

            return certificate;
        }
    }
}

Steps to reproduce the behavior

"dotnet publish" with SDK 2.2.104

Run the controller method.

@ghost ghost added the Needs: Triage label Mar 30, 2019
@darrelmiller
Copy link
Contributor

Can you double check what version of Microsoft.Graph.Core was pulled in ? We released a patch of Microsoft.Graph.Core to 1.14.1 today that had a fix for folks who use a custom HttpProvider.

@gguillen
Copy link
Author

gguillen commented Apr 1, 2019

@darrelmiller I explicitly set the version of Microsoft.Graph.Core and still have the same issue.

    <PackageReference Include="Microsoft.Graph" Version="1.14.0" />
    <PackageReference Include="Microsoft.Graph.Core" Version="1.14.1" />

@peombwa
Copy link
Member

peombwa commented Apr 2, 2019

@gguillen can you verify if the blocked outbound requests hitting your firewall are to login.microsoftonline.com or graph.microsoft.com?

@gguillen
Copy link
Author

gguillen commented Apr 3, 2019

@pwombwa I see denied requests to TCP 23.100.32.136(443) and TCP 23.100.32.138(443) across tests performed 3 minutes apart.

@gguillen
Copy link
Author

gguillen commented Apr 3, 2019

@pwombwa I'll check with another firewall admin in the morning and report back more detailed information.

@peombwa
Copy link
Member

peombwa commented Apr 3, 2019

@gguillen Looks like your authentication requests are not being chanelled to your proxy and you'll need to configure it to Microsoft.IdentityModel.Clients.ActiveDirectory (ADAL) as well. ADAL 4.5 (which you are refrencing) seems to be having an issue with setting a proxy, but they have a fix for it in ADAL 5.0.1-preview.

In ADAL 5.0.1-preview, you can pass your httpClientHandler (with proxy setup) as such:

// Implement ADAL's IHttpClientFactory
public class MyClientFactory: Microsoft.IdentityModel.Clients.ActiveDirectory.IHttpClientFactory
{
    private HttpClient httpClient;

    public MyClientFactory(HttpClientHandler httpClientHandler)
    {
        httpClient = new HttpClient(httpClientHandler);
    }
    public HttpClient GetHttpClient()
    {
        return httpClient;
    }
}

// Configure your proxy
var httpClientHandler = new HttpClientHandler
{
    Proxy = new WebProxy("http://myproxy.mydomain.tld:3128"),
    UseDefaultCredentials = true
};

var httpProvider = new HttpProvider(httpClientHandler, false);

var client = new GraphServiceClient(new DelegateAuthenticationProvider(async requestMessage =>
{
    // Pass your proxy to ADAL
    var authenticationContext = new AuthenticationContext("myauthority", false, null, new MyClientFactory(httpClientHandler));
    var authenticationResult = await authenticationContext.AcquireTokenAsync("https://graph.microsoft.com/", clientAssertionCertificate);
    
    // Append the access token to the request
    requestMessage.Headers.Authorization = new AuthenticationHeaderValue("Bearer", authenticationResult.AccessToken);
}), httpProvider);

@gguillen
Copy link
Author

gguillen commented Apr 3, 2019

@pwombwa That worked! That makes sense because we previously had another chunk of code that did a WebRequest.DefaultWebProxy assignment that must have covered both its function and also indirectly the ADAL authentication and our Graph API requests. Thanks!

@peombwa
Copy link
Member

peombwa commented Apr 3, 2019

@gguillen Great! We are also working on an authentication providers library for Microsoft Graph - Microsoft.Graph.Auth - that will simplify authentication when using our SDKs and handle such kind of issues.

@peombwa peombwa closed this as completed Apr 3, 2019
@ghost ghost locked as resolved and limited conversation to collaborators Feb 22, 2022
# for free to subscribe to this conversation on GitHub. Already have an account? #.
Labels
None yet
Projects
None yet
Development

No branches or pull requests

3 participants