Skip to content

Commit 8b5e890

Browse files
authored
Merge pull request #924 from microsoftgraph/feat/auth-handler
Add `create()` overloads to support adding Authorization handler to HTTP clients
2 parents b3e4021 + b77114d commit 8b5e890

21 files changed

+159
-41
lines changed

README.md

Lines changed: 23 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -36,7 +36,29 @@ For an example of authenticating a UWP app using the V2 Authentication Endpoint,
3636
You can create an instance of **HttpClient** that is pre-configured for making requests to Microsoft Graph APIs using `GraphClientFactory`.
3737

3838
```cs
39-
HttpClient httpClient = GraphClientFactory.Create( version: "beta");
39+
// The client credentials flow requires that you request the
40+
// /.default scope, and pre-configure your permissions on the
41+
// app registration in Azure. An administrator must grant consent
42+
// to those permissions beforehand.
43+
var scopes = new[] { "https://graph.microsoft.com/.default" };
44+
45+
// Values from app registration
46+
var clientId = "YOUR_CLIENT_ID";
47+
var tenantId = "YOUR_TENANT_ID";
48+
var clientSecret = "YOUR_CLIENT_SECRET";
49+
50+
// using Azure.Identity;
51+
var options = new ClientSecretCredentialOptions
52+
{
53+
AuthorityHost = AzureAuthorityHosts.AzurePublicCloud,
54+
};
55+
56+
// https://learn.microsoft.com/dotnet/api/azure.identity.clientsecretcredential
57+
var clientSecretCredential = new ClientSecretCredential(
58+
tenantId, clientId, clientSecret, options);
59+
60+
HttpClient httpClient = GraphClientFactory.create(tokenCredential: clientSecretCredential, version: "beta");
61+
4062
```
4163

