microsoftgraph/msgraph-sdk-dotnet-core

Adding HttpRequestMessage logging handler causes unauthorized call

LockTar opened this issue · 7 comments

I'm trying to upgrade to v5 of the .NET SDK. In v4 we have a handler that logs the HttpRequestMessage.
The code is slightly changed in v5 in comparison with v4.

I now following the documentation in the repo and following the documentation in the Microsoft docs.

I think the documentation of Microsoft docs is better because the documentation here in the repository uses an overload of GraphClientFactory.CreateDefaultHandlers(authenticationProvider); that doesn't exist anymore.
So I'm following the Microsoft docs with the following code:

// https://learn.microsoft.com/dotnet/api/azure.identity.clientsecretcredential
var clientSecretCredential = new ClientSecretCredential(
    tenantId, clientId, clientSecret, options);

var authProvider = new AzureIdentityAuthenticationProvider(clientSecretCredential, scopes);

var handlers = GraphClientFactory.CreateDefaultHandlers();

// Add logging handler before the compression handler. Compression handler is created by CreateDefaultHandlers()
HttpRequestMessageLoggingHandler loggingHandler = new();
handlers.Insert(0, loggingHandler);

var httpClient = GraphClientFactory.Create(handlers);

var graphClient = new Microsoft.Graph.Beta.GraphServiceClient(httpClient, authProvider);

Simple logging handler:

public class HttpRequestMessageLoggingHandler : DelegatingHandler
{
    protected override async Task<HttpResponseMessage> SendAsync(HttpRequestMessage httpRequest, CancellationToken cancellationToken)
    {
        // log the request before it goes out.
        Console.WriteLine($"Sending request => method: '{httpRequest.Method.Method}', uri: '{httpRequest.RequestUri?.AbsoluteUri}'");

        // Always call base.SendAsync so that the request is forwarded through the pipeline.
        HttpResponseMessage httpResponse = await base.SendAsync(httpRequest, cancellationToken);

        // log the response as it comes back.
        Console.WriteLine(
            $"Received response => httpStatusCode: {(int)httpResponse.StatusCode}, reason: '{httpResponse.ReasonPhrase}'");

        return httpResponse;
    }
}

Expected behavior

Getting log of the request through my HttpRequestMessageLoggingHandler and result of the call that I make to the MS Graph.

Actual behavior

Steps to reproduce the behavior

Follow above code to create graph service client and make a call to an endpoint. You will get a 401.
User? user = await graphClient.Me.GetAsync();

I tested a bit more....

When I change in the above code:

var clientSecretCredential = new ClientSecretCredential(
    tenantId, clientId, clientSecret, options);

var authProvider = new AzureIdentityAuthenticationProvider(clientSecretCredential, scopes);

to:

var credential = new DefaultAzureCredential();
TokenProvider accessTokenProvider = new TokenProvider(credential);
var authProvider = new BaseBearerTokenAuthenticationProvider(accessTokenProvider);

everything is working...
So why isn't any accesstoken set when you use ClientSecretCredential with AzureIdentityAuthenticationProvider?

Thanks for raising this @LockTar

#623 has been created to fix the documentation bug.

To help us narrow down the issue, does this issue still persist when you create the authprovider as below?

var authProvider = new Microsoft.Graph.Authentication.AzureIdentityAuthenticationProvider(clientSecretCredential, scopes);

Thanks for raising this @LockTar

microsoftgraph/microsoft-graph-docs#623 has been created to fix the documentation bug.

Your welcome!

To help us narrow down the issue, does this issue still persist when you create the authprovider as below?

var authProvider = new Microsoft.Graph.Authentication.AzureIdentityAuthenticationProvider(clientSecretCredential, scopes);

I don't see any difference with my code? But still tested your line and I got the same 401 response. Acces token is empty.

Hey @LockTar,

Looks like the scopes parameter is passed incorrectly in your scenario. As you are using the ClientSecretCredential you can safely omit it and the SDK will prepopulate the default scopes for you.

var authProvider = new Microsoft.Graph.Authentication.AzureIdentityAuthenticationProvider(clientSecretCredential);

otherwise specify the parameter name as the second parameter for the authProvider is the allowedHosts

var authProvider = new Microsoft.Graph.Authentication.AzureIdentityAuthenticationProvider(clientSecretCredential, scopes: scopes);

Hey @LockTar,

Looks like the scopes parameter is passed incorrectly in your scenario. As you are using the ClientSecretCredential you can safely omit it and the SDK will prepopulate the default scopes for you.

var authProvider = new Microsoft.Graph.Authentication.AzureIdentityAuthenticationProvider(clientSecretCredential);

otherwise specify the parameter name as the second parameter for the authProvider is the allowedHosts

var authProvider = new Microsoft.Graph.Authentication.AzureIdentityAuthenticationProvider(clientSecretCredential, scopes: scopes);

@andrueastman I see what's happening. Your change fixed it for me. I tested both. Thanks for the help!

But I understand why this was happening in my code because this documentation is wrong as well. I copied it from that. Because that doc is wrong and because the second parameter is of the same type it went wrong. That doc should be updated as well so everything is up to date!

Thanks for the feedback there. That should be resolved with https://github.com/microsoftgraph/microsoft-graph-docs/pull/20565