Skip to content

Commit c50df9a

Browse files
committed
Updates as per Doğan's comments
1 parent 19d58dd commit c50df9a

File tree

5 files changed

+67
-29
lines changed

5 files changed

+67
-29
lines changed

4-WebApp-your-API/4-3-AnyOrg/Readme.md

Lines changed: 46 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -66,10 +66,6 @@ The Web API:
6666
### Pre-requisites
6767

6868
- [Visual Studio 2019](https://aka.ms/vsdownload) or just the [.NET Core SDK](https://www.microsoft.com/net/learn/get-started)
69-
- An Internet connection
70-
- A Windows machine (necessary if you want to run the app on Windows)
71-
- An OS X machine (necessary if you want to run the app on Mac)
72-
- A Linux machine (necessary if you want to run the app on Linux)
7369
- An Azure Active Directory (Azure AD) tenant. For more information on how to get an Azure AD tenant, see [How to get an Azure AD tenant](https://azure.microsoft.com/documentation/articles/active-directory-howto-tenant/)
7470
- A user account in your Azure AD tenant. This sample will not work with a Microsoft account (formerly Windows Live account). Therefore, if you signed in to the [Azure portal](https://portal.azure.com) with a Microsoft account and have never created a user account in your directory before, you need to do that now.
7571

@@ -228,6 +224,43 @@ Open the project in your IDE (like Visual Studio) to configure the code.
228224
1. Find the app key `AdminConsentRedirectApi` and replace the existing value with the Redirect URI for WebApi-MultiTenant-v2 app. For example, `https://localhost:44351/api/Home` .
229225
### Step 3: Run the sample
230226

227+
#### Using a command line interface such as VS Code integrated terminal, follow the steps below:
228+
229+
##### Step 1. Install .NET Core dependencies
230+
231+
```console
232+
cd TodoListAPI
233+
dotnet restore
234+
```
235+
Then:
236+
In a separate console window, execute the following commands
237+
238+
```console
239+
cd ../
240+
cd ToDoListClient
241+
dotnet restore
242+
```
243+
244+
##### Step 2. Trust development certificates
245+
246+
```console
247+
dotnet dev-certs https --clean
248+
dotnet dev-certs https --trust
249+
```
250+
251+
Learn more about [HTTPS in .NET Core](https://docs.microsoft.com/aspnet/core/security/enforcing-ssl).
252+
253+
##### Step 3. Run the applications
254+
255+
In both the windows execute the below command:
256+
257+
```console
258+
dotnet run
259+
```
260+
Open your browser and navigate to `https://localhost:44321`.
261+
262+
#### Run the sample using Visual Studio:
263+
231264
Clean the solution, rebuild the solution, and run it. You might want to go into the solution properties and set both projects as startup projects, with the service project starting first.
232265

233266
When you start the Web API from Visual Studio, depending on the browser you use, you'll get:
@@ -237,33 +270,25 @@ When you start the Web API from Visual Studio, depending on the browser you use,
237270

238271
This behavior is expected as you are not authenticated. The client application will be authenticated, so it will be able to access the Web API.
239272

273+
## Explore the sample
274+
240275
Explore the sample by signing in into the TodoList client, adding items to the To Do list and assigning them to users. If you stop the application without signing out, the next time you run the application, you won't be prompted to sign in again.
241276

242-
To make the sample work for guest tenant, you need to provision service principal for that tenant. It can be done by clicking on `Admin Consent` link that is present on home page of Web App. The link provides consent screen that is used to create service principal by admin of the guest tenant.
277+
For Home Tenant, admin needs to sign-in first as `User.Read.All` permission requires admin consent for the organization.
278+
279+
For guest tenant, you need to provision service principal for that tenant. It can be done by clicking on `Admin Consent` link that is present on home page of Web App. The link provides consent screen that is used to create service principal by admin of the guest tenant.
243280

244281
> NOTE: Remember, the To-Do list is stored in memory in this `TodoListService` app. Each time you run the projects, your To-Do list will get emptied.
245282
246283
> Did the sample not work for you as expected? Did you encounter issues trying this sample? Then please reach out to us using the [GitHub Issues](../../../../issues) page.
247284
248285
## About the code
249286

250-
### Consenting to Applications with Distributed Topology
251-
252-
Consider the application suite in this chapter: **TodoListAPI** and **TodoListSPA**. From one perspective, they are two different applications (two different projects), each represented with their own **app registration** on Azure AD, but from another perspective, they really constitute one application together i.e. a todo list application. In practice, an application can have a many such components: one component for the front-end, another for a REST API, another for a database and etc. While these components should have their own separate representation on Azure AD, they should also somehow know one another.
253-
254-
From the perspective of **multi-tenancy**, the main challenge with such topologies is with providing admin-consent. This is due to the fact that some of their components, such as a web API or a background micro-service, do not have a front-end, and as such, has no user-interaction capability. The solution for this is to allow the user (in this case, an admin-user) to consent to web API at the same time they consent to the front-end application i.e. give a **combined consent**. In **Chapter 1**, we have seen that the `/.default` scope can be used to this effect, allowing you to consent to many different scopes at one step. However, unlike **Chapter 1**, our application suite here also has a back-end/web API component. But how could the web API know that the consent comes from a recognized front-end application, as opposed to some foreign application? The answer is to use the **KnownClientApplications** feature.
255-
256-
> #### KnownClientApplications
257-
>
258-
> **KnownClientApplications** is an attribute in **application manifest**. It is used for bundling consent if you have a solution that contains two (or more) parts: a client app and a custom web API. If you enter the `appID` (clientID) of the client app into this array, the user will only have to consent only once to the client app. Azure AD will know that consenting to the client means implicitly consenting to the web API. It will automatically provision service principals for both the client and web API at the same time. Both the client and the web API app must be registered in the same tenant.
259-
260-
If you remember the last step of the registration for the client app (TodoListSPA), you were instructed to find the `KnownClientApplications` in application manifest, and add the Application (client) ID of the `TodoListSPA` application `KnownClient witApplications: ["your-client-id-for-TodoListSPA"]`. Once you do that, your web API will be able to correctly identify your front-end and the combined consent will be successfully carried out.
261-
262287
### Provisioning your Multi-tenant Apps in another Azure AD Tenant
263288

264-
Often the user-based consent will be disabled in an Azure AD tenant or your application will be requesting permissions that requires a tenant-admin consent. In these scenarios, your application will need to utilize the `/adminconsent` endpoint to provision both the **TodoListSPA** and the **TodoListAPI** before the users from that tenant are able to sign-in to your app.
289+
Often the user-based consent will be disabled in an Azure AD tenant or your application will be requesting permissions that requires a tenant-admin consent. In these scenarios, your application will need to utilize the `/adminconsent` endpoint to provision both the **ToDoListClient** and the **TodoListAPI** before the users from that tenant are able to sign-in to your app.
265290

266-
When provisioning, you have to take care of the dependency in the topology where the **TodoListSPA** is dependent on **TodoListAPI**. So in such a case, you would provision the **TodoListAPI** before the **TodoListSPA**.
291+
When provisioning, you have to take care of the dependency in the topology where the **ToDoListClient** is dependent on **TodoListAPI**. So in such a case, you would provision the **TodoListAPI** before the **ToDoListClient**.
267292

268293
### Code for the Web App (TodoListClient)
269294

@@ -330,10 +355,9 @@ private void HandleChallengeFromWebApi(HttpResponseMessage response)
330355
var uri = new Uri(consentUri);
331356

332357
var queryString = System.Web.HttpUtility.ParseQueryString(uri.Query);
333-
queryString.Set("client_id", _ClientId);
334-
queryString.Set("redirect_uri", _RedirectUri);
335-
queryString.Set("scope", _TodoListScope);
358+
queryString.Set("redirect_uri", _ApiRedirectUri);
336359
queryString.Add("prompt", "consent");
360+
queryString.Add("state", _RedirectUri);
337361

338362
var uriBuilder = new UriBuilder(uri);
339363
uriBuilder.Query = queryString.ToString();

4-WebApp-your-API/4-3-AnyOrg/ToDoListClient/Controllers/HomeController.cs

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,13 @@ public HomeController(IConfiguration configuration)
2424
_RedirectUri = configuration["RedirectUri"];
2525
_ApiClientId = configuration["TodoList:TodoListAppId"];
2626
_ApiRedirectUri = configuration["TodoList:AdminConsentRedirectApi"];
27+
if (!string.IsNullOrEmpty(_RedirectUri))
28+
{
29+
if (!_RedirectUri.EndsWith("/"))
30+
{
31+
_RedirectUri = _RedirectUri+"/";
32+
}
33+
}
2734
}
2835

2936
public IActionResult Index()

4-WebApp-your-API/4-3-AnyOrg/ToDoListClient/Services/ToDoListService.cs

Lines changed: 11 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,7 @@ public class ToDoListService : IToDoListService
3131
private readonly string _TodoListBaseAddress = string.Empty;
3232
private readonly string _ClientId = string.Empty;
3333
private readonly string _RedirectUri = string.Empty;
34+
private readonly string _ApiRedirectUri = string.Empty;
3435
private readonly ITokenAcquisition _tokenAcquisition;
3536

3637
public ToDoListService(ITokenAcquisition tokenAcquisition, HttpClient httpClient, IConfiguration configuration)
@@ -41,6 +42,14 @@ public ToDoListService(ITokenAcquisition tokenAcquisition, HttpClient httpClient
4142
_TodoListBaseAddress = configuration["TodoList:TodoListBaseAddress"];
4243
_ClientId = configuration["AzureAd:ClientId"];
4344
_RedirectUri = configuration["RedirectUri"];
45+
_ApiRedirectUri = configuration["TodoList:AdminConsentRedirectApi"];
46+
if (!string.IsNullOrEmpty(_TodoListBaseAddress))
47+
{
48+
if (!_TodoListBaseAddress.EndsWith("/"))
49+
{
50+
_TodoListBaseAddress = _TodoListBaseAddress+"/";
51+
}
52+
}
4453
}
4554
public async Task<ToDoItem> AddAsync(ToDoItem todo)
4655
{
@@ -165,11 +174,9 @@ private void HandleChallengeFromWebApi(HttpResponseMessage response)
165174

166175
//Set values of query string parameters
167176
var queryString = System.Web.HttpUtility.ParseQueryString(uri.Query);
168-
queryString.Set("client_id", _ClientId);
169-
queryString.Set("redirect_uri", _RedirectUri);
170-
queryString.Set("scope", _TodoListScope);
177+
queryString.Set("redirect_uri", _ApiRedirectUri);
171178
queryString.Add("prompt", "consent");
172-
179+
queryString.Add("state", _RedirectUri);
173180
//Update values in consent Uri
174181
var uriBuilder = new UriBuilder(uri);
175182
uriBuilder.Query = queryString.ToString();

4-WebApp-your-API/4-3-AnyOrg/ToDoListClient/appsettings.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
"AzureAd": {
33
"Instance": "https://login.microsoftonline.com/",
44
"Domain": "[Enter the domain of your tenant, e.g. contoso.onmicrosoft.com]",
5-
"TenantId": "[Enter 'common', or 'organizations' or the Tenant Id (Obtained from the Azure portal. Select 'Endpoints' from the 'App registrations' blade and use the GUID in any of the URLs), e.g. da41245a5-11b3-996c-00a8-4d99re19f292]",
5+
"TenantId": "common",
66
"ClientId": "[Enter the Client Id (Application ID obtained from the Azure portal), e.g. ba74781c2-53c2-442a-97c2-3d60re42f403]",
77
"ClientSecret": "[Copy the client secret added to the app from the Azure portal]",
88
"CallbackPath": "/signin-oidc",

4-WebApp-your-API/4-3-AnyOrg/TodoListService/appsettings.json

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,15 +2,15 @@
22
"AzureAd": {
33
"Instance": "https://login.microsoftonline.com/",
44
"Domain": "[Enter the domain of your tenant, e.g. contoso.onmicrosoft.com]",
5-
"TenantId": "[Enter 'common', or 'organizations' or the Tenant Id (Obtained from the Azure portal. Select 'Endpoints' from the 'App registrations' blade and use the GUID in any of the URLs), e.g. da41245a5-11b3-996c-00a8-4d99re19f292]",
5+
"TenantId": "common",
66
"ClientId": "[Enter the Client Id (Application ID obtained from the Azure portal), e.g. ba74781c2-53c2-442a-97c2-3d60re42f403]",
77
"ClientSecret": "[Copy the client secret added to the app from the Azure portal]",
88
"GraphAPI": "https://graph.microsoft.com/v1.0"
99
},
1010
"Kestrel": {
1111
"Endpoints": {
1212
"Http": {
13-
"Url": "http://localhost:4430"
13+
"Url": "https://localhost:44351"
1414
}
1515
}
1616
},

0 commit comments

Comments
 (0)