Skip to content

Commit 142a315

Browse files
committed
Add example to use delegating handlers for auth
1 parent f8a37e0 commit 142a315

File tree

6 files changed

+105
-3
lines changed

6 files changed

+105
-3
lines changed

3-WebApp-multi-APIs/Controllers/HomeController.cs

Lines changed: 20 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -20,14 +20,17 @@ public class HomeController : Controller
2020
readonly ITokenAcquisition tokenAcquisition;
2121
private readonly IGraphApiOperations graphApiOperations;
2222
private readonly IArmOperations armOperations;
23+
private readonly IArmOperationsWithImplicitAuth armOperationsWithImplicitAuth;
2324

2425
public HomeController(ITokenAcquisition tokenAcquisition,
2526
IGraphApiOperations graphApiOperations,
26-
IArmOperations armOperations)
27+
IArmOperations armOperations,
28+
IArmOperationsWithImplicitAuth armOperationsWithImplicitAuth)
2729
{
2830
this.tokenAcquisition = tokenAcquisition;
2931
this.graphApiOperations = graphApiOperations;
3032
this.armOperations = armOperations;
33+
this.armOperationsWithImplicitAuth = armOperationsWithImplicitAuth;
3134
}
3235

3336
public IActionResult Index()
@@ -67,9 +70,23 @@ public async Task<IActionResult> Tenants()
6770

6871
return View();
6972
}
70-
71-
[AuthorizeForScopes(Scopes = new[] { "https://storage.azure.com/user_impersonation" })]
7273

