From 209be89340d768d7265f6adeef4b064cfa23007c Mon Sep 17 00:00:00 2001 From: Jean-Marc Prieur Date: Mon, 20 Jan 2020 11:21:25 -0800 Subject: [PATCH 1/4] Renaming GetAccessTokenOnBehalfOfUserAsync into GetAccessTokenForUserAsync as the OnBehalfOfUser was telling too much about OBO, whereas in the case of a Web App this was not the OBO flow. --- ...AspnetCoreWebApp-calls-Microsoft-Graph.sln | 6 ++++ .../Controllers/HomeController.cs | 2 +- .../Controllers/HomeController.cs | 2 +- .../Controllers/TodoListController.cs | 2 +- .../2-3-Multi-Tenant/README-National-Cloud.md | 4 +-- .../2-3-Multi-Tenant/README.md | 4 +-- .../Controllers/HomeController.cs | 2 +- .../Controllers/HomeController.cs | 8 ++--- .../Client/Services/TodoListService.cs | 2 +- .../Controllers/AccountController.cs | 2 +- .../5-1-Roles/Controllers/HomeController.cs | 2 +- .../Controllers/UserProfileController.cs | 2 +- .../ClaimsPrincipalFactory.cs | 2 +- Microsoft.Identity.Web/ITokenAcquisition.cs | 14 ++++++++ Microsoft.Identity.Web/README.md | 8 ++--- Microsoft.Identity.Web/TokenAcquisition.cs | 27 +++++++++++++- .../MsalAbstractTokenCacheProvider.cs | 2 ++ .../WebApiServiceCollectionExtensions.cs | 2 ++ .../WebAppServiceCollectionExtensions.cs | 36 +++++++++++++++++-- 19 files changed, 105 insertions(+), 24 deletions(-) diff --git a/2-WebApp-graph-user/2-1-Call-MSGraph/AspnetCoreWebApp-calls-Microsoft-Graph.sln b/2-WebApp-graph-user/2-1-Call-MSGraph/AspnetCoreWebApp-calls-Microsoft-Graph.sln index 7024cab3..c1407ddd 100644 --- a/2-WebApp-graph-user/2-1-Call-MSGraph/AspnetCoreWebApp-calls-Microsoft-Graph.sln +++ b/2-WebApp-graph-user/2-1-Call-MSGraph/AspnetCoreWebApp-calls-Microsoft-Graph.sln @@ -7,6 +7,8 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "WebApp-OpenIDConnect-DotNet EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.Identity.Web", "..\..\Microsoft.Identity.Web\Microsoft.Identity.Web.csproj", "{E0CEF26A-6CE6-4505-851B-6580D5564752}" EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.Identity.Web.Test", "..\..\Microsoft.Identity.Web.Test\Microsoft.Identity.Web.Test.csproj", "{0EEC3E2E-69D0-4A7F-98D6-4386330F4965}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -21,6 +23,10 @@ Global {E0CEF26A-6CE6-4505-851B-6580D5564752}.Debug|Any CPU.Build.0 = Debug|Any CPU {E0CEF26A-6CE6-4505-851B-6580D5564752}.Release|Any CPU.ActiveCfg = Release|Any CPU {E0CEF26A-6CE6-4505-851B-6580D5564752}.Release|Any CPU.Build.0 = Release|Any CPU + {0EEC3E2E-69D0-4A7F-98D6-4386330F4965}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {0EEC3E2E-69D0-4A7F-98D6-4386330F4965}.Debug|Any CPU.Build.0 = Debug|Any CPU + {0EEC3E2E-69D0-4A7F-98D6-4386330F4965}.Release|Any CPU.ActiveCfg = Release|Any CPU + {0EEC3E2E-69D0-4A7F-98D6-4386330F4965}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE diff --git a/2-WebApp-graph-user/2-1-Call-MSGraph/Controllers/HomeController.cs b/2-WebApp-graph-user/2-1-Call-MSGraph/Controllers/HomeController.cs index 7b3e9771..e8c3e3dc 100644 --- a/2-WebApp-graph-user/2-1-Call-MSGraph/Controllers/HomeController.cs +++ b/2-WebApp-graph-user/2-1-Call-MSGraph/Controllers/HomeController.cs @@ -61,7 +61,7 @@ public async Task Profile() { return GraphServiceClientFactory.GetAuthenticatedGraphClient(async () => { - string result = await tokenAcquisition.GetAccessTokenOnBehalfOfUserAsync(scopes); + string result = await tokenAcquisition.GetAccessTokenForUserAsync(scopes); return result; }, webOptions.GraphApiUrl); } diff --git a/2-WebApp-graph-user/2-2-TokenCache/Controllers/HomeController.cs b/2-WebApp-graph-user/2-2-TokenCache/Controllers/HomeController.cs index e2cd16ca..f55378a6 100644 --- a/2-WebApp-graph-user/2-2-TokenCache/Controllers/HomeController.cs +++ b/2-WebApp-graph-user/2-2-TokenCache/Controllers/HomeController.cs @@ -31,7 +31,7 @@ public IActionResult Index() public async Task Profile() { var accessToken = - await tokenAcquisition.GetAccessTokenOnBehalfOfUserAsync(new[] {Constants.ScopeUserRead}); + await tokenAcquisition.GetAccessTokenForUserAsync(new[] {Constants.ScopeUserRead}); var me = await graphApiOperations.GetUserInformation(accessToken); var photo = await graphApiOperations.GetPhotoAsBase64Async(accessToken); diff --git a/2-WebApp-graph-user/2-3-Multi-Tenant/Controllers/TodoListController.cs b/2-WebApp-graph-user/2-3-Multi-Tenant/Controllers/TodoListController.cs index 1cbac9ac..21ddcb27 100644 --- a/2-WebApp-graph-user/2-3-Multi-Tenant/Controllers/TodoListController.cs +++ b/2-WebApp-graph-user/2-3-Multi-Tenant/Controllers/TodoListController.cs @@ -97,7 +97,7 @@ public async Task Edit(int id) var userTenant = User.GetTenantId(); // Acquiring token for graph in the signed-in users tenant, so it can be used to retrieve all the users from their tenant - var graphAccessToken = await _tokenAcquisition.GetAccessTokenOnBehalfOfUserAsync(new string[] { GraphScope.UserReadAll }, userTenant); + var graphAccessToken = await _tokenAcquisition.GetAccessTokenForUserAsync(new string[] { GraphScope.UserReadAll }, userTenant); TempData["UsersDropDown"] = (await _msGraphService.GetUsersAsync(graphAccessToken)) .Select(u => new SelectListItem diff --git a/2-WebApp-graph-user/2-3-Multi-Tenant/README-National-Cloud.md b/2-WebApp-graph-user/2-3-Multi-Tenant/README-National-Cloud.md index 42cefbb8..2a2f6d48 100644 --- a/2-WebApp-graph-user/2-3-Multi-Tenant/README-National-Cloud.md +++ b/2-WebApp-graph-user/2-3-Multi-Tenant/README-National-Cloud.md @@ -318,10 +318,10 @@ If a multi-tenant app needs to acquire an access token for Microsoft Graph to be ```csharp var userTenant = User.GetTenantId(); // Acquiring token for graph using the user's tenant, so it can return all the users from their tenant -var graphAccessToken = await _tokenAcquisition.GetAccessTokenOnBehalfOfUserAsync(new string[] { GraphScope.UserReadAll }, userTenant); +var graphAccessToken = await _tokenAcquisition.GetAccessTokenForUserAsync(new string[] { GraphScope.UserReadAll }, userTenant); ``` -We are acquiring an access token for Graph with the scope `User.Read.All`, to list all the users from the tenant so you can assign a todo item to them. `GetAccessTokenOnBehalfOfUserAsync` is a helper method found on `Microsoft.Identity.Web` project, and it receives a **tenantId** as parameter to acquire a token for the desired authority. For that, we get the current authority from the built `IConfidentialClientApplication` and replace the tenantId. Below is an example of this logic. +We are acquiring an access token for Graph with the scope `User.Read.All`, to list all the users from the tenant so you can assign a todo item to them. `GetAccessTokenForUserAsync` is a helper method found on `Microsoft.Identity.Web` project, and it receives a **tenantId** as parameter to acquire a token for the desired authority. For that, we get the current authority from the built `IConfidentialClientApplication` and replace the tenantId. Below is an example of this logic. ```csharp string signedUserAuthority = confidentialClientApplication.Authority.Replace(new Uri(confidentialClientApplication.Authority).PathAndQuery, $"/{tenant}/"); diff --git a/2-WebApp-graph-user/2-3-Multi-Tenant/README.md b/2-WebApp-graph-user/2-3-Multi-Tenant/README.md index 8e3bb634..7a4f2b03 100644 --- a/2-WebApp-graph-user/2-3-Multi-Tenant/README.md +++ b/2-WebApp-graph-user/2-3-Multi-Tenant/README.md @@ -294,10 +294,10 @@ If a multi-tenant app needs to acquire an access token for Microsoft Graph to be ```csharp var userTenant = User.GetTenantId(); // Acquiring token for graph using the user's tenant, so it can return all the users from their tenant -var graphAccessToken = await _tokenAcquisition.GetAccessTokenOnBehalfOfUserAsync(new string[] { GraphScope.UserReadAll }, userTenant); +var graphAccessToken = await _tokenAcquisition.GetAccessTokenForUserAsync(new string[] { GraphScope.UserReadAll }, userTenant); ``` -We are acquiring an access token for Graph with the scope `User.Read.All`, to list all the users from the tenant so you can assign a todo item to them. `GetAccessTokenOnBehalfOfUserAsync` is a helper method found on `Microsoft.Identity.Web` project, and it receives a **tenantId** as parameter to acquire a token for the desired authority. For that, we get the current authority from the built `IConfidentialClientApplication` and replace the tenantId. Below is an example of this logic. +We are acquiring an access token for Graph with the scope `User.Read.All`, to list all the users from the tenant so you can assign a todo item to them. `GetAccessTokenForUserAsync` is a helper method found on `Microsoft.Identity.Web` project, and it receives a **tenantId** as parameter to acquire a token for the desired authority. For that, we get the current authority from the built `IConfidentialClientApplication` and replace the tenantId. Below is an example of this logic. ```csharp string signedUserAuthority = confidentialClientApplication.Authority.Replace(new Uri(confidentialClientApplication.Authority).PathAndQuery, $"/{tenant}/"); diff --git a/2-WebApp-graph-user/2-4-Sovereign-Call-MSGraph/Controllers/HomeController.cs b/2-WebApp-graph-user/2-4-Sovereign-Call-MSGraph/Controllers/HomeController.cs index e2cd16ca..f55378a6 100644 --- a/2-WebApp-graph-user/2-4-Sovereign-Call-MSGraph/Controllers/HomeController.cs +++ b/2-WebApp-graph-user/2-4-Sovereign-Call-MSGraph/Controllers/HomeController.cs @@ -31,7 +31,7 @@ public IActionResult Index() public async Task Profile() { var accessToken = - await tokenAcquisition.GetAccessTokenOnBehalfOfUserAsync(new[] {Constants.ScopeUserRead}); + await tokenAcquisition.GetAccessTokenForUserAsync(new[] {Constants.ScopeUserRead}); var me = await graphApiOperations.GetUserInformation(accessToken); var photo = await graphApiOperations.GetPhotoAsBase64Async(accessToken); diff --git a/3-WebApp-multi-APIs/Controllers/HomeController.cs b/3-WebApp-multi-APIs/Controllers/HomeController.cs index a7863f63..cc280a02 100644 --- a/3-WebApp-multi-APIs/Controllers/HomeController.cs +++ b/3-WebApp-multi-APIs/Controllers/HomeController.cs @@ -39,7 +39,7 @@ public IActionResult Index() public async Task Profile() { var accessToken = - await tokenAcquisition.GetAccessTokenOnBehalfOfUserAsync(new[] {Constants.ScopeUserRead}); + await tokenAcquisition.GetAccessTokenForUserAsync(new[] {Constants.ScopeUserRead}); var me = await graphApiOperations.GetUserInformation(accessToken); var photo = await graphApiOperations.GetPhotoAsBase64Async(accessToken); @@ -56,12 +56,12 @@ public async Task Profile() public async Task Tenants() { var accessToken = - await tokenAcquisition.GetAccessTokenOnBehalfOfUserAsync(new[] { $"{ArmApiOperationService.ArmResource}user_impersonation" }); + await tokenAcquisition.GetAccessTokenForUserAsync(new[] { $"{ArmApiOperationService.ArmResource}user_impersonation" }); var tenantIds = await armOperations.EnumerateTenantsIdsAccessibleByUser(accessToken); /* var tenantsIdsAndNames = await graphApiOperations.EnumerateTenantsIdAndNameAccessibleByUser(tenantIds, - async tenantId => { return await tokenAcquisition.GetAccessTokenOnBehalfOfUserAsync(new string[] { "Directory.Read.All" }, tenantId); }); + async tenantId => { return await tokenAcquisition.GetAccessTokenForUserAsync(new string[] { "Directory.Read.All" }, tenantId); }); */ ViewData["tenants"] = tenantIds; @@ -77,7 +77,7 @@ public async Task Blob() var scopes = new string[] { "https://storage.azure.com/user_impersonation" }; var accessToken = - await tokenAcquisition.GetAccessTokenOnBehalfOfUserAsync(scopes); + await tokenAcquisition.GetAccessTokenForUserAsync(scopes); // create a blob on behalf of the user TokenCredential tokenCredential = new TokenCredential(accessToken); diff --git a/4-WebApp-your-API/Client/Services/TodoListService.cs b/4-WebApp-your-API/Client/Services/TodoListService.cs index d543d968..0771ebe5 100644 --- a/4-WebApp-your-API/Client/Services/TodoListService.cs +++ b/4-WebApp-your-API/Client/Services/TodoListService.cs @@ -138,7 +138,7 @@ public async Task> GetAsync() private async Task PrepareAuthenticatedClient() { - var accessToken = await this._tokenAcquisition.GetAccessTokenOnBehalfOfUserAsync(new[] { this._TodoListScope }); + var accessToken = await this._tokenAcquisition.GetAccessTokenForUserAsync(new[] { this._TodoListScope }); Debug.WriteLine($"access token-{accessToken}"); this._httpClient.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", accessToken); this._httpClient.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json")); diff --git a/5-WebApp-AuthZ/5-1-Roles/Controllers/AccountController.cs b/5-WebApp-AuthZ/5-1-Roles/Controllers/AccountController.cs index 3089008c..c8463f93 100644 --- a/5-WebApp-AuthZ/5-1-Roles/Controllers/AccountController.cs +++ b/5-WebApp-AuthZ/5-1-Roles/Controllers/AccountController.cs @@ -41,7 +41,7 @@ public async Task Groups() GraphServiceClient graphServiceClient = GraphServiceClientFactory.GetAuthenticatedGraphClient(async () => { - string result = await tokenAcquisition.GetAccessTokenOnBehalfOfUserAsync(scopes); + string result = await tokenAcquisition.GetAccessTokenForUserAsync(scopes); return result; }, webOptions.GraphApiUrl); diff --git a/5-WebApp-AuthZ/5-1-Roles/Controllers/HomeController.cs b/5-WebApp-AuthZ/5-1-Roles/Controllers/HomeController.cs index 0f8cb151..4df52c06 100644 --- a/5-WebApp-AuthZ/5-1-Roles/Controllers/HomeController.cs +++ b/5-WebApp-AuthZ/5-1-Roles/Controllers/HomeController.cs @@ -60,7 +60,7 @@ public async Task Profile() { return GraphServiceClientFactory.GetAuthenticatedGraphClient(async () => { - string result = await tokenAcquisition.GetAccessTokenOnBehalfOfUserAsync(scopes); + string result = await tokenAcquisition.GetAccessTokenForUserAsync(scopes); return result; }, webOptions.GraphApiUrl); } diff --git a/5-WebApp-AuthZ/5-2-Groups/Controllers/UserProfileController.cs b/5-WebApp-AuthZ/5-2-Groups/Controllers/UserProfileController.cs index 1ebf7fb7..a4b705c2 100644 --- a/5-WebApp-AuthZ/5-2-Groups/Controllers/UserProfileController.cs +++ b/5-WebApp-AuthZ/5-2-Groups/Controllers/UserProfileController.cs @@ -27,7 +27,7 @@ public async Task Index() // Using group ids/names in the IsInRole method // var isinrole = User.IsInRole("8873daa2-17af-4e72-973e-930c94ef7549"); - string accessToken = await tokenAcquisition.GetAccessTokenOnBehalfOfUserAsync(new[] { Constants.ScopeUserRead, Constants.ScopeDirectoryReadAll }); + string accessToken = await tokenAcquisition.GetAccessTokenForUserAsync(new[] { Constants.ScopeUserRead, Constants.ScopeDirectoryReadAll }); User me = await graphService.GetMeAsync(accessToken); ViewData["Me"] = me; diff --git a/Microsoft.Identity.Web/ClaimsPrincipalFactory.cs b/Microsoft.Identity.Web/ClaimsPrincipalFactory.cs index 0c202dd9..64e2bcf7 100644 --- a/Microsoft.Identity.Web/ClaimsPrincipalFactory.cs +++ b/Microsoft.Identity.Web/ClaimsPrincipalFactory.cs @@ -30,7 +30,7 @@ public static class ClaimsPrincipalFactory /// subscriptionStore.GetSubscriptionInfo(notification.SubscriptionId); /// HttpContext.User = ClaimsPrincipalExtension.FromTenantIdAndObjectId(subscription.TenantId, /// subscription.UserId); - /// string accessToken = await tokenAcquisition.GetAccessTokenOnBehalfOfUserAsync(scopes); + /// string accessToken = await tokenAcquisition.GetAccessTokenForUserAsync(scopes); /// /// public static ClaimsPrincipal FromTenantIdAndObjectId(string tenantId, string objectId) diff --git a/Microsoft.Identity.Web/ITokenAcquisition.cs b/Microsoft.Identity.Web/ITokenAcquisition.cs index d4410d35..02b069e2 100644 --- a/Microsoft.Identity.Web/ITokenAcquisition.cs +++ b/Microsoft.Identity.Web/ITokenAcquisition.cs @@ -4,6 +4,7 @@ using Microsoft.AspNetCore.Authentication.OpenIdConnect; using Microsoft.AspNetCore.Http; using Microsoft.Identity.Client; +using System; using System.Collections.Generic; using System.Threading.Tasks; @@ -54,8 +55,21 @@ public interface ITokenAcquisition /// Enables to override the tenant/account for the same identity. This is useful in the /// cases where a given account is guest in other tenants, and you want to acquire tokens for a specific tenant /// An access token to call on behalf of the user, the downstream API characterized by its scopes + [Obsolete("Renamed to GetAccessTokenForUserAsync")] Task GetAccessTokenOnBehalfOfUserAsync(IEnumerable scopes, string tenantId = null); + /// + /// Typically used from an ASP.NET Core Web App or Web API controller, this method gets an access token + /// for a downstream API on behalf of the user account which claims are provided in the + /// member of the parameter + /// + /// HttpContext associated with the Controller or auth operation + /// Scopes to request for the downstream API to call + /// Enables to override the tenant/account for the same identity. This is useful in the + /// cases where a given account is guest in other tenants, and you want to acquire tokens for a specific tenant + /// An access token to call on behalf of the user, the downstream API characterized by its scopes + Task GetAccessTokenForUserAsync(IEnumerable scopes, string tenantId = null); + /// /// Removes the account associated with context.HttpContext.User from the MSAL.NET cache /// diff --git a/Microsoft.Identity.Web/README.md b/Microsoft.Identity.Web/README.md index 9a07e42d..f2b84417 100644 --- a/Microsoft.Identity.Web/README.md +++ b/Microsoft.Identity.Web/README.md @@ -118,7 +118,7 @@ public class HomeController : Controller ... ``` -Then in your controller actions, you'll need to call: `ITokenAcquisition.GetAccessTokenOnBehalfOfUserAsync` passing the scopes for which to request a token. The other methods of ITokenAcquisition are used from the `AddWebAppCallsProtectedWebApi()` method and similar methods for web APIs (see below). +Then in your controller actions, you'll need to call: `ITokenAcquisition.GetAccessTokenForUserAsync` passing the scopes for which to request a token. The other methods of ITokenAcquisition are used from the `AddWebAppCallsProtectedWebApi()` method and similar methods for web APIs (see below). ```CSharp [Authorize] @@ -130,13 +130,13 @@ public class HomeController : Controller public async Task Action() { string[] scopes = new []{"user.read"}; - string token = await tokenAcquisition.GetAccessTokenOnBehalfOfUserAsync(scopes); + string token = await tokenAcquisition.GetAccessTokenForUserAsync(scopes); ... // call the downstream API with the bearer token in the Authorize header } ``` -The controller action is decorated by an attribute `AuthorizeForScopesAttribute` which enables to process the `MsalUiRequiredException` that could be thrown by the service implementing `ITokenAcquisition.GetAccessTokenOnBehalfOfUserAsync` so that the web app interacts with the user, and ask them to consent to the scopes, or re-sign-in if needed. +The controller action is decorated by an attribute `AuthorizeForScopesAttribute` which enables to process the `MsalUiRequiredException` that could be thrown by the service implementing `ITokenAcquisition.GetAccessTokenForUserAsync` so that the web app interacts with the user, and ask them to consent to the scopes, or re-sign-in if needed. AuthorizeForScopesAttribute @@ -235,7 +235,7 @@ For your web API to call downstream APIs, you'll need to: ScopesRequiredHttpContextExtensions -- in your controller actions, to call: `ITokenAcquisition.GetAccessTokenOnBehalfOfUserAsync` passing the scopes for which to request a token. +- in your controller actions, to call: `ITokenAcquisition.GetAccessTokenForUserAsync` passing the scopes for which to request a token. The following code snippet shows how to combine these steps: diff --git a/Microsoft.Identity.Web/TokenAcquisition.cs b/Microsoft.Identity.Web/TokenAcquisition.cs index 9455cb0e..dbb2b4ec 100644 --- a/Microsoft.Identity.Web/TokenAcquisition.cs +++ b/Microsoft.Identity.Web/TokenAcquisition.cs @@ -158,9 +158,34 @@ public async Task AddAccountToCacheFromAuthorizationCodeAsync(AuthorizationCodeR /// passing the validated token (as a JwtSecurityToken). Calling it from a Web App supposes that /// you have previously called AddAccountToCacheFromAuthorizationCodeAsync from a method called by /// OpenIdConnectOptions.Events.OnAuthorizationCodeReceived + [Obsolete("Renamed to GetAccessTokenForUserAsync. Please use that method")] public async Task GetAccessTokenOnBehalfOfUserAsync( IEnumerable scopes, string tenant = null) + { + return await GetAccessTokenForUserAsync(scopes, tenant); + } + + /// + /// Typically used from a Web App or WebAPI controller, this method retrieves an access token + /// for a downstream API using; + /// 1) the token cache (for Web Apps and Web APis) if a token exists in the cache + /// 2) or the on-behalf-of flow + /// in Web APIs, for the user account that is ascertained from claims are provided in the + /// instance of the current HttpContext + /// + /// Scopes to request for the downstream API to call + /// Enables overriding of the tenant/account for the same identity. This is useful in the + /// cases where a given account is guest in other tenants, and you want to acquire tokens for a specific tenant, like where the user is a guest in + /// An access token to call the downstream API and populated with this downstream Api's scopes + /// Calling this method from a Web API supposes that you have previously called, + /// in a method called by JwtBearerOptions.Events.OnTokenValidated, the HttpContextExtensions.StoreTokenUsedToCallWebAPI method + /// passing the validated token (as a JwtSecurityToken). Calling it from a Web App supposes that + /// you have previously called AddAccountToCacheFromAuthorizationCodeAsync from a method called by + /// OpenIdConnectOptions.Events.OnAuthorizationCodeReceived + public async Task GetAccessTokenForUserAsync( + IEnumerable scopes, + string tenant = null) { if (scopes == null) { @@ -178,7 +203,7 @@ public async Task GetAccessTokenOnBehalfOfUserAsync( } catch(MsalUiRequiredException ex) { - // GetAccessTokenOnBehalfOfUserAsync is an abstraction that can be called from a Web App or a Web API + // GetAccessTokenForUserAsync is an abstraction that can be called from a Web App or a Web API // to get a token for a Web API on behalf of the user, but not necessarily with the on behalf of OAuth2.0 // flow as this one only applies to Web APIs. JwtSecurityToken validatedToken = CurrentHttpContext.GetTokenUsedToCallWebAPI(); diff --git a/Microsoft.Identity.Web/TokenCacheProviders/MsalAbstractTokenCacheProvider.cs b/Microsoft.Identity.Web/TokenCacheProviders/MsalAbstractTokenCacheProvider.cs index 7ad18082..ac6c155e 100644 --- a/Microsoft.Identity.Web/TokenCacheProviders/MsalAbstractTokenCacheProvider.cs +++ b/Microsoft.Identity.Web/TokenCacheProviders/MsalAbstractTokenCacheProvider.cs @@ -110,6 +110,8 @@ public async Task ClearAsync() { // This is here a user token cache await RemoveKeyAsync(GetCacheKey(false)).ConfigureAwait(false); + + // TODO: Clear the cookie session if any } /// diff --git a/Microsoft.Identity.Web/WebApiServiceCollectionExtensions.cs b/Microsoft.Identity.Web/WebApiServiceCollectionExtensions.cs index ffd9843a..4df57708 100644 --- a/Microsoft.Identity.Web/WebApiServiceCollectionExtensions.cs +++ b/Microsoft.Identity.Web/WebApiServiceCollectionExtensions.cs @@ -23,6 +23,8 @@ namespace Microsoft.Identity.Web /// public static class WebApiServiceCollectionExtensions { + #region Compatibility + #endregion /// /// Protects the Web API with Microsoft identity platform (formerly Azure AD v2.0) /// This method expects the configuration file will have a section named "AzureAd" with the necessary settings to initialize authentication options. diff --git a/Microsoft.Identity.Web/WebAppServiceCollectionExtensions.cs b/Microsoft.Identity.Web/WebAppServiceCollectionExtensions.cs index 914bf48d..97cf90ac 100644 --- a/Microsoft.Identity.Web/WebAppServiceCollectionExtensions.cs +++ b/Microsoft.Identity.Web/WebAppServiceCollectionExtensions.cs @@ -10,6 +10,7 @@ using Microsoft.Identity.Client; using Microsoft.Identity.Web.Resource; using Microsoft.IdentityModel.Protocols.OpenIdConnect; +using System; using System.Collections.Generic; using System.Threading.Tasks; @@ -20,6 +21,34 @@ namespace Microsoft.Identity.Web /// public static class WebAppServiceCollectionExtensions { + #region + [Obsolete("Use AddSignIn")] + public static IServiceCollection AddMicrosoftIdentityPlatform( + this IServiceCollection services, + IConfiguration configuration, + string configSectionName = "AzureAd", + bool subscribeToOpenIdConnectMiddlewareDiagnosticsEvents = false) + { + return AddSignIn(services, + configuration, + configSectionName, + subscribeToOpenIdConnectMiddlewareDiagnosticsEvents); + } + + [Obsolete("Use AddWebAppCallsProtectedWebApi")] + public static IServiceCollection AddMsal(this IServiceCollection services, + IConfiguration configuration, + IEnumerable initialScopes, + string configSectionName = "AzureAd") + { + return AddWebAppCallsProtectedWebApi(services, + configuration, + initialScopes, + configSectionName); + } + + #endregion + /// /// Add authentication with Microsoft identity platform. /// This method expects the configuration file will have a section named "AzureAd" with the necessary settings to initialize authentication options. @@ -66,7 +95,7 @@ public static IServiceCollection AddSignIn( // For more details see [ID Tokens](https://docs.microsoft.com/azure/active-directory/develop/id-tokens) // and [Access Tokens](https://docs.microsoft.com/azure/active-directory/develop/access-tokens) options.TokenValidationParameters.NameClaimType = "preferred_username"; - + // Avoids having users being presented the select account dialog when they are already signed-in // for instance when going through incremental consent options.Events.OnRedirectToIdentityProvider = context => @@ -111,7 +140,10 @@ public static IServiceCollection AddSignIn( /// Service collection to which to add authentication /// Initial scopes to request at sign-in /// - public static IServiceCollection AddWebAppCallsProtectedWebApi(this IServiceCollection services, IConfiguration configuration, IEnumerable initialScopes, string configSectionName = "AzureAd") + public static IServiceCollection AddWebAppCallsProtectedWebApi(this IServiceCollection services, + IConfiguration configuration, + IEnumerable initialScopes, + string configSectionName = "AzureAd") { // Ensure that configuration options for MSAL.NET, HttpContext accessor and the Token acquisition service // (encapsulating MSAL.NET) are available through dependency injection From defe5dd76a14e4348f1ed03b989c918893d8e3ab Mon Sep 17 00:00:00 2001 From: Jean-Marc Prieur Date: Mon, 20 Jan 2020 11:45:44 -0800 Subject: [PATCH 2/4] endregion --- Microsoft.Identity.Web/WebAppServiceCollectionExtensions.cs | 1 - 1 file changed, 1 deletion(-) diff --git a/Microsoft.Identity.Web/WebAppServiceCollectionExtensions.cs b/Microsoft.Identity.Web/WebAppServiceCollectionExtensions.cs index 97cf90ac..ec0614dc 100644 --- a/Microsoft.Identity.Web/WebAppServiceCollectionExtensions.cs +++ b/Microsoft.Identity.Web/WebAppServiceCollectionExtensions.cs @@ -46,7 +46,6 @@ public static IServiceCollection AddMsal(this IServiceCollection services, initialScopes, configSectionName); } - #endregion /// From ee2bdccc1c3961316848129023ac39242a29b62e Mon Sep 17 00:00:00 2001 From: Jean-Marc Prieur Date: Mon, 20 Jan 2020 11:59:46 -0800 Subject: [PATCH 3/4] Addressing PR feedback --- .../WebApiServiceCollectionExtensions.cs | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/Microsoft.Identity.Web/WebApiServiceCollectionExtensions.cs b/Microsoft.Identity.Web/WebApiServiceCollectionExtensions.cs index 4df57708..c290d05c 100644 --- a/Microsoft.Identity.Web/WebApiServiceCollectionExtensions.cs +++ b/Microsoft.Identity.Web/WebApiServiceCollectionExtensions.cs @@ -24,7 +24,24 @@ namespace Microsoft.Identity.Web public static class WebApiServiceCollectionExtensions { #region Compatibility + /// + /// Protects the Web API with Microsoft identity platform (formerly Azure AD v2.0) + /// This supposes that the configuration files have a section named configSectionName (typically "AzureAD") + /// + /// Service collection to which to add authentication + /// Configuration + /// + public static IServiceCollection AddProtectedApiCallsWebApis( + this IServiceCollection services, + IConfiguration configuration, + string configSectionName = "AzureAd") + { + return AddProtectedWebApiCallsProtectedWebApi(services, + configuration, + configSectionName); + } #endregion + /// /// Protects the Web API with Microsoft identity platform (formerly Azure AD v2.0) /// This method expects the configuration file will have a section named "AzureAd" with the necessary settings to initialize authentication options. From 28d15b10585d6cecc489ed41b9c0e7b9647ee867 Mon Sep 17 00:00:00 2001 From: Jean-Marc Prieur Date: Mon, 20 Jan 2020 12:06:15 -0800 Subject: [PATCH 4/4] Addressing PR comment --- .../TokenCacheProviders/MsalAbstractTokenCacheProvider.cs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/Microsoft.Identity.Web/TokenCacheProviders/MsalAbstractTokenCacheProvider.cs b/Microsoft.Identity.Web/TokenCacheProviders/MsalAbstractTokenCacheProvider.cs index 45b6b0e8..9a3ac080 100644 --- a/Microsoft.Identity.Web/TokenCacheProviders/MsalAbstractTokenCacheProvider.cs +++ b/Microsoft.Identity.Web/TokenCacheProviders/MsalAbstractTokenCacheProvider.cs @@ -111,7 +111,8 @@ public async Task ClearAsync() // This is a user token cache await RemoveKeyAsync(GetCacheKey(false)).ConfigureAwait(false); - // TODO: Clear the cookie session if any + // TODO: Clear the cookie session if any. Get inspiration from + // https://github.com/Azure-Samples/active-directory-aspnetcore-webapp-openidconnect-v2/issues/240 } ///