Skip to content

Commit 9fc46d0

Browse files
authored
Merge pull request #99 from Azure-Samples/updateDocAfterKCAFix
Update README.md now that KCA works for MSA
2 parents 4f9a9bc + 625a0e2 commit 9fc46d0

File tree

5 files changed

+92
-120
lines changed

5 files changed

+92
-120
lines changed

2. Web API now calls Microsoft Graph/README-incremental-instructions.md

Lines changed: 54 additions & 65 deletions
Original file line numberDiff line numberDiff line change
@@ -7,16 +7,15 @@ client: .NET Desktop (WPF)
77
service: ASP.NET Core Web API, Microsoft Graph
88
endpoint: AAD v2.0
99
---
10-
# ASP.NET Core 2.1 Web API calling Microsoft Graph, itself called from a WPF application using Azure AD V2
10+
# ASP.NET Core Web API calling Microsoft Graph, itself called from a WPF application using Microsoft identity platform
1111

12-
![Build badge](https://identitydivision.visualstudio.com/_apis/public/build/definitions/a7934fdd-dcde-4492-a406-7fad6ac00e17/497/badge)
12+
[![Build status](https://identitydivision.visualstudio.com/IDDP/_apis/build/status/AAD%20Samples/.NET%20client%20samples/active-directory-dotnet-native-aspnetcore-v2)](https://identitydivision.visualstudio.com/IDDP/_build/latest?definitionId=516)
1313

1414
> The sample in this folder is part of a multi-phase tutorial. This folder is about the second phase named **Web API now calls Microsoft Graph**.
1515
> The first phase is available from [1. Desktop app calls Web API](../1.%20Desktop%20app%20calls%20Web%20API).
1616
>
1717
> This article [README-incremental-instructions.md](README-incremental-instructions.md) builds on top of the README.md of the first part. If you want to see the full instructions on how to configure the sample, see [README.md](README.md)
1818
19-
> At that time, the Azure AD v2.0 endpoint does not yet completely support the on-behalf-of flow for users signing-in with a Microsoft Personal account. Limitations are called out in the [Current limitations](#Current-limitations) section
2019

2120
## About this sample
2221

@@ -29,7 +28,7 @@ endpoint: AAD v2.0
2928
- [How to run this sample](#How-to-run-this-sample)
3029
- [Step 1: Clone or download this repository](#step-1--clone-or-download-this-repository)
3130
- [Step 2: Register the sample with your Azure Active Directory tenant](#step-2--register-the-sample-with-your-azure-active-directory-tenant)
32-
- [Step 3: Configure the sample to use your Azure AD tenant](#step-3--configure-the-sample-to-use-your-azure-ad-tenant)
31+
- [Step 3: Configure the sample code to use your Azure AD tenant](#step-3--configure-the-sample-code-to-use-your-azure-ad-tenant)
3332
- [Step 4: Run the sample](#step-4-run-the-sample)
3433
- [Troubleshooting](#Troubleshooting)
3534
- [Current limitations](#Current-limitations)
@@ -76,6 +75,8 @@ There are two projects in this sample. Each needs to be separately registered in
7675
- **automatically** creates the Azure AD applications and related objects (passwords, permissions, dependencies) for you
7776
- modify the Visual Studio projects' configuration files.
7877

78+
> Note however that the automation will not, at this point, allow you to sign-in with a personal Microsoft account. If you want to allow sign in with personal Microsoft accounts, use the manual instructions.
79+
7980
#### Using scripts
8081

8182
If you want to use this automation:
@@ -95,14 +96,13 @@ If you want to use this automation:
9596
- in the manifest, search for **"accessTokenAcceptedVersion"**, and replace **null** by **2**. This property lets Azure AD know that the Web API accepts v2.0 tokens
9697
- search for **signInAudience** and make sure it's set to **AzureADandPersonalMicrosoftAccount**
9798
- Select **Save**
98-
1. In the **Authentication** page for the *TodoListService-v2* application, check the `urn:ietf:wg:oauth:2.0:oob` reply URI so that the client can propose incremental consent to the user for the Web API when needed.
99-
1. In tthe application registration page for the *TodoListClient-v2* application, select the **Manifest** section:
99+
1. In the application registration page for the *TodoListClient-v2* application, select the **Manifest** section:
100100
- search for **signInAudience** and make sure it's set to **AzureADandPersonalMicrosoftAccount**
101101
- Select **Save**
102102

103103
> Tip: Get directly to the app registration portal page for a give app, you can navigate to the links provided in the [AppCreationScripts\createdApps.html](AppCreationScripts\createdApps.html). This file is generated by the scripts during the app registration and configuration.
104104
105-
1. Open the Visual Studio solution and click start
105+
5. Open the Visual Studio solution and click start
106106

107107
If you don't want to use this automation, follow the steps below
108108

@@ -112,7 +112,7 @@ These instructions only show the differences with the first part.
112112

113113
#### Register the service app (TodoListService)
114114

115-
1. In **App registrations (Preview)** page, find the *TodoListService-2* app
115+
1. In **App registrations** page, find the *TodoListService-2* app
116116
1. From the **Certificates & secrets** page, in the **Client secrets** section, choose **New client secret**:
117117
- Type a key description (of instance `app secret`),
118118
- Select a key duration of either **In 1 year**, **In 2 years**, or **Never Expires**.
@@ -123,32 +123,34 @@ These instructions only show the differences with the first part.
123123
- Click the **Add a permission** button and then,
124124
- Ensure that the **Microsoft APIs** tab is selected
125125
- In the *Commonly used Microsoft APIs* section, click on **Microsoft Graph**
126-
- In the **Delegated permissions** section, ensure that the right permissions are checked: **User.Read**. Use the search box if necessary.
126+
- In the **Delegated permissions** section, ensure that the right permissions are checked: **User.Read** and **offline_access**. Use the search box if necessary.
127127
- Select the **Add permissions** button
128-
- [Optional] if you are a tenant admin, and agree to grant the admin consent to the web api, select **Grant admin consent for {your tenant domain}**.
129-
1. [Otherwise] If you have not granted admin consent to the Web API in the previous optional step, select **Authentication** in the list of pages and there:
130-
- Check the `urn:ietf:wg:oauth:2.0:oob` Redirect URI checkbox. This is so that the client can propose incremental consent to the user for the downstream web apis used by our *TodoListService-v2* Web API.
131-
- Select **Save**
128+
- [Optional] if you are a tenant admin, and agree to grant the admin consent to the web api, select **Grant admin consent for {your tenant domain}**. If you don't do
129+
it, users will be presented a consent screen enabling them to consent to using the web api. The consent screen will also mention the permissions required by the web api itself.
132130
1. [Optional] Select the **Manifest** section and:
133131
- in the manifest, search for **"accessTokenAcceptedVersion"**, and see that its value is **2**. This property lets Azure AD know that the Web API accepts v2.0 tokens
134132
- Select **Save**
135133

136-
> Important: it's up to the Web API to decide which version of token (v1.0 or v2.0) it accepts. Then when clients request a token for your Web API using the v2.0 endpoint, they'll get a token which version is accepted by the Web API. The code validating the tokens in this sample was written to accept both versions.
134+
> Important: it's up to the Web API to decide which version of token (v1.0 or v2.0) it accepts. Then when clients request a token for your Web API using the Microsoft identity platform endpoint, they'll get a token which version is accepted by the Web API. The code validating the tokens in this sample was written to accept both versions.
137135
138136
#### Register the client app (TodoListClient)
139137

140138
Nothing more to do more here. All was done in the first part
141139

142140
### Step 3: Configure the sample to use your Azure AD tenant
143141

144-
By default the sample is configured to enable users to sign in with any work and school accounts (AAD) accounts.
145-
This constrain is ensured by `ida:Tenant` in `TodoListClient\App.Config` having the value `organizations`.
142+
By default the sample is configured to enable users to sign in with any work and school accounts (AAD) or personal Microsoft accounts.
143+
This constraint is ensured by `ida:Tenant` in `TodoListClient\App.Config` having the value `common`.
146144

147145
#### Configure the TodoListService C# project
148146

149147
1. Open the solution in Visual Studio.
150148
1. In the *TodoListService-v2* project, open the `appsettings.json` file.
151149
1. Find the `ClientSecret` property and replace the existing value with the key you saved during the creation of the `TodoListService-v2` app, in the Azure portal.
150+
> Note
151+
> In chapter 1, the protected Web API did not call any downstrream API. In this chapter it does, and thus
152+
> it needs to acquire s token, and becomes a confidential client. Therefore it needs to prove its identity to
153+
> Azure AD through a client secret (or a certificate)
152154
153155
#### Configure the TodoListClient C# project
154156

@@ -158,12 +160,9 @@ Nothing more to do more here. All was done in the first part
158160

159161
Clean the solution, rebuild the solution, and run it
160162

161-
### Current limitations
163+
### Alternative architecture
162164

163-
The on-behalf-of flow works for Microsoft Personal accounts, but the consent is not yet rolled-up in the client for the user to consent to the Web API calling the downstream API (here Microsoft Graph). To make this work, the suggestion is:
164-
165-
- either to use the same client ID in the Client and the Service. This way the consent for the service will appear in the client.
166-
- or to provide a protected page on the Web API (which therefore also becomes a Web app) so that the user can have an interaction
165+
This part of the sample uses different client ID for the client and the service and uses the on-behalf-of flow. If you are the author of both the client and the service, you might alternatively want to use the same client ID in the Client and the Service. This approach is described in the third part of the tutorial [3.-Web-api-call-Microsoft-graph-for-personal-accounts](../3.-Web-api-call-Microsoft-graph-for-personal-accounts)
167166

168167
## How was the code created
169168

@@ -196,36 +195,32 @@ Update `Startup.cs` file:
196195

197196
```csharp
198197
services.AddProtectedWebApi(Configuration)
199-
.AddProtectedApiCallsWebApis(Configuration, new string[] { "user.read" })
198+
.AddProtectedApiCallsWebApis(Configuration)
200199
.AddInMemoryTokenCaches();
201200
```
202201

203202
`AddProtectedWebApi` does the following:
204203
- add the **Jwt**BearerAuthenticationScheme (Note the replacement of BearerAuthenticationScheme by **Jwt**BearerAuthenticationScheme)
205-
- set the authority to be the Microsoft identity platform v2.0 identity
204+
- set the authority to be the Microsoft identity platform identity
206205
- sets the audiences to validate
207206
- register an issuer validator that accepts issuers to be in the Microsoft identity platform clouds.
208-
-
209207

210-
Here is an idea of the code:
208+
Here is an idea of the code (in the `WebApiServiceCollectionExtensions.cs` file)
211209

212210
```csharp
213211
services.AddAuthentication(AzureADDefaults.JwtBearerAuthenticationScheme)
214212
.AddAzureADBearer(options => configuration.Bind("AzureAd", options));
215213

216-
services.AddSession();
217-
218214
// Added
219215
services.Configure<JwtBearerOptions>(AzureADDefaults.JwtBearerAuthenticationScheme, options =>
220216
{
221-
// This is an Azure AD v2.0 Web API
217+
// This is an Microsoft identity platform Web API
222218
options.Authority += "/v2.0";
223219

224220
// The valid audiences are both the Client ID (options.Audience) and api://{ClientID}
225221
options.TokenValidationParameters.ValidAudiences = new string[]
226222
{
227-
options.Audience,
228-
$"api://{options.Audience}"
223+
options.Audience, $"api://{options.Audience}"
229224
};
230225

231226
// Instead of using the default validation (validating against a single tenant
@@ -236,7 +231,7 @@ Update `Startup.cs` file:
236231
});
237232
```
238233

239-
The services that are added are:
234+
The .NET Core "services" that are added are:
240235

241236
- a token acquisition service leveraging MSAL.NET
242237
- an in memory token cache
@@ -252,29 +247,22 @@ Update `Startup.cs` file:
252247
// so that it can be used from the controllers.
253248
options.Events = new JwtBearerEvents();
254249

255-
// Subscribing to OnTokenValidated to add the token to the cache using the OnBehalfOf flow
250+
// Subscribing to OnTokenValidated to verify that the token has at least the Scope, scp or roles claims
256251
options.Events.OnTokenValidated = async context =>
257252
{
258-
var tokenAcquisition = context.HttpContext.RequestServices.GetRequiredService<ITokenAcquisition>();
259-
var scopes = new string[] { "user.read" };
260-
context.Success();
253+
// This check is required to ensure that the Web API only accepts tokens from tenants where it has been consented and provisioned.
254+
if (!context.Principal.Claims.Any(x => x.Type == ClaimConstants.Scope)
255+
&& !context.Principal.Claims.Any(y => y.Type == ClaimConstants.Scp)
256+
&& !context.Principal.Claims.Any(y => y.Type == ClaimConstants.Roles))
257+
{
258+
throw new UnauthorizedAccessException("Neither scope or roles claim was found in the bearer token.");
259+
}
261260

262-
// Adds the token to the cache, and also handles the incremental consent and claim challenges
263-
tokenAcquisition.AddAccountToCacheFromJwt(context, scopes);
264-
await Task.FromResult(0);
261+
await Task.FromResult(0);
265262
};
266263
});
267264
```
268265

269-
- At the beginning of the `Configure` method, insert `app.UseSession()`. This code ensures that the session exists for the session-based token cache to work properly.
270-
271-
```CSharp
272-
public void Configure(IApplicationBuilder app, IHostingEnvironment env)
273-
{
274-
app.UseSession();
275-
...
276-
```
277-
278266
### Modify the TodoListController.cs file to add information to the todo item about its owner
279267

280268
In the `TodoListController.cs` file, the Post() action was modified
@@ -312,7 +300,7 @@ This method is the following. It:
312300
}
313301
catch (MsalUiRequiredException ex)
314302
{
315-
tokenAcquisition.ReplyForbiddenWithWwwAuthenticateHeader(HttpContext, scopes, ex);
303+
tokenAcquisition.ReplyForbiddenWithWwwAuthenticateHeader(scopes, ex);
316304
return string.Empty;
317305
}
318306
}
@@ -332,23 +320,21 @@ This sample uses the `ReplyForbiddenWithWwwAuthenticateHeader` method of the `To
332320
- the scopes to request
333321
- the claims (for conditional access, MFA etc ...)
334322

335-
The code for this method is available in [Microsoft.Identity.Web\Client\TokenAcquisition.cs L394-L427](https://github.com/Azure-Samples/active-directory-dotnet-native-aspnetcore-v2/blob/2e5d34aedabbf20b583580a661433ad4b00bb035/Microsoft.Identity.Web/Client/TokenAcquisition.cs#L394-L427)
323+
The code for this method is available in [Microsoft.Identity.Web\Client\TokenAcquisition.cs L457-L493](https://github.com/Azure-Samples/active-directory-dotnet-native-aspnetcore-v2/blob/4f9a9bc7f08e79f1a3e908cb513c59f1976470da/Microsoft.Identity.Web/TokenAcquisition.cs#L457-L493)
336324
337325
#### On the client side
338326

339327
On the client side, when it calls the Web API and receives a 403 with a www-Authenticate header, the client will call the `HandleChallengeFromWebApi` method, which will
340328

341329
- extract from the www-Authenticate header
342-
- the client ID
343-
- the scopes
344-
- the claims
345-
- Create a new `PublicClientApplication` (as the client is here a desktop app), and call `AcquireTokenAsync` method to ask the user to consent.
330+
- the consent URi
331+
- Navigate to the consent URI provided by the Web API.
346332

347-
The code for `HandleChallengeFromWebApi` method is available from [TodoListClient\MainWindow.xaml.cs L180-L206](https://github.com/Azure-Samples/active-directory-dotnet-native-aspnetcore-v2/blob/9be6d8b3705ada352caa9a47be0a5add8389b6f6/2.%20Web%20API%20now%20calls%20Microsoft%20Graph/TodoListClient/MainWindow.xaml.cs#L180-L206)
333+
The code for `HandleChallengeFromWebApi` method is available from [TodoListClient\MainWindow.xaml.cs L162-197](https://github.com/Azure-Samples/active-directory-dotnet-native-aspnetcore-v2/blob/4f9a9bc7f08e79f1a3e908cb513c59f1976470da/2.%20Web%20API%20now%20calls%20Microsoft%20Graph/TodoListClient/MainWindow.xaml.cs#L162-L197)
348334
349335
## How to deploy this sample to Azure
350336

351-
See section [How to deploy this sample to Azure](../1.%20Desktop%20app%20calls%20Web%20API/README.md#How-to-deploy-this-sample-to-Azure) in the first part of this tutorial, as the deployment is exactly the same.
337+
See section [How to deploy this sample to Azure](../1.%20Desktop%20app%20calls%20Web%20API/README.md#How-to-deploy-this-sample-to-Azure) in the first part of this tutorial, as the deployment is the same.
352338

353339
## Community Help and Support
354340

@@ -370,19 +356,22 @@ This project has adopted the [Microsoft Open Source Code of Conduct](https://ope
370356

371357
For more information, visit the following links:
372358

373-
- To lean more about the application registration, visit:
359+
- to learn more about the scenario, see [Scenario: Web app that calls web APIs](https://docs.microsoft.com/en-us/azure/active-directory/develop/scenario-web-app-call-api-overview)
360+
361+
- to learn more about Microsoft.Identity.Web, see [Microsoft.Identity.Web/README.md](../Microsoft.Identity.Web/README.md)
362+
363+
- To learn more about the application registration, visit:
374364

375-
- [Quickstart: Register an application with the Microsoft identity platform (Preview)](https://docs.microsoft.com/en-us/azure/active-directory/develop/quickstart-register-app)
376-
- [Quickstart: Configure a client application to access web APIs (Preview)](https://docs.microsoft.com/en-us/azure/active-directory/develop/quickstart-configure-app-access-web-apis)
377-
- [Quickstart: Quickstart: Configure an application to expose web APIs (Preview)](https://docs.microsoft.com/en-us/azure/active-directory/develop/quickstart-configure-app-expose-web-apis)
365+
- [Quickstart: Register an application with the Microsoft identity platform](https://docs.microsoft.com/en-us/azure/active-directory/develop/quickstart-register-app)
366+
- [Quickstart: Configure a client application to access web APIs](https://docs.microsoft.com/en-us/azure/active-directory/develop/quickstart-configure-app-access-web-apis)
367+
- [Quickstart: Quickstart: Configure an application to expose web APIs](https://docs.microsoft.com/en-us/azure/active-directory/develop/quickstart-configure-app-expose-web-apis)
378368
379369
- To learn more about the code, visit [Conceptual documentation for MSAL.NET](https://github.com/AzureAD/microsoft-authentication-library-for-dotnet/wiki#conceptual-documentation) and in particular:
380-
- [Acquiring tokens with authorization codes on web apps](https://github.com/AzureAD/microsoft-authentication-library-for-dotnet/wiki/Acquiring-tokens-with-authorization-codes-on-web-apps)
381-
- [Customizing Token cache serialization](https://github.com/AzureAD/microsoft-authentication-library-for-dotnet/wiki/token-cache-serialization)
370+
- [Acquiring tokens with the on-behalf-of flow](https://github.com/AzureAD/microsoft-authentication-library-for-dotnet/wiki/on-behalf-of)
382371
383-
- Articles about the Azure AD V2 endpoint [http://aka.ms/aaddevv2](http://aka.ms/aaddevv2), with a focus on:
384-
- [Azure Active Directory v2.0 and OAuth 2.0 On-Behalf-Of flow](https://docs.microsoft.com/en-us/azure/active-directory/develop/active-directory-v2-protocols-oauth-on-behalf-of)
372+
- Articles about the Microsoft identity platform endpoint [http://aka.ms/aaddevv2](http://aka.ms/aaddevv2), with a focus on:
373+
- [identity platform and OAuth 2.0 On-Behalf-Of flow](https://docs.microsoft.com/en-us/azure/active-directory/develop/active-directory-v2-protocols-oauth-on-behalf-of)
385374
386-
- [Introduction to Identity on ASP.NET Core](https://docs.microsoft.com/en-us/aspnet/core/security/authentication/identity?view=aspnetcore-2.1&tabs=visual-studio%2Caspnetcore2x)
375+
- [Introduction to Identity on ASP.NET Core](https://docs.microsoft.com/en-us/aspnet/core/security/authentication/identity?view=aspnetcore-2.2&tabs=visual-studio%2Caspnetcore2x)
387376
- [AuthenticationBuilder](https://docs.microsoft.com/en-us/dotnet/api/microsoft.aspnetcore.authentication.authenticationbuilder?view=aspnetcore-2.0)
388-
- [Azure Active Directory with ASP.NET Core](https://docs.microsoft.com/en-us/aspnet/core/security/authentication/azure-active-directory/?view=aspnetcore-2.1)
377+
- [Azure Active Directory with ASP.NET Core](https://docs.microsoft.com/en-us/aspnet/core/security/authentication/azure-active-directory/?view=aspnetcore-2.2)

0 commit comments

Comments
 (0)