4264
For more information on initializing a client instance, see the [library overview](https://docs.microsoft.com/en-us/graph/sdks/sdks-overview)

src/Microsoft.Graph.Core/Extensions/HttpRequestMessageExtensions.cs

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -6,11 +6,8 @@ namespace Microsoft.Graph
66
{
77
using System;
88
using System.Collections.Generic;
9-
using System.IO;
109
using System.Linq;
1110
using System.Net.Http;
12-
using System.Threading.Tasks;
13-
using Microsoft.Kiota.Http.HttpClientLibrary.Extensions;
1411

1512
/// <summary>
1613
/// Contains extension methods for <see cref="HttpRequestMessage"/>

src/Microsoft.Graph.Core/Extensions/IDecryptableContentExtensions.cs

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -9,10 +9,8 @@ namespace Microsoft.Graph
99
using System.Security.Cryptography;
1010
using System.Security.Cryptography.X509Certificates;
1111
using System.Text;
12-
using System.Text.Json;
1312
using System.Threading.Tasks;
1413
using Microsoft.Kiota.Abstractions.Serialization;
15-
using Microsoft.Kiota.Serialization.Json;
1614

1715
/// <summary>
1816
/// Contains extension methods for <see cref="IDecryptableContentExtensions"/>
@@ -43,7 +41,7 @@ public static class IDecryptableContentExtensions
4341

4442
/// <summary>
4543
/// Validates the signature and decrypted content attached with the notification.
46-
/// https://docs.microsoft.com/en-us/graph/webhooks-with-resource-data#decrypting-resource-data-from-change-notifications
44+
/// https://docs.microsoft.com/en-us/graph/webhooks-with-resource-data#decrypting-resource-data-from-change-notifications
4745
/// </summary>
4846
/// <param name="encryptedContent">The encrypted content of type <see cref="IDecryptableContent"/></param>
4947
/// <param name="certificateProvider">Certificate provider to decrypt the content.

src/Microsoft.Graph.Core/Microsoft.Graph.Core.csproj

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -68,10 +68,10 @@
6868
<PackageReference Include="Microsoft.Kiota.Abstractions" Version="1.14.0" />
6969
<PackageReference Include="Microsoft.Kiota.Authentication.Azure" Version="1.14.0" />
7070
<PackageReference Include="Microsoft.Kiota.Serialization.Json" Version="1.14.0" />
71-
<PackageReference Include="Microsoft.Kiota.Serialization.Text" Version="1.12.3" />
72-
<PackageReference Include="Microsoft.Kiota.Serialization.Form" Version="1.12.3" />
71+
<PackageReference Include="Microsoft.Kiota.Serialization.Text" Version="1.14.0" />
72+
<PackageReference Include="Microsoft.Kiota.Serialization.Form" Version="1.14.0" />
7373
<PackageReference Include="Microsoft.Kiota.Http.HttpClientLibrary" Version="1.14.0" />
74-
<PackageReference Include="Microsoft.Kiota.Serialization.Multipart" Version="1.12.3" />
74+
<PackageReference Include="Microsoft.Kiota.Serialization.Multipart" Version="1.14.0" />
7575
<PackageReference Include="Microsoft.VisualStudio.Threading.Analyzers" Version="17.11.20">
7676
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
7777
<PrivateAssets>all</PrivateAssets>
Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,4 @@
1-
using System.Reflection;
2-
using System.Resources;
3-
using System.Runtime.CompilerServices;
1+
using System.Runtime.CompilerServices;
42
#if DEBUG
53
[assembly: InternalsVisibleTo("Microsoft.Graph.DotnetCore.Core.Test")]
64
#endif

src/Microsoft.Graph.Core/Requests/AsyncMonitor.cs

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,6 @@ namespace Microsoft.Graph
77
using System;
88
using System.Net;
99
using System.Net.Http;
10-
using System.Text.Json;
1110
using System.Threading;
1211
using System.Threading.Tasks;
1312
using Microsoft.Kiota.Abstractions;

src/Microsoft.Graph.Core/Requests/Content/BatchRequestContent.cs

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,6 @@
55
namespace Microsoft.Graph
66
{
77
using System;
8-
using System.Collections;
98
using System.Collections.Generic;
109
using System.ComponentModel;
1110
using System.IO;

src/Microsoft.Graph.Core/Requests/Content/BatchResponseContentCollection.cs

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,6 @@
66
using System.Linq;
77
using System.Net;
88
using System.Net.Http;
9-
using System.Text.Json;
109
using System.Threading.Tasks;
1110
using Microsoft.Kiota.Abstractions;
1211
using Microsoft.Kiota.Abstractions.Serialization;

src/Microsoft.Graph.Core/Requests/DeltaResponseHandler.cs

Lines changed: 4 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,6 @@
44

55
namespace Microsoft.Graph
66
{
7-
using System;
87
using System.Collections.Generic;
98
using System.IO;
109
using System.Linq;
@@ -21,7 +20,7 @@ namespace Microsoft.Graph
2120
#endif
2221

2322
/// <summary>
24-
/// PREVIEW
23+
/// PREVIEW
2524
/// A response handler that exposes the list of changes returned in a response.
2625
/// This supports scenarios where the service expresses changes to 'null'. The
2726
/// deserializer can't express changes to null so you can now discover if a property
@@ -56,7 +55,7 @@ public async Task<ModelType> HandleResponseAsync<NativeResponseType, ModelType>(
5655
// set on the response body object.
5756
var responseString = await GetResponseStringAsync(responseMessage).ConfigureAwait(false);
5857

59-
// Get the response body object with the change list
58+
// Get the response body object with the change list
6059
// set on each response item.
6160
var responseWithChangeList = await GetResponseBodyWithChangelistAsync(responseString).ConfigureAwait(false);
6261
using var responseWithChangeListStream = new MemoryStream(Encoding.UTF8.GetBytes(responseWithChangeList));
@@ -112,7 +111,7 @@ private async Task<string> GetResponseBodyWithChangelistAsync(string deltaRespon
112111
// return a string instead.
113112
using (var responseJsonDocument = JsonDocument.Parse(deltaResponseBody))
114113
{
115-
// An array of delta objects. We will need to process
114+
// An array of delta objects. We will need to process
116115
// each one independently of each other.
117116
if (!responseJsonDocument.RootElement.TryGetProperty("value", out var pageOfDeltaObjects))
118117
{
@@ -193,7 +192,7 @@ private async Task GetObjectPropertiesAsync(JsonElement changedObject, List<stri
193192
var arrayEnumerator = property.Value.EnumerateArray();
194193
if (!arrayEnumerator.Any())
195194
{
196-
// Handle the edge case when the change involves changing to an empty array
195+
// Handle the edge case when the change involves changing to an empty array
197196
// as we can't observe elements in an empty collection in the foreach loop below
198197
changes.Add(parent);
199198
break;

src/Microsoft.Graph.Core/Requests/GraphClientFactory.cs

Lines changed: 56 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,9 @@ namespace Microsoft.Graph
1010
using System.Net.Http;
1111
using System.Net.Http.Headers;
1212
using System.Threading;
13+
using Azure.Core;
14+
using Microsoft.Graph.Authentication;
15+
using Microsoft.Kiota.Abstractions.Authentication;
1316
using Microsoft.Kiota.Http.HttpClientLibrary;
1417
using Microsoft.Kiota.Http.HttpClientLibrary.Middleware;
1518

@@ -107,6 +110,58 @@ public static HttpClient Create(
107110
return client;
108111
}
109112

113+
/// <summary>
114+
/// Creates a new <see cref="HttpClient"/> instance configured to authenticate requests using the provided <see cref="BaseBearerTokenAuthenticationProvider"/>.
115+
/// </summary>
116+
/// <param name="authenticationProvider">The authentication provider to initialise the Authorization handler</param>
117+
/// <param name="handlers">Custom middleware pipeline to which the Authorization handler is appended. If null, default handlers are initialised</param>
118+
/// <param name="version">The Graph version to use in the base URL</param>
119+
/// <param name="nationalCloud">The national cloud endpoint to use</param>
120+
/// <param name="proxy">The proxy to be used with the created client</param>
121+
/// <param name="finalHandler">The last HttpMessageHandler to HTTP calls.</param>
122+
/// <param name="disposeHandler">true if the inner handler should be disposed of by Dispose(), false if you intend to reuse the inner handler..</param>
123+
/// <returns>An <see cref="HttpClient"/> instance with the configured handlers</returns>
124+
public static HttpClient Create(
125+
BaseBearerTokenAuthenticationProvider authenticationProvider,
126+
IEnumerable<DelegatingHandler> handlers = null,
127+
string version = "v1.0",
128+
string nationalCloud = Global_Cloud,
129+
IWebProxy proxy = null,
130+
HttpMessageHandler finalHandler = null,
131+
bool disposeHandler = true)
132+
{
133+
if (handlers == null)
134+
{
135+
handlers = CreateDefaultHandlers();
136+
}
137+
var handlerList = handlers.ToList();
138+
handlerList.Add(new AuthorizationHandler(authenticationProvider));
139+
return Create(handlerList, version, nationalCloud, proxy, finalHandler, disposeHandler);
140+
}
141+
142+
/// <summary>
143+
/// Creates a new <see cref="HttpClient"/> instance configured to authenticate requests using the provided <see cref="TokenCredential"/>.
144+
/// </summary>
145+
/// <param name="tokenCredential">Token credential object use to initialise an <see cref="AzureIdentityAuthenticationProvider"/></param>
146+
/// <param name="handlers">Custom middleware pipeline to which the Authorization handler is appended. If null, default handlers are initialised</param>
147+
/// <param name="version">The Graph version to use in the base URL</param>
148+
/// <param name="nationalCloud">The national cloud endpoint to use</param>
149+
/// <param name="proxy">The proxy to be used with the created client</param>
150+
/// <param name="finalHandler">The last HttpMessageHandler to HTTP calls</param>
151+
/// <param name="disposeHandler">true if the inner handler should be disposed of by Dispose(), false if you intend to reuse the inner handler.</param>
152+
/// <returns>An <see cref="HttpClient"/> instance with the configured handlers</returns>
153+
public static HttpClient Create(
154+
TokenCredential tokenCredential,
155+
IEnumerable<DelegatingHandler> handlers = null,
156+
string version = "v1.0",
157+
string nationalCloud = Global_Cloud,
158+
IWebProxy proxy = null,
159+
HttpMessageHandler finalHandler = null,
160+
bool disposeHandler = true)
161+
{
162+
return Create(new AzureIdentityAuthenticationProvider(tokenCredential, null, null, true), handlers, version, nationalCloud, proxy, finalHandler, disposeHandler);
163+
}
164+
110165
/// <summary>
111166
/// Create a default set of middleware for calling Microsoft Graph
112167
/// </summary>
@@ -211,7 +266,7 @@ internal static HttpMessageHandler GetNativePlatformHttpHandler(IWebProxy proxy
211266
return new WinHttpHandler { Proxy = proxy, AutomaticDecompression = DecompressionMethods.GZip | DecompressionMethods.Deflate , WindowsProxyUsePolicy = proxyPolicy, SendTimeout = Timeout.InfiniteTimeSpan, ReceiveDataTimeout = Timeout.InfiniteTimeSpan, ReceiveHeadersTimeout = Timeout.InfiniteTimeSpan };
212267
#elif NET6_0_OR_GREATER
213268
//use resilient configs when we can https://learn.microsoft.com/en-us/aspnet/core/fundamentals/http-requests?view=aspnetcore-5.0#alternatives-to-ihttpclientfactory-1
214-
return new SocketsHttpHandler { Proxy = proxy, AllowAutoRedirect = false, AutomaticDecompression = DecompressionMethods.All, PooledConnectionLifetime = TimeSpan.FromMinutes(1)};
269+
return new SocketsHttpHandler { Proxy = proxy, AllowAutoRedirect = false, AutomaticDecompression = DecompressionMethods.All, PooledConnectionLifetime = TimeSpan.FromMinutes(1)};
215270
#else
216271
return new HttpClientHandler { Proxy = proxy, AllowAutoRedirect = false, AutomaticDecompression = DecompressionMethods.GZip | DecompressionMethods.Deflate };
217272
#endif

0 commit comments

Comments
 (0)