74+
// Requires that the app has added the Azure Service Management / user_impersonation scope, and that
75+
// the admin tenant does not require admin consent for ARM.
76+
[AuthorizeForScopes(Scopes = new[] { "https://management.core.windows.net/user_impersonation" })]
77+
public async Task<IActionResult> TenantsWithImplicitAuth()
78+
{
79+
var tenantIds = await armOperationsWithImplicitAuth.EnumerateTenantsIds();
80+
/*
81+
var tenantsIdsAndNames = await graphApiOperations.EnumerateTenantsIdAndNameAccessibleByUser(tenantIds,
82+
async tenantId => { return await tokenAcquisition.GetAccessTokenForUserAsync(new string[] { "Directory.Read.All" }, tenantId); });
83+
*/
84+
ViewData["tenants"] = tenantIds;
85+
86+
return View(nameof(Tenants));
87+
}
88+
89+
[AuthorizeForScopes(Scopes = new[] { "https://storage.azure.com/user_impersonation" })]
7390
public async Task<IActionResult> Blob()
7491
{
7592
string message = "Blob failed to create";

3-WebApp-multi-APIs/README.md

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -191,6 +191,39 @@ insert
191191
<li><a asp-area="" asp-controller="Home" asp-action="Blob">Blob</a></li>
192192
```
193193

194+
### Using implicit authentication (ArmApiOperationsServiceWithImplicitAuth)
195+
When calling an API, instead of explicitly giving the token in each call you can use a delegating handler on your HTTP client to automatically inject the access token.
196+
197+
In `Startup.cs` after `services.AddHttpClient<IArmOperations, ArmApiOperationService>();` add `.services.AddHttpClient<IArmOperationsWithImplicitAuth, ArmApiOperationServiceWithImplicitAuth>()...`:
198+
199+
```CSharp
200+
public void ConfigureServices(IServiceCollection services)
201+
{
202+
. . .
203+
services.AddMicrosoftIdentityWebAppAuthentication(Configuration)
204+
.EnableTokenAcquisitionToCallDownstreamApi( new string[] { Constants.ScopeUserRead })
205+
.AddInMemoryTokenCaches();
206+
services.AddHttpClient<IArmOperations, ArmApiOperationService>();
207+
services.AddHttpClient<IArmOperationsWithImplicitAuth, ArmApiOperationServiceWithImplicitAuth>()
208+
.AddMicrosoftIdentityUserAuthenticationHandler(
209+
"arm",
210+
options => options.Scopes = $"{ArmApiOperationService.ArmResource}user_impersonation");
211+
```
212+
213+
In `HomeController.cs` add `TenantsWithImplicitAuth`:
214+
```CSharp
215+
// Requires that the app has added the Azure Service Management / user_impersonation scope, and that
216+
// the admin tenant does not require admin consent for ARM.
217+
[AuthorizeForScopes(Scopes = new[] { "https://management.core.windows.net/user_impersonation" })]
218+
public async Task<IActionResult> TenantsWithImplicitAuth()
219+
{
220+
var tenantIds = await armOperationsWithImplicitAuth.EnumerateTenantsIds();
221+
ViewData["tenants"] = tenantIds;
222+
223+
return View(nameof(Tenants));
224+
}
225+
```
226+
194227
## Troubleshooting
195228

196229
To access Azure Resource Management (ARM), you'll need a work or school account (AAD account) and an Azure subscription. If your Azure subscription is for a Microsoft personal account, just create a new user in your directory, and use this user to run the sample
Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
using Microsoft.Extensions.Options;
2+
3+
using Newtonsoft.Json;
4+
5+
using System.Collections.Generic;
6+
using System.Linq;
7+
using System.Net.Http;
8+
using System.Threading.Tasks;
9+
10+
using WebApp_OpenIDConnect_DotNet.Services.GraphOperations;
11+
12+
namespace WebApp_OpenIDConnect_DotNet.Services.Arm
13+
{
14+
public class ArmApiOperationServiceWithImplicitAuth : IArmOperationsWithImplicitAuth
15+
{
16+
private readonly HttpClient httpClient;
17+
18+
public ArmApiOperationServiceWithImplicitAuth(HttpClient httpClient)
19+
{
20+
this.httpClient = httpClient;
21+
}
22+
23+
/// <summary>
24+
/// Enumerates the list of Tenant IDs. Token for the user or app needs to be configured on the HTTP client.
25+
/// </summary>
26+
/// <returns></returns>
27+
public async Task<IEnumerable<string>> EnumerateTenantsIds()
28+
{
29+
var httpResult = await httpClient.GetAsync(ArmListTenantUrl);
30+
string json = await httpResult.Content.ReadAsStringAsync();
31+
ArmResult armTenants = JsonConvert.DeserializeObject<ArmResult>(json);
32+
return armTenants.value.Select(t => t.tenantId);
33+
}
34+
35+
protected string ArmListTenantUrl { get; } = "https://management.azure.com/tenants?api-version=2016-06-01";
36+
}
37+
}
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
using System.Collections.Generic;
2+
using System.Threading.Tasks;
3+
4+
namespace WebApp_OpenIDConnect_DotNet.Services.Arm
5+
{
6+
public interface IArmOperationsWithImplicitAuth
7+
{
8+
Task<IEnumerable<string>> EnumerateTenantsIds();
9+
}
10+
}

3-WebApp-multi-APIs/Startup.cs

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,10 @@ public void ConfigureServices(IServiceCollection services)
4646
// Add APIs
4747
services.AddGraphService(Configuration);
4848
services.AddHttpClient<IArmOperations, ArmApiOperationService>();
49+
services.AddHttpClient<IArmOperationsWithImplicitAuth, ArmApiOperationServiceWithImplicitAuth>()
50+
.AddMicrosoftIdentityUserAuthenticationHandler(
51+
"arm",
52+
options => options.Scopes = $"{ArmApiOperationService.ArmResource}user_impersonation");
4953

5054
services.AddControllersWithViews(options =>
5155
{

3-WebApp-multi-APIs/Views/Shared/_Layout.cshtml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,7 @@
3333
<li><a asp-area="" asp-controller="Home" asp-action="Index">Home</a></li>
3434
<li><a asp-area="" asp-controller="Home" asp-action="Profile">Profile</a></li>
3535
<li><a asp-area="" asp-controller="Home" asp-action="Tenants">Tenants</a></li>
36+
<li><a asp-area="" asp-controller="Home" asp-action="TenantsWithImplicitAuth">Tenants (with implicit auth)</a></li>
3637
<li><a asp-area="" asp-controller="Home" asp-action="Blob">Blob</a></li>
3738
</ul>
3839
<partial name="_LoginPartial" />

0 commit comments

Comments
 (0)