From 7f7bf9868b5a791e3bf67623a4ff58ad5d805f8f Mon Sep 17 00:00:00 2001 From: Jean-Marc Prieur Date: Thu, 27 Aug 2020 14:15:10 +0200 Subject: [PATCH 1/7] Updating Web project templates to ms.id.web 0.3.0-preview --- .../.template.config/template.json | 2 - .../Pages/CallWebApi.razor | 14 +++- .../Services/DownstreamWebApi.cs | 72 ------------------- .../MicrosoftGraphServiceExtensions.cs | 36 ---------- .../TokenAcquisitionCredentialProvider.cs | 27 ------- .../content/BlazorServerWeb-CSharp/Startup.cs | 41 ++++++----- .../BlazorServerWeb-CSharp/appsettings.json | 12 ++-- .../.template.config/template.json | 17 +---- .../Controllers/WeatherForecastController.cs | 18 +++-- .../Server/Services/DownstreamWebApi.cs | 72 ------------------- .../MicrosoftGraphServiceExtensions.cs | 36 ---------- .../TokenAcquisitionCredentialProvider.cs | 27 ------- .../Server/Startup.cs | 34 +++++---- .../Server/appsettings.json | 12 ++-- .../.template.config/template.json | 13 ---- .../Pages/Index.cshtml.cs | 14 +++- .../Services/DownstreamWebApi.cs | 72 ------------------- .../MicrosoftGraphServiceExtensions.cs | 37 ---------- .../TokenAcquisitionCredentialProvider.cs | 27 ------- .../content/RazorPagesWeb-CSharp/Startup.cs | 50 +++++++------ .../RazorPagesWeb-CSharp/appsettings.json | 12 ++-- .../.template.config/template.json | 13 ---- .../Controllers/HomeController.cs | 17 +++-- .../Services/DownstreamWebApi.cs | 72 ------------------- .../MicrosoftGraphServiceExtensions.cs | 37 ---------- .../TokenAcquisitionCredentialProvider.cs | 27 ------- .../content/StarterWeb-CSharp/Startup.cs | 48 ++++++------- .../StarterWeb-CSharp/appsettings.json | 12 ++-- .../.template.config/dotnetcli.host.json | 4 ++ .../.template.config/template.json | 20 ++---- .../Controllers/WeatherForecastController.cs | 14 +++- .../Services/DownstreamWebApi.cs | 72 ------------------- .../MicrosoftGraphServiceExtensions.cs | 37 ---------- .../TokenAcquisitionCredentialProvider.cs | 27 ------- .../content/WebApi-CSharp/Startup.cs | 42 +++++------ .../content/WebApi-CSharp/appsettings.json | 12 ++-- 36 files changed, 209 insertions(+), 890 deletions(-) delete mode 100644 src/ProjectTemplates/Web.ProjectTemplates/content/BlazorServerWeb-CSharp/Services/DownstreamWebApi.cs delete mode 100644 src/ProjectTemplates/Web.ProjectTemplates/content/BlazorServerWeb-CSharp/Services/MicrosoftGraphServiceExtensions.cs delete mode 100644 src/ProjectTemplates/Web.ProjectTemplates/content/BlazorServerWeb-CSharp/Services/TokenAcquisitionCredentialProvider.cs delete mode 100644 src/ProjectTemplates/Web.ProjectTemplates/content/ComponentsWebAssembly-CSharp/Server/Services/DownstreamWebApi.cs delete mode 100644 src/ProjectTemplates/Web.ProjectTemplates/content/ComponentsWebAssembly-CSharp/Server/Services/MicrosoftGraphServiceExtensions.cs delete mode 100644 src/ProjectTemplates/Web.ProjectTemplates/content/ComponentsWebAssembly-CSharp/Server/Services/TokenAcquisitionCredentialProvider.cs delete mode 100644 src/ProjectTemplates/Web.ProjectTemplates/content/RazorPagesWeb-CSharp/Services/DownstreamWebApi.cs delete mode 100644 src/ProjectTemplates/Web.ProjectTemplates/content/RazorPagesWeb-CSharp/Services/MicrosoftGraphServiceExtensions.cs delete mode 100644 src/ProjectTemplates/Web.ProjectTemplates/content/RazorPagesWeb-CSharp/Services/TokenAcquisitionCredentialProvider.cs delete mode 100644 src/ProjectTemplates/Web.ProjectTemplates/content/StarterWeb-CSharp/Services/DownstreamWebApi.cs delete mode 100644 src/ProjectTemplates/Web.ProjectTemplates/content/StarterWeb-CSharp/Services/MicrosoftGraphServiceExtensions.cs delete mode 100644 src/ProjectTemplates/Web.ProjectTemplates/content/StarterWeb-CSharp/Services/TokenAcquisitionCredentialProvider.cs delete mode 100644 src/ProjectTemplates/Web.ProjectTemplates/content/WebApi-CSharp/Services/DownstreamWebApi.cs delete mode 100644 src/ProjectTemplates/Web.ProjectTemplates/content/WebApi-CSharp/Services/MicrosoftGraphServiceExtensions.cs delete mode 100644 src/ProjectTemplates/Web.ProjectTemplates/content/WebApi-CSharp/Services/TokenAcquisitionCredentialProvider.cs diff --git a/src/ProjectTemplates/Web.ProjectTemplates/content/BlazorServerWeb-CSharp/.template.config/template.json b/src/ProjectTemplates/Web.ProjectTemplates/content/BlazorServerWeb-CSharp/.template.config/template.json index 463e0eb7ba2b..020748fbc433 100644 --- a/src/ProjectTemplates/Web.ProjectTemplates/content/BlazorServerWeb-CSharp/.template.config/template.json +++ b/src/ProjectTemplates/Web.ProjectTemplates/content/BlazorServerWeb-CSharp/.template.config/template.json @@ -138,8 +138,6 @@ { "condition": "(!GenerateGraph)", "exclude": [ - "Services/MicrosoftGraphServiceExtensions.cs", - "Services/TokenAcquisitionCredentialProvider.cs", "Shared/NavMenu.CallsMicrosoftGraph.razor", "Pages/ShowProfile.razor" ] diff --git a/src/ProjectTemplates/Web.ProjectTemplates/content/BlazorServerWeb-CSharp/Pages/CallWebApi.razor b/src/ProjectTemplates/Web.ProjectTemplates/content/BlazorServerWeb-CSharp/Pages/CallWebApi.razor index 858bf6f09f6a..d3bad28a25d4 100644 --- a/src/ProjectTemplates/Web.ProjectTemplates/content/BlazorServerWeb-CSharp/Pages/CallWebApi.razor +++ b/src/ProjectTemplates/Web.ProjectTemplates/content/BlazorServerWeb-CSharp/Pages/CallWebApi.razor @@ -21,13 +21,25 @@ else } @code { + private HttpResponseMessage response; private string apiResult; protected override async Task OnInitializedAsync() { try { - apiResult = await downstreamAPI.CallWebApiAsync("me"); + response = await downstreamAPI.CallWebApiForUserAsync( + "DownstreamApi", + options => options.RelativePath = ""); + + if (response.StatusCode == System.Net.HttpStatusCode.OK) + { + apiResult = await response.Content.ReadAsStringAsync(); + } + else + { + apiResult = "Failed to call the web API"; + } } catch (Exception ex) { diff --git a/src/ProjectTemplates/Web.ProjectTemplates/content/BlazorServerWeb-CSharp/Services/DownstreamWebApi.cs b/src/ProjectTemplates/Web.ProjectTemplates/content/BlazorServerWeb-CSharp/Services/DownstreamWebApi.cs deleted file mode 100644 index fbb0de85f661..000000000000 --- a/src/ProjectTemplates/Web.ProjectTemplates/content/BlazorServerWeb-CSharp/Services/DownstreamWebApi.cs +++ /dev/null @@ -1,72 +0,0 @@ -using System.Net; -using System.Net.Http; -using System.Threading.Tasks; -using Microsoft.Extensions.Configuration; -using Microsoft.Extensions.DependencyInjection; -using Microsoft.Identity.Web; - -namespace BlazorServerWeb_CSharp -{ - public interface IDownstreamWebApi - { - Task CallWebApiAsync(string relativeEndpoint = "", string[] requiredScopes = null); - } - - public static class DownstreamWebApiExtensions - { - public static void AddDownstreamWebApiService(this IServiceCollection services, IConfiguration configuration) - { - // https://docs.microsoft.com/en-us/dotnet/standard/microservices-architecture/implement-resilient-applications/use-httpclientfactory-to-implement-resilient-http-requests - services.AddHttpClient(); - } - } - - public class DownstreamWebApi : IDownstreamWebApi - { - private readonly ITokenAcquisition _tokenAcquisition; - - private readonly IConfiguration _configuration; - - private readonly HttpClient _httpClient; - - public DownstreamWebApi( - ITokenAcquisition tokenAcquisition, - IConfiguration configuration, - HttpClient httpClient) - { - _tokenAcquisition = tokenAcquisition; - _configuration = configuration; - _httpClient = httpClient; - } - - /// - /// Calls the Web API with the required scopes - /// - /// [Optional] Scopes required to call the Web API. If - /// not specified, uses scopes from the configuration - /// Endpoint relative to the CalledApiUrl configuration - /// A JSON string representing the result of calling the Web API - public async Task CallWebApiAsync(string relativeEndpoint = "", string[] requiredScopes = null) - { - string[] scopes = requiredScopes ?? _configuration["CalledApi:CalledApiScopes"]?.Split(' '); - string apiUrl = (_configuration["CalledApi:CalledApiUrl"] as string)?.TrimEnd('/') + $"/{relativeEndpoint}"; - - string accessToken = await _tokenAcquisition.GetAccessTokenForUserAsync(scopes); - HttpRequestMessage httpRequestMessage = new HttpRequestMessage(HttpMethod.Get, apiUrl); - httpRequestMessage.Headers.Add("Authorization", $"bearer {accessToken}"); - - string apiResult; - var response = await _httpClient.SendAsync(httpRequestMessage); - if (response.StatusCode == HttpStatusCode.OK) - { - apiResult = await response.Content.ReadAsStringAsync(); - } - else - { - apiResult = $"Error calling the API '{apiUrl}'"; - } - - return apiResult; - } - } -} diff --git a/src/ProjectTemplates/Web.ProjectTemplates/content/BlazorServerWeb-CSharp/Services/MicrosoftGraphServiceExtensions.cs b/src/ProjectTemplates/Web.ProjectTemplates/content/BlazorServerWeb-CSharp/Services/MicrosoftGraphServiceExtensions.cs deleted file mode 100644 index f917fbed586f..000000000000 --- a/src/ProjectTemplates/Web.ProjectTemplates/content/BlazorServerWeb-CSharp/Services/MicrosoftGraphServiceExtensions.cs +++ /dev/null @@ -1,36 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using Microsoft.Extensions.Configuration; -using Microsoft.Extensions.DependencyInjection; -using Microsoft.Graph; -using Microsoft.Identity.Web; - -namespace BlazorServerWeb_CSharp -{ - public static class MicrosoftGraphServiceExtensions - { - /// - /// Adds the Microsoft Graph client as a singleton. - /// - /// Service collection. - /// Initial scopes. - /// Base URL for Microsoft graph. This can be - /// changed for instance for applications running in national clouds - public static IServiceCollection AddMicrosoftGraph(this IServiceCollection services, - IEnumerable initialScopes, - string graphBaseUrl = "https://graph.microsoft.com/v1.0") - { - services.AddTokenAcquisition(true); - services.AddSingleton(serviceProvider => - { - var tokenAquisitionService = serviceProvider.GetService(); - GraphServiceClient client = string.IsNullOrWhiteSpace(graphBaseUrl) ? - new GraphServiceClient(new TokenAcquisitionCredentialProvider(tokenAquisitionService, initialScopes)) : - new GraphServiceClient(graphBaseUrl, new TokenAcquisitionCredentialProvider(tokenAquisitionService, initialScopes)); - return client; - }); - return services; - } - } -} diff --git a/src/ProjectTemplates/Web.ProjectTemplates/content/BlazorServerWeb-CSharp/Services/TokenAcquisitionCredentialProvider.cs b/src/ProjectTemplates/Web.ProjectTemplates/content/BlazorServerWeb-CSharp/Services/TokenAcquisitionCredentialProvider.cs deleted file mode 100644 index 5d6c643ca455..000000000000 --- a/src/ProjectTemplates/Web.ProjectTemplates/content/BlazorServerWeb-CSharp/Services/TokenAcquisitionCredentialProvider.cs +++ /dev/null @@ -1,27 +0,0 @@ -using Microsoft.Graph; -using Microsoft.Identity.Web; -using System.Collections; -using System.Collections.Generic; -using System.Net.Http; -using System.Threading.Tasks; - -namespace BlazorServerWeb_CSharp -{ - internal class TokenAcquisitionCredentialProvider : IAuthenticationProvider - { - public TokenAcquisitionCredentialProvider(ITokenAcquisition tokenAcquisition, IEnumerable initialScopes) - { - _tokenAcquisition = tokenAcquisition; - _initialScopes = initialScopes; - } - - ITokenAcquisition _tokenAcquisition; - IEnumerable _initialScopes; - - public async Task AuthenticateRequestAsync(HttpRequestMessage request) - { - request.Headers.Add("Authorization", - $"Bearer {await _tokenAcquisition.GetAccessTokenForUserAsync(_initialScopes)}"); - } - } -} diff --git a/src/ProjectTemplates/Web.ProjectTemplates/content/BlazorServerWeb-CSharp/Startup.cs b/src/ProjectTemplates/Web.ProjectTemplates/content/BlazorServerWeb-CSharp/Startup.cs index 72c0379b5712..89b4491a48b9 100644 --- a/src/ProjectTemplates/Web.ProjectTemplates/content/BlazorServerWeb-CSharp/Startup.cs +++ b/src/ProjectTemplates/Web.ProjectTemplates/content/BlazorServerWeb-CSharp/Startup.cs @@ -4,9 +4,9 @@ using System.Threading.Tasks; #if (OrganizationalAuth || IndividualB2CAuth) using Microsoft.AspNetCore.Authentication; +using Microsoft.AspNetCore.Authentication.OpenIdConnect; using Microsoft.Identity.Web; using Microsoft.Identity.Web.UI; -using Microsoft.Identity.Web.TokenCacheProviders.InMemory; #endif #if (OrganizationalAuth) #if (MultiOrgAuth) @@ -73,38 +73,41 @@ public void ConfigureServices(IServiceCollection services) .AddEntityFrameworkStores(); #elif (OrganizationalAuth) #if (GenerateApiOrGraph) - string[] scopes = Configuration.GetValue("CalledApi:CalledApiScopes")?.Split(' '); + string[] initialScopes = Configuration.GetValue("DownstreamApi:Scopes")?.Split(' '); + #endif + services.AddAuthentication(OpenIdConnectDefaults.AuthenticationScheme) + .AddMicrosoftIdentityWebApp(Configuration.GetSection("AzureAd")) #if (GenerateApiOrGraph) - services.AddMicrosoftWebAppAuthentication(Configuration, "AzureAd") - .AddMicrosoftWebAppCallsWebApi(Configuration, scopes, "AzureAd") - .AddInMemoryTokenCaches(); -#else - services.AddMicrosoftWebAppAuthentication(Configuration, "AzureAd"); -#endif + .EnableTokenAcquisitionToCallDownstreamApi(initialScopes) #if (GenerateApi) - services.AddDownstreamWebApiService(Configuration); + .AddDownstreamWebApi("DownstreamApi", Configuration.GetSection("DownstreamApi")) #endif #if (GenerateGraph) - services.AddMicrosoftGraph(scopes, Configuration.GetValue("CalledApi:CalledApiUrl")); + .AddMicrosoftGraph(Configuration.GetSection("DownstreamApi")) +#endif + .AddInMemoryTokenCaches(); +#else + ; #endif #elif (IndividualB2CAuth) #if (GenerateApi) - string[] scopes = Configuration.GetValue("CalledApi:CalledApiScopes")?.Split(' '); + string[] initialScopes = Configuration.GetValue("DownstreamApi:Scopes")?.Split(' '); + #endif + services.AddAuthentication(OpenIdConnectDefaults.AuthenticationScheme) + .AddMicrosoftIdentityWebApp(Configuration.GetSection("AzureAdB2C")) #if (GenerateApi) - services.AddMicrosoftWebAppAuthentication(Configuration, "AzureAdB2C") - .AddMicrosoftWebAppCallsWebApi(Configuration, scopes, "AzureAdB2C") - .AddInMemoryTokenCaches(); - - services.AddDownstreamWebApiService(Configuration); + .EnableTokenAcquisitionToCallDownstreamApi(initialScopes) + .AddDownstreamWebApi("DownstreamApi", Configuration.GetSection("DownstreamApi")) + .AddInMemoryTokenCaches(); #else - services.AddMicrosoftWebAppAuthentication(Configuration, "AzureAdB2C"); + ; #endif #endif #if (OrganizationalAuth || IndividualB2CAuth) services.AddControllersWithViews() - .AddMicrosoftIdentityUI(); + .AddMicrosoftIdentityUI(); services.AddAuthorization(options => { @@ -116,7 +119,7 @@ public void ConfigureServices(IServiceCollection services) services.AddRazorPages(); #if (OrganizationalAuth || IndividualB2CAuth) services.AddServerSideBlazor() - .AddMicrosoftIdentityConsentHandler(); + .AddMicrosoftIdentityConsentHandler(); #else services.AddServerSideBlazor(); #endif diff --git a/src/ProjectTemplates/Web.ProjectTemplates/content/BlazorServerWeb-CSharp/appsettings.json b/src/ProjectTemplates/Web.ProjectTemplates/content/BlazorServerWeb-CSharp/appsettings.json index b2b1a5fa42d9..1c0516223b50 100644 --- a/src/ProjectTemplates/Web.ProjectTemplates/content/BlazorServerWeb-CSharp/appsettings.json +++ b/src/ProjectTemplates/Web.ProjectTemplates/content/BlazorServerWeb-CSharp/appsettings.json @@ -17,10 +17,10 @@ // }, ////#elseif (OrganizationalAuth) // "AzureAd": { +// "Instance": "https:////login.microsoftonline.com/", //#if (MultiOrgAuth) -// "Instance": "https:////login.microsoftonline.com/common", +// "TenantId": "common", //#elseif (SingleOrgAuth) -// "Instance": "https:////login.microsoftonline.com/", // "Domain": "qualified.domain.name", // "TenantId": "22222222-2222-2222-2222-222222222222", //#endif @@ -34,16 +34,16 @@ // }, ////#endif ////#if (GenerateApiOrGraph) -// "CalledApi": { +// "DownstreamApi": { // /* -// 'CalledApiScopes' contains space separated scopes of the Web API you want to call. This can be: +// 'Scopes' contains space separated scopes of the Web API you want to call. This can be: // - a scope for a V2 application (for instance api://b3682cc7-8b30-4bd2-aaba-080c6bf0fd31/access_as_user) // - a scope corresponding to a V1 application (for instance /.default, where is the // App ID URI of a legacy v1 Web application // Applications are registered in the https://portal.azure.com portal. // */ -// "CalledApiScopes": "user.read", -// "CalledApiUrl": "[WebApiUrl]" +// "BaseUrl": "[WebApiUrl]", +// "Scopes": "user.read" // }, ////#endif ////#if (IndividualLocalAuth) diff --git a/src/ProjectTemplates/Web.ProjectTemplates/content/ComponentsWebAssembly-CSharp/.template.config/template.json b/src/ProjectTemplates/Web.ProjectTemplates/content/ComponentsWebAssembly-CSharp/.template.config/template.json index 74be1e1bff6b..a35ba5c57a49 100644 --- a/src/ProjectTemplates/Web.ProjectTemplates/content/ComponentsWebAssembly-CSharp/.template.config/template.json +++ b/src/ProjectTemplates/Web.ProjectTemplates/content/ComponentsWebAssembly-CSharp/.template.config/template.json @@ -211,19 +211,6 @@ "Server/Controllers/OidcConfigurationController.cs", "Server/Models/ApplicationUser.cs" ] - }, - { - "condition": "(Hosted && !GenerateApi)", - "exclude": [ - "Server/Services/DownstreamWebApi.cs" - ] - }, - { - "condition": "(Hosted &&!GenerateGraph)", - "exclude": [ - "Server/Services/MicrosoftGraphServiceExtensions.cs", - "Server/Services/TokenAcquisitionCredentialProvider.cs" - ] } ] } @@ -301,7 +288,7 @@ "SignUpSignInPolicyId": { "type": "parameter", "datatype": "string", - "defaultValue": "", + "defaultValue": "b2c_1_susi", "replaces": "MySignUpSignInPolicyId", "description": "The sign-in and sign-up policy ID for this project (use with IndividualB2C auth)." }, @@ -340,7 +327,7 @@ "type": "parameter", "datatype": "string", "replaces": "api-scope", - "defaultValue": "user_impersonation", + "defaultValue": "access_as_user", "description": "The API scope the client needs to request to provision an access token. (use with IndividualB2C, SingleOrg)." }, "TenantId": { diff --git a/src/ProjectTemplates/Web.ProjectTemplates/content/ComponentsWebAssembly-CSharp/Server/Controllers/WeatherForecastController.cs b/src/ProjectTemplates/Web.ProjectTemplates/content/ComponentsWebAssembly-CSharp/Server/Controllers/WeatherForecastController.cs index 99971dfae8e5..a8cd916c8d9c 100644 --- a/src/ProjectTemplates/Web.ProjectTemplates/content/ComponentsWebAssembly-CSharp/Server/Controllers/WeatherForecastController.cs +++ b/src/ProjectTemplates/Web.ProjectTemplates/content/ComponentsWebAssembly-CSharp/Server/Controllers/WeatherForecastController.cs @@ -15,10 +15,10 @@ using Microsoft.Graph; #endif using Microsoft.AspNetCore.Mvc; +using Microsoft.Extensions.Logging; #if (OrganizationalAuth || IndividualB2CAuth) using Microsoft.Identity.Web.Resource; #endif -using Microsoft.Extensions.Logging; using ComponentsWebAssembly_CSharp.Shared; namespace ComponentsWebAssembly_CSharp.Server.Controllers @@ -37,7 +37,7 @@ public class WeatherForecastController : ControllerBase private readonly ILogger _logger; - // The Web API will only accept tokens 1) for users, and 2) having the api-scope scope for this API + // The Web API will only accept tokens 1) for users, and 2) having the "api-scope" scope for this API static readonly string[] scopeRequiredByApi = new string[] { "api-scope" }; #if (GenerateApi) @@ -55,7 +55,17 @@ public async Task> Get() { HttpContext.VerifyUserHasAnyAcceptedScope(scopeRequiredByApi); - string downstreamApiResult = await _downstreamWebApi.CallWebApiAsync(); + var response = await _downstreamWebApi.CallWebApiForUserAsync("DownstreamApi").ConfigureAwait(false); + if (response.StatusCode == System.Net.HttpStatusCode.OK) + { + string apiResult = await response.Content.ReadAsStringAsync().ConfigureAwait(false); + // Do something + } + else + { + string error = await response.Content.ReadAsStringAsync().ConfigureAwait(false); + throw new HttpRequestException($"Invalid status code in the HttpResponseMessage: {response.StatusCode}: {error}"); + } var rng = new Random(); return Enumerable.Range(1, 5).Select(index => new WeatherForecast @@ -75,7 +85,7 @@ public WeatherForecastController(ILogger logger, { _logger = logger; _graphServiceClient = graphServiceClient; - } + } [HttpGet] public async Task> Get() diff --git a/src/ProjectTemplates/Web.ProjectTemplates/content/ComponentsWebAssembly-CSharp/Server/Services/DownstreamWebApi.cs b/src/ProjectTemplates/Web.ProjectTemplates/content/ComponentsWebAssembly-CSharp/Server/Services/DownstreamWebApi.cs deleted file mode 100644 index 0c7d0fcb9674..000000000000 --- a/src/ProjectTemplates/Web.ProjectTemplates/content/ComponentsWebAssembly-CSharp/Server/Services/DownstreamWebApi.cs +++ /dev/null @@ -1,72 +0,0 @@ -using System.Net; -using System.Net.Http; -using System.Threading.Tasks; -using Microsoft.Extensions.Configuration; -using Microsoft.Extensions.DependencyInjection; -using Microsoft.Identity.Web; - -namespace ComponentsWebAssembly_CSharp.Server -{ - public interface IDownstreamWebApi - { - Task CallWebApiAsync(string relativeEndpoint = "", string[] requiredScopes = null); - } - - public static class DownstreamWebApiExtensions - { - public static void AddDownstreamWebApiService(this IServiceCollection services, IConfiguration configuration) - { - // https://docs.microsoft.com/en-us/dotnet/standard/microservices-architecture/implement-resilient-applications/use-httpclientfactory-to-implement-resilient-http-requests - services.AddHttpClient(); - } - } - - public class DownstreamWebApi : IDownstreamWebApi - { - private readonly ITokenAcquisition _tokenAcquisition; - - private readonly IConfiguration _configuration; - - private readonly HttpClient _httpClient; - - public DownstreamWebApi( - ITokenAcquisition tokenAcquisition, - IConfiguration configuration, - HttpClient httpClient) - { - _tokenAcquisition = tokenAcquisition; - _configuration = configuration; - _httpClient = httpClient; - } - - /// - /// Calls the Web API with the required scopes - /// - /// [Optional] Scopes required to call the Web API. If - /// not specified, uses scopes from the configuration - /// Endpoint relative to the CalledApiUrl configuration - /// A JSON string representing the result of calling the Web API - public async Task CallWebApiAsync(string relativeEndpoint = "", string[] requiredScopes = null) - { - string[] scopes = requiredScopes ?? _configuration["CalledApi:CalledApiScopes"]?.Split(' '); - string apiUrl = (_configuration["CalledApi:CalledApiUrl"] as string)?.TrimEnd('/') + $"/{relativeEndpoint}"; - - string accessToken = await _tokenAcquisition.GetAccessTokenForUserAsync(scopes); - HttpRequestMessage httpRequestMessage = new HttpRequestMessage(HttpMethod.Get, apiUrl); - httpRequestMessage.Headers.Add("Authorization", $"bearer {accessToken}"); - - string apiResult; - var response = await _httpClient.SendAsync(httpRequestMessage); - if (response.StatusCode == HttpStatusCode.OK) - { - apiResult = await response.Content.ReadAsStringAsync(); - } - else - { - apiResult = $"Error calling the API '{apiUrl}'"; - } - - return apiResult; - } - } -} diff --git a/src/ProjectTemplates/Web.ProjectTemplates/content/ComponentsWebAssembly-CSharp/Server/Services/MicrosoftGraphServiceExtensions.cs b/src/ProjectTemplates/Web.ProjectTemplates/content/ComponentsWebAssembly-CSharp/Server/Services/MicrosoftGraphServiceExtensions.cs deleted file mode 100644 index 6702cc33714f..000000000000 --- a/src/ProjectTemplates/Web.ProjectTemplates/content/ComponentsWebAssembly-CSharp/Server/Services/MicrosoftGraphServiceExtensions.cs +++ /dev/null @@ -1,36 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using Microsoft.Extensions.Configuration; -using Microsoft.Extensions.DependencyInjection; -using Microsoft.Graph; -using Microsoft.Identity.Web; - -namespace ComponentsWebAssembly_CSharp.Server -{ - public static class MicrosoftGraphServiceExtensions - { - /// - /// Adds the Microsoft Graph client as a singleton. - /// - /// Service collection. - /// Initial scopes. - /// Base URL for Microsoft graph. This can be - /// changed for instance for applications running in national clouds - public static IServiceCollection AddMicrosoftGraph(this IServiceCollection services, - IEnumerable initialScopes, - string graphBaseUrl = "https://graph.microsoft.com/v1.0") - { - services.AddTokenAcquisition(true); - services.AddSingleton(serviceProvider => - { - var tokenAquisitionService = serviceProvider.GetService(); - GraphServiceClient client = string.IsNullOrWhiteSpace(graphBaseUrl) ? - new GraphServiceClient(new TokenAcquisitionCredentialProvider(tokenAquisitionService, initialScopes)) : - new GraphServiceClient(graphBaseUrl, new TokenAcquisitionCredentialProvider(tokenAquisitionService, initialScopes)); - return client; - }); - return services; - } - } -} diff --git a/src/ProjectTemplates/Web.ProjectTemplates/content/ComponentsWebAssembly-CSharp/Server/Services/TokenAcquisitionCredentialProvider.cs b/src/ProjectTemplates/Web.ProjectTemplates/content/ComponentsWebAssembly-CSharp/Server/Services/TokenAcquisitionCredentialProvider.cs deleted file mode 100644 index a6cc2b080ec4..000000000000 --- a/src/ProjectTemplates/Web.ProjectTemplates/content/ComponentsWebAssembly-CSharp/Server/Services/TokenAcquisitionCredentialProvider.cs +++ /dev/null @@ -1,27 +0,0 @@ -using Microsoft.Graph; -using Microsoft.Identity.Web; -using System.Collections; -using System.Collections.Generic; -using System.Net.Http; -using System.Threading.Tasks; - -namespace ComponentsWebAssembly_CSharp.Server -{ - internal class TokenAcquisitionCredentialProvider : IAuthenticationProvider - { - public TokenAcquisitionCredentialProvider(ITokenAcquisition tokenAcquisition, IEnumerable initialScopes) - { - _tokenAcquisition = tokenAcquisition; - _initialScopes = initialScopes; - } - - ITokenAcquisition _tokenAcquisition; - IEnumerable _initialScopes; - - public async Task AuthenticateRequestAsync(HttpRequestMessage request) - { - request.Headers.Add("Authorization", - $"Bearer {await _tokenAcquisition.GetAccessTokenForUserAsync(_initialScopes)}"); - } - } -} diff --git a/src/ProjectTemplates/Web.ProjectTemplates/content/ComponentsWebAssembly-CSharp/Server/Startup.cs b/src/ProjectTemplates/Web.ProjectTemplates/content/ComponentsWebAssembly-CSharp/Server/Startup.cs index 80c2560a8ef3..d0ffb393ea7a 100644 --- a/src/ProjectTemplates/Web.ProjectTemplates/content/ComponentsWebAssembly-CSharp/Server/Startup.cs +++ b/src/ProjectTemplates/Web.ProjectTemplates/content/ComponentsWebAssembly-CSharp/Server/Startup.cs @@ -3,8 +3,8 @@ #endif using Microsoft.AspNetCore.Builder; #if (OrganizationalAuth || IndividualB2CAuth) +using Microsoft.AspNetCore.Authentication.JwtBearer; using Microsoft.Identity.Web; -using Microsoft.Identity.Web.TokenCacheProviders.InMemory; #endif #if (RequiresHttps) using Microsoft.AspNetCore.HttpsPolicy; @@ -63,31 +63,29 @@ public void ConfigureServices(IServiceCollection services) .AddIdentityServerJwt(); #endif #if (OrganizationalAuth) + services.AddAuthentication(JwtBearerDefaults.AuthenticationScheme) + .AddMicrosoftIdentityWebApi(Configuration.GetSection("AzureAd")) #if (GenerateApiOrGraph) - // Adds Microsoft Identity platform (AAD v2.0) support to protect this Api - services.AddMicrosoftWebApiAuthentication(Configuration, "AzureAd") - .AddMicrosoftWebApiCallsWebApi(Configuration, "AzureAd") - .AddInMemoryTokenCaches(); -#else - // Adds Microsoft Identity platform (AAD v2.0) support to protect this Api - services.AddMicrosoftWebApiAuthentication(Configuration, "AzureAd"); -#endif + .EnableTokenAcquisitionToCallDownstreamApi() #if (GenerateApi) - services.AddDownstreamWebApiService(Configuration); + .AddDownstreamWebApi("DownstreamApi", Configuration.GetSection("DownstreamApi")) #endif #if (GenerateGraph) - services.AddMicrosoftGraph(Configuration.GetValue("CalledApi:CalledApiScopes")?.Split(' '), - Configuration.GetValue("CalledApi:CalledApiUrl")); + .AddMicrosoftGraph(Configuration.GetSection("DownstreamApi")) +#endif + .AddInMemoryTokenCaches(); +#else + ; #endif #elif (IndividualB2CAuth) + services.AddAuthentication(JwtBearerDefaults.AuthenticationScheme) + .AddMicrosoftIdentityWebApi(Configuration.GetSection("AzureAdB2C")) #if (GenerateApi) - services.AddMicrosoftWebApiAuthentication(Configuration, "AzureAdB2C") - .AddMicrosoftWebApiCallsWebApi(Configuration, "AzureAdB2C") - .AddInMemoryTokenCaches(); - - services.AddDownstreamWebApiService(Configuration); + .EnableTokenAcquisitionToCallDownstreamApi() + .AddDownstreamWebApi("DownstreamApi", Configuration.GetSection("DownstreamApi")) + .AddInMemoryTokenCaches(); #else - services.AddMicrosoftWebApiAuthentication(Configuration, "AzureAdB2C"); + ; #endif #endif diff --git a/src/ProjectTemplates/Web.ProjectTemplates/content/ComponentsWebAssembly-CSharp/Server/appsettings.json b/src/ProjectTemplates/Web.ProjectTemplates/content/ComponentsWebAssembly-CSharp/Server/appsettings.json index da1c94c1b9a7..b5f991734b9f 100644 --- a/src/ProjectTemplates/Web.ProjectTemplates/content/ComponentsWebAssembly-CSharp/Server/appsettings.json +++ b/src/ProjectTemplates/Web.ProjectTemplates/content/ComponentsWebAssembly-CSharp/Server/appsettings.json @@ -21,10 +21,10 @@ // }, ////#elseif (OrganizationalAuth) // "AzureAd": { +// "Instance": "https:////login.microsoftonline.com/", //#if (!SingleOrgAuth) -// "Instance": "https:////login.microsoftonline.com/common", +// "TenantId": "common", //#else -// "Instance": "https:////login.microsoftonline.com/", // "Domain": "qualified.domain.name", // "TenantId": "22222222-2222-2222-2222-222222222222", //#endif @@ -38,16 +38,16 @@ // }, ////#endif ////#if (GenerateApiOrGraph) -// "CalledApi": { +// "DownstreamAPI": { // /* -// 'CalledApiScopes' contains space separated scopes of the Web API you want to call. This can be: +// 'Scopes' contains space separated scopes of the Web API you want to call. This can be: // - a scope for a V2 application (for instance api://b3682cc7-8b30-4bd2-aaba-080c6bf0fd31/access_as_user) // - a scope corresponding to a V1 application (for instance /.default, where is the // App ID URI of a legacy v1 Web application // Applications are registered in the https://portal.azure.com portal. // */ -// "CalledApiScopes": "user.read", -// "CalledApiUrl": "[WebApiUrl]" +// "BaseUrl": "[WebApiUrl]", +// "Scopes": "user.read" // }, ////#endif "Logging": { diff --git a/src/ProjectTemplates/Web.ProjectTemplates/content/RazorPagesWeb-CSharp/.template.config/template.json b/src/ProjectTemplates/Web.ProjectTemplates/content/RazorPagesWeb-CSharp/.template.config/template.json index f939274638ce..a13d60b66a0f 100644 --- a/src/ProjectTemplates/Web.ProjectTemplates/content/RazorPagesWeb-CSharp/.template.config/template.json +++ b/src/ProjectTemplates/Web.ProjectTemplates/content/RazorPagesWeb-CSharp/.template.config/template.json @@ -94,19 +94,6 @@ "exclude": [ "Data/SqlServer/**" ] - }, - { - "condition": "(!GenerateApi)", - "exclude": [ - "Services/DownstreamWebApi.cs" - ] - }, - { - "condition": "(!GenerateGraph)", - "exclude": [ - "Services/MicrosoftGraphServiceExtensions.cs", - "Services/TokenAcquisitionCredentialProvider.cs" - ] } ] } diff --git a/src/ProjectTemplates/Web.ProjectTemplates/content/RazorPagesWeb-CSharp/Pages/Index.cshtml.cs b/src/ProjectTemplates/Web.ProjectTemplates/content/RazorPagesWeb-CSharp/Pages/Index.cshtml.cs index bac64933452b..dfb724c5a48e 100644 --- a/src/ProjectTemplates/Web.ProjectTemplates/content/RazorPagesWeb-CSharp/Pages/Index.cshtml.cs +++ b/src/ProjectTemplates/Web.ProjectTemplates/content/RazorPagesWeb-CSharp/Pages/Index.cshtml.cs @@ -18,7 +18,7 @@ namespace Company.WebApplication1.Pages { #if (GenerateApiOrGraph) - [AuthorizeForScopes(ScopeKeySection = "CalledApi:CalledApiScopes")] + [AuthorizeForScopes(ScopeKeySection = "DownstreamApi:Scopes")] #endif public class IndexModel : PageModel { @@ -36,7 +36,17 @@ public IndexModel(ILogger logger, public async Task OnGet() { - ViewData["ApiResult"] = await _downstreamWebApi.CallWebApiAsync(); + var response = await _downstreamWebApi.CallWebApiForUserAsync("DownstreamApi").ConfigureAwait(false); + if (response.StatusCode == System.Net.HttpStatusCode.OK) + { + string apiResult = await response.Content.ReadAsStringAsync().ConfigureAwait(false); + ViewData["ApiResult"] = apiResult; + } + else + { + string error = await response.Content.ReadAsStringAsync().ConfigureAwait(false); + throw new HttpRequestException($"Invalid status code in the HttpResponseMessage: {response.StatusCode}: {error}"); + } } #elseif (GenerateGraph) private readonly GraphServiceClient _graphServiceClient; diff --git a/src/ProjectTemplates/Web.ProjectTemplates/content/RazorPagesWeb-CSharp/Services/DownstreamWebApi.cs b/src/ProjectTemplates/Web.ProjectTemplates/content/RazorPagesWeb-CSharp/Services/DownstreamWebApi.cs deleted file mode 100644 index 4806ca192d9e..000000000000 --- a/src/ProjectTemplates/Web.ProjectTemplates/content/RazorPagesWeb-CSharp/Services/DownstreamWebApi.cs +++ /dev/null @@ -1,72 +0,0 @@ -using System.Net; -using System.Net.Http; -using System.Threading.Tasks; -using Microsoft.Extensions.Configuration; -using Microsoft.Extensions.DependencyInjection; -using Microsoft.Identity.Web; - -namespace Company.WebApplication1 -{ - public interface IDownstreamWebApi - { - Task CallWebApiAsync(string relativeEndpoint = "", string[] requiredScopes = null); - } - - public static class DownstreamWebApiExtensions - { - public static void AddDownstreamWebApiService(this IServiceCollection services, IConfiguration configuration) - { - // https://docs.microsoft.com/en-us/dotnet/standard/microservices-architecture/implement-resilient-applications/use-httpclientfactory-to-implement-resilient-http-requests - services.AddHttpClient(); - } - } - - public class DownstreamWebApi : IDownstreamWebApi - { - private readonly ITokenAcquisition _tokenAcquisition; - - private readonly IConfiguration _configuration; - - private readonly HttpClient _httpClient; - - public DownstreamWebApi( - ITokenAcquisition tokenAcquisition, - IConfiguration configuration, - HttpClient httpClient) - { - _tokenAcquisition = tokenAcquisition; - _configuration = configuration; - _httpClient = httpClient; - } - - /// - /// Calls the Web API with the required scopes - /// - /// [Optional] Scopes required to call the Web API. If - /// not specified, uses scopes from the configuration - /// Endpoint relative to the CalledApiUrl configuration - /// A JSON string representing the result of calling the Web API - public async Task CallWebApiAsync(string relativeEndpoint = "", string[] requiredScopes = null) - { - string[] scopes = requiredScopes ?? _configuration["CalledApi:CalledApiScopes"]?.Split(' '); - string apiUrl = (_configuration["CalledApi:CalledApiUrl"] as string)?.TrimEnd('/') + $"/{relativeEndpoint}"; - - string accessToken = await _tokenAcquisition.GetAccessTokenForUserAsync(scopes); - HttpRequestMessage httpRequestMessage = new HttpRequestMessage(HttpMethod.Get, apiUrl); - httpRequestMessage.Headers.Add("Authorization", $"bearer {accessToken}"); - - string apiResult; - var response = await _httpClient.SendAsync(httpRequestMessage); - if (response.StatusCode == HttpStatusCode.OK) - { - apiResult = await response.Content.ReadAsStringAsync(); - } - else - { - apiResult = $"Error calling the API '{apiUrl}'"; - } - - return apiResult; - } - } -} diff --git a/src/ProjectTemplates/Web.ProjectTemplates/content/RazorPagesWeb-CSharp/Services/MicrosoftGraphServiceExtensions.cs b/src/ProjectTemplates/Web.ProjectTemplates/content/RazorPagesWeb-CSharp/Services/MicrosoftGraphServiceExtensions.cs deleted file mode 100644 index 2e805a968890..000000000000 --- a/src/ProjectTemplates/Web.ProjectTemplates/content/RazorPagesWeb-CSharp/Services/MicrosoftGraphServiceExtensions.cs +++ /dev/null @@ -1,37 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using Microsoft.Extensions.Configuration; -using Microsoft.Extensions.DependencyInjection; -using Microsoft.Graph; -using Microsoft.Identity.Web; - -namespace Company.WebApplication1 -{ - public static class MicrosoftGraphServiceExtensions - { - /// - /// Adds the Microsoft Graph client as a singleton. - /// - /// Service collection. - /// Initial scopes. - /// Base URL for Microsoft graph. This can be - /// changed for instance for applications running in national clouds - public static IServiceCollection AddMicrosoftGraph(this IServiceCollection services, - IEnumerable initialScopes, - string graphBaseUrl = "https://graph.microsoft.com/v1.0") - { - services.AddTokenAcquisition(true); - services.AddSingleton(serviceProvider => - { - var tokenAquisitionService = serviceProvider.GetService(); - GraphServiceClient client = string.IsNullOrWhiteSpace(graphBaseUrl) ? - new GraphServiceClient(new TokenAcquisitionCredentialProvider(tokenAquisitionService, initialScopes)) : - new GraphServiceClient(graphBaseUrl, new TokenAcquisitionCredentialProvider(tokenAquisitionService, initialScopes)); - return client; - }); - - return services; - } - } -} diff --git a/src/ProjectTemplates/Web.ProjectTemplates/content/RazorPagesWeb-CSharp/Services/TokenAcquisitionCredentialProvider.cs b/src/ProjectTemplates/Web.ProjectTemplates/content/RazorPagesWeb-CSharp/Services/TokenAcquisitionCredentialProvider.cs deleted file mode 100644 index 8042149d82f8..000000000000 --- a/src/ProjectTemplates/Web.ProjectTemplates/content/RazorPagesWeb-CSharp/Services/TokenAcquisitionCredentialProvider.cs +++ /dev/null @@ -1,27 +0,0 @@ -using Microsoft.Graph; -using Microsoft.Identity.Web; -using System.Collections; -using System.Collections.Generic; -using System.Net.Http; -using System.Threading.Tasks; - -namespace Company.WebApplication1 -{ - internal class TokenAcquisitionCredentialProvider : IAuthenticationProvider - { - public TokenAcquisitionCredentialProvider(ITokenAcquisition tokenAcquisition, IEnumerable initialScopes) - { - _tokenAcquisition = tokenAcquisition; - _initialScopes = initialScopes; - } - - ITokenAcquisition _tokenAcquisition; - IEnumerable _initialScopes; - - public async Task AuthenticateRequestAsync(HttpRequestMessage request) - { - request.Headers.Add("Authorization", - $"Bearer {await _tokenAcquisition.GetAccessTokenForUserAsync(_initialScopes)}"); - } - } -} diff --git a/src/ProjectTemplates/Web.ProjectTemplates/content/RazorPagesWeb-CSharp/Startup.cs b/src/ProjectTemplates/Web.ProjectTemplates/content/RazorPagesWeb-CSharp/Startup.cs index c181d5a49c96..c7cfada6be11 100644 --- a/src/ProjectTemplates/Web.ProjectTemplates/content/RazorPagesWeb-CSharp/Startup.cs +++ b/src/ProjectTemplates/Web.ProjectTemplates/content/RazorPagesWeb-CSharp/Startup.cs @@ -4,21 +4,11 @@ using System.Threading.Tasks; #if (OrganizationalAuth || IndividualB2CAuth) using Microsoft.AspNetCore.Authentication; -#endif -#if (OrganizationalAuth) using Microsoft.Identity.Web; using Microsoft.Identity.Web.UI; -using Microsoft.Identity.Web.TokenCacheProviders.InMemory; -#if (MultiOrgAuth) using Microsoft.AspNetCore.Authentication.OpenIdConnect; -#endif using Microsoft.AspNetCore.Authorization; #endif -#if (IndividualB2CAuth) -using Microsoft.Identity.Web; -using Microsoft.Identity.Web.UI; -using Microsoft.Identity.Web.TokenCacheProviders.InMemory; -#endif using Microsoft.AspNetCore.Builder; #if (IndividualLocalAuth) using Microsoft.AspNetCore.Identity; @@ -73,28 +63,36 @@ public void ConfigureServices(IServiceCollection services) .AddEntityFrameworkStores(); #elif (OrganizationalAuth) #if (GenerateApiOrGraph) - services.AddMicrosoftWebAppAuthentication(Configuration, "AzureAd") - .AddMicrosoftWebAppCallsWebApi(Configuration, "AzureAd") - .AddInMemoryTokenCaches(); -#else - services.AddMicrosoftWebAppAuthentication(Configuration, "AzureAd"); + string[] initialScopes = Configuration.GetValue("DownstreamApi:Scopes")?.Split(' '); + #endif + services.AddAuthentication(OpenIdConnectDefaults.AuthenticationScheme) + .AddMicrosoftIdentityWebApp(Configuration.GetSection("AzureAd")) +#if (GenerateApiOrGraph) + .EnableTokenAcquisitionToCallDownstreamApi(initialScopes) #if (GenerateApi) - services.AddDownstreamWebApiService(Configuration); + .AddDownstreamWebApi("DownstreamApi", Configuration.GetSection("DownstreamApi")) #endif #if (GenerateGraph) - services.AddMicrosoftGraph(Configuration.GetValue("CalledApi:CalledApiScopes")?.Split(' '), - Configuration.GetValue("CalledApi:CalledApiUrl")); + .AddMicrosoftGraph(Configuration.GetSection("DownstreamApi")) +#endif + .AddInMemoryTokenCaches(); +#else + ; #endif #elif (IndividualB2CAuth) #if (GenerateApi) - services.AddMicrosoftWebAppAuthentication(Configuration, "AzureAdB2C") - .AddMicrosoftWebAppCallsWebApi(Configuration, "AzureAdB2C") - .AddInMemoryTokenCaches(); + string[] initialScopes = Configuration.GetValue("DownstreamApi:Scopes")?.Split(' '); - services.AddDownstreamWebApiService(Configuration); +#endif + services.AddAuthentication(OpenIdConnectDefaults.AuthenticationScheme) + .AddMicrosoftIdentityWebApp(Configuration.GetSection("AzureAdB2C")) +#if (GenerateApi) + .EnableTokenAcquisitionToCallDownstreamApi(initialScopes) + .AddDownstreamWebApi("DownstreamApi", Configuration.GetSection("DownstreamApi")) + .AddInMemoryTokenCaches(); #else - services.AddMicrosoftWebAppAuthentication(Configuration, "AzureAdB2C"); + ; #endif #endif #if (OrganizationalAuth) @@ -105,11 +103,11 @@ public void ConfigureServices(IServiceCollection services) options.FallbackPolicy = options.DefaultPolicy; }); services.AddRazorPages() - .AddMvcOptions(options => {}) - .AddMicrosoftIdentityUI(); + .AddMvcOptions(options => {}) + .AddMicrosoftIdentityUI(); #elif (IndividualB2CAuth) services.AddRazorPages() - .AddMicrosoftIdentityUI(); + .AddMicrosoftIdentityUI(); #else services.AddRazorPages(); #endif diff --git a/src/ProjectTemplates/Web.ProjectTemplates/content/RazorPagesWeb-CSharp/appsettings.json b/src/ProjectTemplates/Web.ProjectTemplates/content/RazorPagesWeb-CSharp/appsettings.json index c0dd4d1ab9c1..634fb6db8a6d 100644 --- a/src/ProjectTemplates/Web.ProjectTemplates/content/RazorPagesWeb-CSharp/appsettings.json +++ b/src/ProjectTemplates/Web.ProjectTemplates/content/RazorPagesWeb-CSharp/appsettings.json @@ -17,10 +17,10 @@ // }, ////#elseif (OrganizationalAuth) // "AzureAd": { +// "Instance": "https:////login.microsoftonline.com/", //#if (MultiOrgAuth) -// "Instance": "https:////login.microsoftonline.com/common", +// "TenantId": "common", //#elseif (SingleOrgAuth) -// "Instance": "https:////login.microsoftonline.com/", // "Domain": "qualified.domain.name", // "TenantId": "22222222-2222-2222-2222-222222222222", //#endif @@ -34,16 +34,16 @@ // }, ////#endif ////#if (GenerateApiOrGraph) -// "CalledApi": { +// "DownstreamApi": { // /* -// 'CalledApiScopes' contains space separated scopes of the Web API you want to call. This can be: +// 'Scopes' contains space separated scopes of the Web API you want to call. This can be: // - a scope for a V2 application (for instance api://b3682cc7-8b30-4bd2-aaba-080c6bf0fd31/access_as_user) // - a scope corresponding to a V1 application (for instance /.default, where is the // App ID URI of a legacy v1 Web application // Applications are registered in the https://portal.azure.com portal. // */ -// "CalledApiScopes": "user.read", -// "CalledApiUrl": "[WebApiUrl]" +// "BaseUrl": "[WebApiUrl]", +// "Scopes": "user.read" // }, ////#endif ////#if (IndividualLocalAuth) diff --git a/src/ProjectTemplates/Web.ProjectTemplates/content/StarterWeb-CSharp/.template.config/template.json b/src/ProjectTemplates/Web.ProjectTemplates/content/StarterWeb-CSharp/.template.config/template.json index f0e86147b2a8..ac54ee3f914d 100644 --- a/src/ProjectTemplates/Web.ProjectTemplates/content/StarterWeb-CSharp/.template.config/template.json +++ b/src/ProjectTemplates/Web.ProjectTemplates/content/StarterWeb-CSharp/.template.config/template.json @@ -90,19 +90,6 @@ "exclude": [ "Data/SqlServer/**" ] - }, - { - "condition": "(!GenerateApi)", - "exclude": [ - "Services/DownstreamWebApi.cs" - ] - }, - { - "condition": "(!GenerateGraph)", - "exclude": [ - "Services/MicrosoftGraphServiceExtensions.cs", - "Services/TokenAcquisitionCredentialProvider.cs" - ] } ] } diff --git a/src/ProjectTemplates/Web.ProjectTemplates/content/StarterWeb-CSharp/Controllers/HomeController.cs b/src/ProjectTemplates/Web.ProjectTemplates/content/StarterWeb-CSharp/Controllers/HomeController.cs index cd05892b06bb..366fba743e6a 100644 --- a/src/ProjectTemplates/Web.ProjectTemplates/content/StarterWeb-CSharp/Controllers/HomeController.cs +++ b/src/ProjectTemplates/Web.ProjectTemplates/content/StarterWeb-CSharp/Controllers/HomeController.cs @@ -38,11 +38,20 @@ public HomeController(ILogger logger, _downstreamWebApi = downstreamWebApi; } - [AuthorizeForScopes(ScopeKeySection = "CalledApi:CalledApiScopes")] + [AuthorizeForScopes(ScopeKeySection = "DownstreamApi:Scopes")] public async Task Index() { - ViewData["ApiResult"] = await _downstreamWebApi.CallWebApiAsync(); - + var response = await _downstreamWebApi.CallWebApiForUserAsync("DownstreamApi").ConfigureAwait(false); + if (response.StatusCode == System.Net.HttpStatusCode.OK) + { + string apiResult = await response.Content.ReadAsStringAsync().ConfigureAwait(false); + ViewData["ApiResult"] = apiResult; + } + else + { + string error = await response.Content.ReadAsStringAsync().ConfigureAwait(false); + throw new HttpRequestException($"Invalid status code in the HttpResponseMessage: {response.StatusCode}: {error}"); + } return View(); } #elseif (GenerateGraph) @@ -55,7 +64,7 @@ public HomeController(ILogger logger, _graphServiceClient = graphServiceClient; } - [AuthorizeForScopes(ScopeKeySection = "CalledApi:CalledApiScopes")] + [AuthorizeForScopes(ScopeKeySection = "DownstreamApi:Scopes")] public async Task Index() { var user = await _graphServiceClient.Me.Request().GetAsync(); diff --git a/src/ProjectTemplates/Web.ProjectTemplates/content/StarterWeb-CSharp/Services/DownstreamWebApi.cs b/src/ProjectTemplates/Web.ProjectTemplates/content/StarterWeb-CSharp/Services/DownstreamWebApi.cs deleted file mode 100644 index 4806ca192d9e..000000000000 --- a/src/ProjectTemplates/Web.ProjectTemplates/content/StarterWeb-CSharp/Services/DownstreamWebApi.cs +++ /dev/null @@ -1,72 +0,0 @@ -using System.Net; -using System.Net.Http; -using System.Threading.Tasks; -using Microsoft.Extensions.Configuration; -using Microsoft.Extensions.DependencyInjection; -using Microsoft.Identity.Web; - -namespace Company.WebApplication1 -{ - public interface IDownstreamWebApi - { - Task CallWebApiAsync(string relativeEndpoint = "", string[] requiredScopes = null); - } - - public static class DownstreamWebApiExtensions - { - public static void AddDownstreamWebApiService(this IServiceCollection services, IConfiguration configuration) - { - // https://docs.microsoft.com/en-us/dotnet/standard/microservices-architecture/implement-resilient-applications/use-httpclientfactory-to-implement-resilient-http-requests - services.AddHttpClient(); - } - } - - public class DownstreamWebApi : IDownstreamWebApi - { - private readonly ITokenAcquisition _tokenAcquisition; - - private readonly IConfiguration _configuration; - - private readonly HttpClient _httpClient; - - public DownstreamWebApi( - ITokenAcquisition tokenAcquisition, - IConfiguration configuration, - HttpClient httpClient) - { - _tokenAcquisition = tokenAcquisition; - _configuration = configuration; - _httpClient = httpClient; - } - - /// - /// Calls the Web API with the required scopes - /// - /// [Optional] Scopes required to call the Web API. If - /// not specified, uses scopes from the configuration - /// Endpoint relative to the CalledApiUrl configuration - /// A JSON string representing the result of calling the Web API - public async Task CallWebApiAsync(string relativeEndpoint = "", string[] requiredScopes = null) - { - string[] scopes = requiredScopes ?? _configuration["CalledApi:CalledApiScopes"]?.Split(' '); - string apiUrl = (_configuration["CalledApi:CalledApiUrl"] as string)?.TrimEnd('/') + $"/{relativeEndpoint}"; - - string accessToken = await _tokenAcquisition.GetAccessTokenForUserAsync(scopes); - HttpRequestMessage httpRequestMessage = new HttpRequestMessage(HttpMethod.Get, apiUrl); - httpRequestMessage.Headers.Add("Authorization", $"bearer {accessToken}"); - - string apiResult; - var response = await _httpClient.SendAsync(httpRequestMessage); - if (response.StatusCode == HttpStatusCode.OK) - { - apiResult = await response.Content.ReadAsStringAsync(); - } - else - { - apiResult = $"Error calling the API '{apiUrl}'"; - } - - return apiResult; - } - } -} diff --git a/src/ProjectTemplates/Web.ProjectTemplates/content/StarterWeb-CSharp/Services/MicrosoftGraphServiceExtensions.cs b/src/ProjectTemplates/Web.ProjectTemplates/content/StarterWeb-CSharp/Services/MicrosoftGraphServiceExtensions.cs deleted file mode 100644 index 2e805a968890..000000000000 --- a/src/ProjectTemplates/Web.ProjectTemplates/content/StarterWeb-CSharp/Services/MicrosoftGraphServiceExtensions.cs +++ /dev/null @@ -1,37 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using Microsoft.Extensions.Configuration; -using Microsoft.Extensions.DependencyInjection; -using Microsoft.Graph; -using Microsoft.Identity.Web; - -namespace Company.WebApplication1 -{ - public static class MicrosoftGraphServiceExtensions - { - /// - /// Adds the Microsoft Graph client as a singleton. - /// - /// Service collection. - /// Initial scopes. - /// Base URL for Microsoft graph. This can be - /// changed for instance for applications running in national clouds - public static IServiceCollection AddMicrosoftGraph(this IServiceCollection services, - IEnumerable initialScopes, - string graphBaseUrl = "https://graph.microsoft.com/v1.0") - { - services.AddTokenAcquisition(true); - services.AddSingleton(serviceProvider => - { - var tokenAquisitionService = serviceProvider.GetService(); - GraphServiceClient client = string.IsNullOrWhiteSpace(graphBaseUrl) ? - new GraphServiceClient(new TokenAcquisitionCredentialProvider(tokenAquisitionService, initialScopes)) : - new GraphServiceClient(graphBaseUrl, new TokenAcquisitionCredentialProvider(tokenAquisitionService, initialScopes)); - return client; - }); - - return services; - } - } -} diff --git a/src/ProjectTemplates/Web.ProjectTemplates/content/StarterWeb-CSharp/Services/TokenAcquisitionCredentialProvider.cs b/src/ProjectTemplates/Web.ProjectTemplates/content/StarterWeb-CSharp/Services/TokenAcquisitionCredentialProvider.cs deleted file mode 100644 index 8042149d82f8..000000000000 --- a/src/ProjectTemplates/Web.ProjectTemplates/content/StarterWeb-CSharp/Services/TokenAcquisitionCredentialProvider.cs +++ /dev/null @@ -1,27 +0,0 @@ -using Microsoft.Graph; -using Microsoft.Identity.Web; -using System.Collections; -using System.Collections.Generic; -using System.Net.Http; -using System.Threading.Tasks; - -namespace Company.WebApplication1 -{ - internal class TokenAcquisitionCredentialProvider : IAuthenticationProvider - { - public TokenAcquisitionCredentialProvider(ITokenAcquisition tokenAcquisition, IEnumerable initialScopes) - { - _tokenAcquisition = tokenAcquisition; - _initialScopes = initialScopes; - } - - ITokenAcquisition _tokenAcquisition; - IEnumerable _initialScopes; - - public async Task AuthenticateRequestAsync(HttpRequestMessage request) - { - request.Headers.Add("Authorization", - $"Bearer {await _tokenAcquisition.GetAccessTokenForUserAsync(_initialScopes)}"); - } - } -} diff --git a/src/ProjectTemplates/Web.ProjectTemplates/content/StarterWeb-CSharp/Startup.cs b/src/ProjectTemplates/Web.ProjectTemplates/content/StarterWeb-CSharp/Startup.cs index f0d19f071267..1b5371c74b17 100644 --- a/src/ProjectTemplates/Web.ProjectTemplates/content/StarterWeb-CSharp/Startup.cs +++ b/src/ProjectTemplates/Web.ProjectTemplates/content/StarterWeb-CSharp/Startup.cs @@ -4,20 +4,10 @@ using System.Threading.Tasks; #if (OrganizationalAuth || IndividualB2CAuth) using Microsoft.AspNetCore.Authentication; -#endif -#if (OrganizationalAuth) -using Microsoft.Identity.Web; -using Microsoft.Identity.Web.UI; -using Microsoft.Identity.Web.TokenCacheProviders.InMemory; -#if (MultiOrgAuth) using Microsoft.AspNetCore.Authentication.OpenIdConnect; -#endif -using Microsoft.AspNetCore.Authorization; -#endif -#if (IndividualB2CAuth) using Microsoft.Identity.Web; using Microsoft.Identity.Web.UI; -using Microsoft.Identity.Web.TokenCacheProviders.InMemory; +using Microsoft.AspNetCore.Authorization; #endif using Microsoft.AspNetCore.Builder; #if (IndividualLocalAuth) @@ -74,28 +64,36 @@ public void ConfigureServices(IServiceCollection services) .AddEntityFrameworkStores(); #elif (OrganizationalAuth) #if (GenerateApiOrGraph) - services.AddMicrosoftWebAppAuthentication(Configuration, "AzureAd") - .AddMicrosoftWebAppCallsWebApi(Configuration, "AzureAd") - .AddInMemoryTokenCaches(); -#else - services.AddMicrosoftWebAppAuthentication(Configuration, "AzureAd"); + string[] initialScopes = Configuration.GetValue("DownstreamApi:Scopes")?.Split(' '); + #endif + services.AddAuthentication(OpenIdConnectDefaults.AuthenticationScheme) + .AddMicrosoftIdentityWebApp(Configuration.GetSection("AzureAd")) +#if (GenerateApiOrGraph) + .EnableTokenAcquisitionToCallDownstreamApi(initialScopes) #if (GenerateApi) - services.AddDownstreamWebApiService(Configuration); + .AddDownstreamWebApi("DownstreamApi", Configuration.GetSection("DownstreamApi")) #endif #if (GenerateGraph) - services.AddMicrosoftGraph(Configuration.GetValue("CalledApi:CalledApiScopes")?.Split(' '), - Configuration.GetValue("CalledApi:CalledApiUrl")); + .AddMicrosoftGraph(Configuration.GetSection("DownstreamApi")) +#endif + .AddInMemoryTokenCaches(); +#else + ; #endif #elif (IndividualB2CAuth) #if (GenerateApi) - services.AddMicrosoftWebAppAuthentication(Configuration, "AzureAdB2C") - .AddMicrosoftWebAppCallsWebApi(Configuration, "AzureAdB2C") - .AddInMemoryTokenCaches(); + string[] initialScopes = Configuration.GetValue("DownstreamApi:Scopes")?.Split(' '); - services.AddDownstreamWebApiService(Configuration); +#endif + services.AddAuthentication(OpenIdConnectDefaults.AuthenticationScheme) + .AddMicrosoftIdentityWebApp(Configuration.GetSection("AzureAdB2C")) +#if (GenerateApi) + .EnableTokenAcquisitionToCallDownstreamApi(initialScopes) + .AddDownstreamWebApi("DownstreamApi", Configuration.GetSection("DownstreamApi")) + .AddInMemoryTokenCaches(); #else - services.AddMicrosoftWebAppAuthentication(Configuration, "AzureAdB2C"); + ; #endif #endif #if (OrganizationalAuth) @@ -112,7 +110,7 @@ public void ConfigureServices(IServiceCollection services) #endif #if (OrganizationalAuth || IndividualB2CAuth) services.AddRazorPages() - .AddMicrosoftIdentityUI(); + .AddMicrosoftIdentityUI(); #endif } diff --git a/src/ProjectTemplates/Web.ProjectTemplates/content/StarterWeb-CSharp/appsettings.json b/src/ProjectTemplates/Web.ProjectTemplates/content/StarterWeb-CSharp/appsettings.json index f72b1ef6312d..f6582afb0773 100644 --- a/src/ProjectTemplates/Web.ProjectTemplates/content/StarterWeb-CSharp/appsettings.json +++ b/src/ProjectTemplates/Web.ProjectTemplates/content/StarterWeb-CSharp/appsettings.json @@ -17,10 +17,10 @@ // }, ////#elseif (OrganizationalAuth) // "AzureAd": { +// "Instance": "https:////login.microsoftonline.com/", //#if (MultiOrgAuth) -// "Instance": "https:////login.microsoftonline.com/common", +// "TenantId": "common", //#elseif (SingleOrgAuth) -// "Instance": "https:////login.microsoftonline.com/", // "Domain": "qualified.domain.name", // "TenantId": "22222222-2222-2222-2222-222222222222", //#endif @@ -34,16 +34,16 @@ // }, ////#endif ////#if (GenerateApiOrGraph) -// "CalledApi": { +// "DownstreamApi": { // /* -// 'CalledApiScopes' contains space separated scopes of the Web API you want to call. This can be: +// 'Scopes' contains space separated scopes of the Web API you want to call. This can be: // - a scope for a V2 application (for instance api://b3682cc7-8b30-4bd2-aaba-080c6bf0fd31/access_as_user) // - a scope corresponding to a V1 application (for instance /.default, where is the // App ID URI of a legacy v1 Web application // Applications are registered in the https://portal.azure.com portal. // */ -// "CalledApiScopes": "user.read", -// "CalledApiUrl": "[WebApiUrl]" +// "BaseUrl": "[WebApiUrl]", +// "Scopes": "user.read" // }, ////#endif ////#if (IndividualLocalAuth) diff --git a/src/ProjectTemplates/Web.ProjectTemplates/content/WebApi-CSharp/.template.config/dotnetcli.host.json b/src/ProjectTemplates/Web.ProjectTemplates/content/WebApi-CSharp/.template.config/dotnetcli.host.json index af0458974c1f..61d78ca619df 100644 --- a/src/ProjectTemplates/Web.ProjectTemplates/content/WebApi-CSharp/.template.config/dotnetcli.host.json +++ b/src/ProjectTemplates/Web.ProjectTemplates/content/WebApi-CSharp/.template.config/dotnetcli.host.json @@ -32,6 +32,10 @@ "longName": "tenant-id", "shortName": "" }, + "DefaultScope": { + "longName": "default-scope", + "shortName": "s" + }, "Framework": { "longName": "framework" }, diff --git a/src/ProjectTemplates/Web.ProjectTemplates/content/WebApi-CSharp/.template.config/template.json b/src/ProjectTemplates/Web.ProjectTemplates/content/WebApi-CSharp/.template.config/template.json index f11b772861d8..78260944d261 100644 --- a/src/ProjectTemplates/Web.ProjectTemplates/content/WebApi-CSharp/.template.config/template.json +++ b/src/ProjectTemplates/Web.ProjectTemplates/content/WebApi-CSharp/.template.config/template.json @@ -36,19 +36,6 @@ "exclude": [ "Properties/launchSettings.json" ] - }, - { - "condition": "(!GenerateApi)", - "exclude": [ - "Services/DownstreamWebApi.cs" - ] - }, - { - "condition": "(!GenerateGraph)", - "exclude": [ - "Services/MicrosoftGraphServiceExtensions.cs", - "Services/TokenAcquisitionCredentialProvider.cs" - ] } ] } @@ -111,6 +98,13 @@ "replaces": "qualified.domain.name", "description": "The domain for the directory tenant (use with SingleOrg or IndividualB2C auth)." }, + "DefaultScope": { + "type": "parameter", + "datatype": "string", + "replaces": "api-scope", + "defaultValue": "access_as_user", + "description": "The API scope the client needs to request to provision an access token. (use with IndividualB2C, SingleOrg)." + }, "TenantId": { "type": "parameter", "datatype": "string", diff --git a/src/ProjectTemplates/Web.ProjectTemplates/content/WebApi-CSharp/Controllers/WeatherForecastController.cs b/src/ProjectTemplates/Web.ProjectTemplates/content/WebApi-CSharp/Controllers/WeatherForecastController.cs index 584c64869c2c..f39a23474a9d 100644 --- a/src/ProjectTemplates/Web.ProjectTemplates/content/WebApi-CSharp/Controllers/WeatherForecastController.cs +++ b/src/ProjectTemplates/Web.ProjectTemplates/content/WebApi-CSharp/Controllers/WeatherForecastController.cs @@ -37,7 +37,7 @@ public class WeatherForecastController : ControllerBase private readonly ILogger _logger; // The Web API will only accept tokens 1) for users, and 2) having the access_as_user scope for this API - static readonly string[] scopeRequiredByApi = new string[] { "access_as_user" }; + static readonly string[] scopeRequiredByApi = new string[] { "api-scope" }; #if (GenerateApi) private readonly IDownstreamWebApi _downstreamWebApi; @@ -54,7 +54,17 @@ public async Task> Get() { HttpContext.VerifyUserHasAnyAcceptedScope(scopeRequiredByApi); - string downstreamApiResult = await _downstreamWebApi.CallWebApiAsync(); + var response = await _downstreamWebApi.CallWebApiForUserAsync("DownstreamApi").ConfigureAwait(false); + if (response.StatusCode == System.Net.HttpStatusCode.OK) + { + string apiResult = await response.Content.ReadAsStringAsync().ConfigureAwait(false); + // Do something + } + else + { + string error = await response.Content.ReadAsStringAsync().ConfigureAwait(false); + throw new HttpRequestException($"Invalid status code in the HttpResponseMessage: {response.StatusCode}: {error}"); + } var rng = new Random(); return Enumerable.Range(1, 5).Select(index => new WeatherForecast diff --git a/src/ProjectTemplates/Web.ProjectTemplates/content/WebApi-CSharp/Services/DownstreamWebApi.cs b/src/ProjectTemplates/Web.ProjectTemplates/content/WebApi-CSharp/Services/DownstreamWebApi.cs deleted file mode 100644 index 4806ca192d9e..000000000000 --- a/src/ProjectTemplates/Web.ProjectTemplates/content/WebApi-CSharp/Services/DownstreamWebApi.cs +++ /dev/null @@ -1,72 +0,0 @@ -using System.Net; -using System.Net.Http; -using System.Threading.Tasks; -using Microsoft.Extensions.Configuration; -using Microsoft.Extensions.DependencyInjection; -using Microsoft.Identity.Web; - -namespace Company.WebApplication1 -{ - public interface IDownstreamWebApi - { - Task CallWebApiAsync(string relativeEndpoint = "", string[] requiredScopes = null); - } - - public static class DownstreamWebApiExtensions - { - public static void AddDownstreamWebApiService(this IServiceCollection services, IConfiguration configuration) - { - // https://docs.microsoft.com/en-us/dotnet/standard/microservices-architecture/implement-resilient-applications/use-httpclientfactory-to-implement-resilient-http-requests - services.AddHttpClient(); - } - } - - public class DownstreamWebApi : IDownstreamWebApi - { - private readonly ITokenAcquisition _tokenAcquisition; - - private readonly IConfiguration _configuration; - - private readonly HttpClient _httpClient; - - public DownstreamWebApi( - ITokenAcquisition tokenAcquisition, - IConfiguration configuration, - HttpClient httpClient) - { - _tokenAcquisition = tokenAcquisition; - _configuration = configuration; - _httpClient = httpClient; - } - - /// - /// Calls the Web API with the required scopes - /// - /// [Optional] Scopes required to call the Web API. If - /// not specified, uses scopes from the configuration - /// Endpoint relative to the CalledApiUrl configuration - /// A JSON string representing the result of calling the Web API - public async Task CallWebApiAsync(string relativeEndpoint = "", string[] requiredScopes = null) - { - string[] scopes = requiredScopes ?? _configuration["CalledApi:CalledApiScopes"]?.Split(' '); - string apiUrl = (_configuration["CalledApi:CalledApiUrl"] as string)?.TrimEnd('/') + $"/{relativeEndpoint}"; - - string accessToken = await _tokenAcquisition.GetAccessTokenForUserAsync(scopes); - HttpRequestMessage httpRequestMessage = new HttpRequestMessage(HttpMethod.Get, apiUrl); - httpRequestMessage.Headers.Add("Authorization", $"bearer {accessToken}"); - - string apiResult; - var response = await _httpClient.SendAsync(httpRequestMessage); - if (response.StatusCode == HttpStatusCode.OK) - { - apiResult = await response.Content.ReadAsStringAsync(); - } - else - { - apiResult = $"Error calling the API '{apiUrl}'"; - } - - return apiResult; - } - } -} diff --git a/src/ProjectTemplates/Web.ProjectTemplates/content/WebApi-CSharp/Services/MicrosoftGraphServiceExtensions.cs b/src/ProjectTemplates/Web.ProjectTemplates/content/WebApi-CSharp/Services/MicrosoftGraphServiceExtensions.cs deleted file mode 100644 index 2e805a968890..000000000000 --- a/src/ProjectTemplates/Web.ProjectTemplates/content/WebApi-CSharp/Services/MicrosoftGraphServiceExtensions.cs +++ /dev/null @@ -1,37 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using Microsoft.Extensions.Configuration; -using Microsoft.Extensions.DependencyInjection; -using Microsoft.Graph; -using Microsoft.Identity.Web; - -namespace Company.WebApplication1 -{ - public static class MicrosoftGraphServiceExtensions - { - /// - /// Adds the Microsoft Graph client as a singleton. - /// - /// Service collection. - /// Initial scopes. - /// Base URL for Microsoft graph. This can be - /// changed for instance for applications running in national clouds - public static IServiceCollection AddMicrosoftGraph(this IServiceCollection services, - IEnumerable initialScopes, - string graphBaseUrl = "https://graph.microsoft.com/v1.0") - { - services.AddTokenAcquisition(true); - services.AddSingleton(serviceProvider => - { - var tokenAquisitionService = serviceProvider.GetService(); - GraphServiceClient client = string.IsNullOrWhiteSpace(graphBaseUrl) ? - new GraphServiceClient(new TokenAcquisitionCredentialProvider(tokenAquisitionService, initialScopes)) : - new GraphServiceClient(graphBaseUrl, new TokenAcquisitionCredentialProvider(tokenAquisitionService, initialScopes)); - return client; - }); - - return services; - } - } -} diff --git a/src/ProjectTemplates/Web.ProjectTemplates/content/WebApi-CSharp/Services/TokenAcquisitionCredentialProvider.cs b/src/ProjectTemplates/Web.ProjectTemplates/content/WebApi-CSharp/Services/TokenAcquisitionCredentialProvider.cs deleted file mode 100644 index 8042149d82f8..000000000000 --- a/src/ProjectTemplates/Web.ProjectTemplates/content/WebApi-CSharp/Services/TokenAcquisitionCredentialProvider.cs +++ /dev/null @@ -1,27 +0,0 @@ -using Microsoft.Graph; -using Microsoft.Identity.Web; -using System.Collections; -using System.Collections.Generic; -using System.Net.Http; -using System.Threading.Tasks; - -namespace Company.WebApplication1 -{ - internal class TokenAcquisitionCredentialProvider : IAuthenticationProvider - { - public TokenAcquisitionCredentialProvider(ITokenAcquisition tokenAcquisition, IEnumerable initialScopes) - { - _tokenAcquisition = tokenAcquisition; - _initialScopes = initialScopes; - } - - ITokenAcquisition _tokenAcquisition; - IEnumerable _initialScopes; - - public async Task AuthenticateRequestAsync(HttpRequestMessage request) - { - request.Headers.Add("Authorization", - $"Bearer {await _tokenAcquisition.GetAccessTokenForUserAsync(_initialScopes)}"); - } - } -} diff --git a/src/ProjectTemplates/Web.ProjectTemplates/content/WebApi-CSharp/Startup.cs b/src/ProjectTemplates/Web.ProjectTemplates/content/WebApi-CSharp/Startup.cs index 0e935d107a7f..7b961302e262 100644 --- a/src/ProjectTemplates/Web.ProjectTemplates/content/WebApi-CSharp/Startup.cs +++ b/src/ProjectTemplates/Web.ProjectTemplates/content/WebApi-CSharp/Startup.cs @@ -9,15 +9,9 @@ #endif using Microsoft.AspNetCore.Mvc; #if (OrganizationalAuth || IndividualB2CAuth) +using Microsoft.AspNetCore.Authentication.JwtBearer; using Microsoft.AspNetCore.Authentication; -#endif -#if (OrganizationalAuth) -using Microsoft.Identity.Web; -using Microsoft.Identity.Web.TokenCacheProviders.InMemory; -#endif -#if (IndividualB2CAuth) using Microsoft.Identity.Web; -using Microsoft.Identity.Web.TokenCacheProviders.InMemory; #endif using Microsoft.Extensions.Configuration; using Microsoft.Extensions.DependencyInjection; @@ -45,32 +39,29 @@ public Startup(IConfiguration configuration) public void ConfigureServices(IServiceCollection services) { #if (OrganizationalAuth) + services.AddAuthentication(JwtBearerDefaults.AuthenticationScheme) + .AddMicrosoftIdentityWebApi(Configuration.GetSection("AzureAd")) #if (GenerateApiOrGraph) - // Adds Microsoft Identity platform (AAD v2.0) support to protect this Api - services.AddMicrosoftWebApiAuthentication(Configuration, "AzureAd") - .AddMicrosoftWebApiCallsWebApi(Configuration, "AzureAd") - .AddInMemoryTokenCaches(); - -#else - // Adds Microsoft Identity platform (AAD v2.0) support to protect this Api - services.AddMicrosoftWebApiAuthentication(Configuration, "AzureAd"); -#endif + .EnableTokenAcquisitionToCallDownstreamApi() #if (GenerateApi) - services.AddDownstreamWebApiService(Configuration); + .AddDownstreamWebApi("DownstreamApi", Configuration.GetSection("DownstreamApi")) #endif #if (GenerateGraph) - services.AddMicrosoftGraph(Configuration.GetValue("CalledApi:CalledApiScopes")?.Split(' '), - Configuration.GetValue("CalledApi:CalledApiUrl")); + .AddMicrosoftGraph(Configuration.GetSection("DownstreamApi")) +#endif + .AddInMemoryTokenCaches(); +#else + ; #endif #elif (IndividualB2CAuth) + services.AddAuthentication(JwtBearerDefaults.AuthenticationScheme) + .AddMicrosoftIdentityWebApi(Configuration.GetSection("AzureAdB2C")) #if (GenerateApi) - services.AddMicrosoftWebApiAuthentication(Configuration, "AzureAdB2C") - .AddMicrosoftWebApiCallsWebApi(Configuration, "AzureAdB2C") - .AddInMemoryTokenCaches(); - - services.AddDownstreamWebApiService(Configuration); + .EnableTokenAcquisitionToCallDownstreamApi() + .AddDownstreamWebApi("DownstreamApi", Configuration.GetSection("DownstreamApi")) + .AddInMemoryTokenCaches(); #else - services.AddMicrosoftWebApiAuthentication(Configuration, "AzureAdB2C"); + ; #endif #endif @@ -98,6 +89,7 @@ public void Configure(IApplicationBuilder app, IWebHostEnvironment env) app.UseHttpsRedirection(); #endif + app.UseRouting(); #if (OrganizationalAuth || IndividualAuth) diff --git a/src/ProjectTemplates/Web.ProjectTemplates/content/WebApi-CSharp/appsettings.json b/src/ProjectTemplates/Web.ProjectTemplates/content/WebApi-CSharp/appsettings.json index cd6a5721b610..2b965fcc659c 100644 --- a/src/ProjectTemplates/Web.ProjectTemplates/content/WebApi-CSharp/appsettings.json +++ b/src/ProjectTemplates/Web.ProjectTemplates/content/WebApi-CSharp/appsettings.json @@ -13,10 +13,10 @@ // }, ////#elseif (OrganizationalAuth) // "AzureAd": { +// "Instance": "https:////login.microsoftonline.com/", //#if (!SingleOrgAuth) -// "Instance": "https:////login.microsoftonline.com/common", +// "TenantId": "common", //#else -// "Instance": "https:////login.microsoftonline.com/", // "Domain": "qualified.domain.name", // "TenantId": "22222222-2222-2222-2222-222222222222", //#endif @@ -31,16 +31,16 @@ // }, ////#endif ////#if (GenerateApiOrGraph) -// "CalledApi": { +// "DownstreamAPI": { // /* -// 'CalledApiScopes' contains space separated scopes of the Web API you want to call. This can be: +// 'Scopes' contains space separated scopes of the Web API you want to call. This can be: // - a scope for a V2 application (for instance api://b3682cc7-8b30-4bd2-aaba-080c6bf0fd31/access_as_user) // - a scope corresponding to a V1 application (for instance /.default, where is the // App ID URI of a legacy v1 Web application // Applications are registered in the https://portal.azure.com portal. // */ -// "CalledApiScopes": "user.read", -// "CalledApiUrl": "[WebApiUrl]" +// "BaseUrl": "[WebApiUrl]", +// "Scopes": "user.read" // }, ////#endif "Logging": { From c1869f527efcc22330d638c17f5dbab15081f0be Mon Sep 17 00:00:00 2001 From: Jean-Marc Prieur Date: Thu, 27 Aug 2020 18:32:08 +0200 Subject: [PATCH 2/7] version bump and quick fix to the Blasorwasm default values for the Web api so that customers have a better experience --- eng/Versions.props | 4 ++-- .../.template.config/template.json | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/eng/Versions.props b/eng/Versions.props index 9c2fd0000ebc..2130501adf90 100644 --- a/eng/Versions.props +++ b/eng/Versions.props @@ -258,8 +258,8 @@ 4.0.4 4.0.4 2.1.90 - 0.2.1-preview - 0.2.1-preview + 0.3.1-preview + 0.3.1-preview 3.8.0 $(MessagePackPackageVersion) 4.10.0 diff --git a/src/ProjectTemplates/Web.ProjectTemplates/content/ComponentsWebAssembly-CSharp/.template.config/template.json b/src/ProjectTemplates/Web.ProjectTemplates/content/ComponentsWebAssembly-CSharp/.template.config/template.json index a35ba5c57a49..0e8fd2766fe9 100644 --- a/src/ProjectTemplates/Web.ProjectTemplates/content/ComponentsWebAssembly-CSharp/.template.config/template.json +++ b/src/ProjectTemplates/Web.ProjectTemplates/content/ComponentsWebAssembly-CSharp/.template.config/template.json @@ -457,7 +457,7 @@ "type": "parameter", "datatype": "string", "replaces": "[WebApiUrl]", - "defaultValue" : "https://graph.microsoft.com/v1.0/me", + "defaultValue": "https://graph.microsoft.com/v1.0", "description": "URL of the API to call from the web app. This option only applies if --auth SingleOrg, --auth MultiOrg or --auth IndividualB2C without and ASP.NET Core host is specified." }, "CallsMicrosoftGraph": { @@ -474,7 +474,7 @@ }, "GenerateApi": { "type": "computed", - "value": "(( (IndividualB2CAuth && !Hosted) || OrganizationalAuth) && (CalledApiUrl != \"https://graph.microsoft.com/v1.0/me\" || CalledApiScopes != \"user.read\"))" + "value": "(( (IndividualB2CAuth && !Hosted) || OrganizationalAuth) && (CalledApiUrl != \"https://graph.microsoft.com/v1.0\" || CalledApiScopes != \"user.read\"))" }, "GenerateGraph": { "type": "computed", From 1eb540fa74a5b5384b3fb0d1f2cbd0d553c8876e Mon Sep 17 00:00:00 2001 From: Jean-Marc Prieur Date: Thu, 27 Aug 2020 22:26:53 +0200 Subject: [PATCH 3/7] Addressing John's PR review comments --- .../content/BlazorServerWeb-CSharp/Startup.cs | 26 +++++++++---------- .../Server/Startup.cs | 22 ++++++++-------- .../content/RazorPagesWeb-CSharp/Startup.cs | 26 +++++++++---------- .../content/StarterWeb-CSharp/Startup.cs | 25 +++++++++--------- .../.template.config/dotnetcli.host.json | 2 +- .../Controllers/WeatherForecastController.cs | 6 ++--- .../content/WebApi-CSharp/Startup.cs | 24 ++++++++--------- 7 files changed, 66 insertions(+), 65 deletions(-) diff --git a/src/ProjectTemplates/Web.ProjectTemplates/content/BlazorServerWeb-CSharp/Startup.cs b/src/ProjectTemplates/Web.ProjectTemplates/content/BlazorServerWeb-CSharp/Startup.cs index 89b4491a48b9..737f218353db 100644 --- a/src/ProjectTemplates/Web.ProjectTemplates/content/BlazorServerWeb-CSharp/Startup.cs +++ b/src/ProjectTemplates/Web.ProjectTemplates/content/BlazorServerWeb-CSharp/Startup.cs @@ -73,36 +73,36 @@ public void ConfigureServices(IServiceCollection services) .AddEntityFrameworkStores(); #elif (OrganizationalAuth) #if (GenerateApiOrGraph) - string[] initialScopes = Configuration.GetValue("DownstreamApi:Scopes")?.Split(' '); + var initialScopes = Configuration.GetValue("DownstreamApi:Scopes")?.Split(' '); #endif services.AddAuthentication(OpenIdConnectDefaults.AuthenticationScheme) - .AddMicrosoftIdentityWebApp(Configuration.GetSection("AzureAd")) #if (GenerateApiOrGraph) - .EnableTokenAcquisitionToCallDownstreamApi(initialScopes) + .AddMicrosoftIdentityWebApp(Configuration.GetSection("AzureAd")) + .EnableTokenAcquisitionToCallDownstreamApi(initialScopes) #if (GenerateApi) - .AddDownstreamWebApi("DownstreamApi", Configuration.GetSection("DownstreamApi")) + .AddDownstreamWebApi("DownstreamApi", Configuration.GetSection("DownstreamApi")) #endif #if (GenerateGraph) - .AddMicrosoftGraph(Configuration.GetSection("DownstreamApi")) + .AddMicrosoftGraph(Configuration.GetSection("DownstreamApi")) #endif - .AddInMemoryTokenCaches(); + .AddInMemoryTokenCaches(); #else - ; + .AddMicrosoftIdentityWebApp(Configuration.GetSection("AzureAd")); #endif #elif (IndividualB2CAuth) #if (GenerateApi) - string[] initialScopes = Configuration.GetValue("DownstreamApi:Scopes")?.Split(' '); + var initialScopes = Configuration.GetValue("DownstreamApi:Scopes")?.Split(' '); #endif services.AddAuthentication(OpenIdConnectDefaults.AuthenticationScheme) - .AddMicrosoftIdentityWebApp(Configuration.GetSection("AzureAdB2C")) #if (GenerateApi) - .EnableTokenAcquisitionToCallDownstreamApi(initialScopes) - .AddDownstreamWebApi("DownstreamApi", Configuration.GetSection("DownstreamApi")) - .AddInMemoryTokenCaches(); + .AddMicrosoftIdentityWebApp(Configuration.GetSection("AzureAdB2C")) + .EnableTokenAcquisitionToCallDownstreamApi(initialScopes) + .AddDownstreamWebApi("DownstreamApi", Configuration.GetSection("DownstreamApi")) + .AddInMemoryTokenCaches(); #else - ; + .AddMicrosoftIdentityWebApp(Configuration.GetSection("AzureAdB2C")); #endif #endif #if (OrganizationalAuth || IndividualB2CAuth) diff --git a/src/ProjectTemplates/Web.ProjectTemplates/content/ComponentsWebAssembly-CSharp/Server/Startup.cs b/src/ProjectTemplates/Web.ProjectTemplates/content/ComponentsWebAssembly-CSharp/Server/Startup.cs index d0ffb393ea7a..cadd67d408d2 100644 --- a/src/ProjectTemplates/Web.ProjectTemplates/content/ComponentsWebAssembly-CSharp/Server/Startup.cs +++ b/src/ProjectTemplates/Web.ProjectTemplates/content/ComponentsWebAssembly-CSharp/Server/Startup.cs @@ -64,28 +64,28 @@ public void ConfigureServices(IServiceCollection services) #endif #if (OrganizationalAuth) services.AddAuthentication(JwtBearerDefaults.AuthenticationScheme) - .AddMicrosoftIdentityWebApi(Configuration.GetSection("AzureAd")) #if (GenerateApiOrGraph) - .EnableTokenAcquisitionToCallDownstreamApi() + .AddMicrosoftIdentityWebApi(Configuration.GetSection("AzureAd")) + .EnableTokenAcquisitionToCallDownstreamApi() #if (GenerateApi) - .AddDownstreamWebApi("DownstreamApi", Configuration.GetSection("DownstreamApi")) + .AddDownstreamWebApi("DownstreamApi", Configuration.GetSection("DownstreamApi")) #endif #if (GenerateGraph) - .AddMicrosoftGraph(Configuration.GetSection("DownstreamApi")) + .AddMicrosoftGraph(Configuration.GetSection("DownstreamApi")) #endif - .AddInMemoryTokenCaches(); + .AddInMemoryTokenCaches(); #else - ; + .AddMicrosoftIdentityWebApi(Configuration.GetSection("AzureAd")); #endif #elif (IndividualB2CAuth) services.AddAuthentication(JwtBearerDefaults.AuthenticationScheme) - .AddMicrosoftIdentityWebApi(Configuration.GetSection("AzureAdB2C")) #if (GenerateApi) - .EnableTokenAcquisitionToCallDownstreamApi() - .AddDownstreamWebApi("DownstreamApi", Configuration.GetSection("DownstreamApi")) - .AddInMemoryTokenCaches(); + .AddMicrosoftIdentityWebApi(Configuration.GetSection("AzureAdB2C")) + .EnableTokenAcquisitionToCallDownstreamApi() + .AddDownstreamWebApi("DownstreamApi", Configuration.GetSection("DownstreamApi")) + .AddInMemoryTokenCaches(); #else - ; + .AddMicrosoftIdentityWebApi(Configuration.GetSection("AzureAdB2C")); #endif #endif diff --git a/src/ProjectTemplates/Web.ProjectTemplates/content/RazorPagesWeb-CSharp/Startup.cs b/src/ProjectTemplates/Web.ProjectTemplates/content/RazorPagesWeb-CSharp/Startup.cs index c7cfada6be11..f2ccb947a73b 100644 --- a/src/ProjectTemplates/Web.ProjectTemplates/content/RazorPagesWeb-CSharp/Startup.cs +++ b/src/ProjectTemplates/Web.ProjectTemplates/content/RazorPagesWeb-CSharp/Startup.cs @@ -63,36 +63,36 @@ public void ConfigureServices(IServiceCollection services) .AddEntityFrameworkStores(); #elif (OrganizationalAuth) #if (GenerateApiOrGraph) - string[] initialScopes = Configuration.GetValue("DownstreamApi:Scopes")?.Split(' '); + var initialScopes = Configuration.GetValue("DownstreamApi:Scopes")?.Split(' '); #endif services.AddAuthentication(OpenIdConnectDefaults.AuthenticationScheme) - .AddMicrosoftIdentityWebApp(Configuration.GetSection("AzureAd")) #if (GenerateApiOrGraph) - .EnableTokenAcquisitionToCallDownstreamApi(initialScopes) + .AddMicrosoftIdentityWebApp(Configuration.GetSection("AzureAd")) + .EnableTokenAcquisitionToCallDownstreamApi(initialScopes) #if (GenerateApi) - .AddDownstreamWebApi("DownstreamApi", Configuration.GetSection("DownstreamApi")) + .AddDownstreamWebApi("DownstreamApi", Configuration.GetSection("DownstreamApi")) #endif #if (GenerateGraph) - .AddMicrosoftGraph(Configuration.GetSection("DownstreamApi")) + .AddMicrosoftGraph(Configuration.GetSection("DownstreamApi")) #endif - .AddInMemoryTokenCaches(); + .AddInMemoryTokenCaches(); #else - ; + .AddMicrosoftIdentityWebApp(Configuration.GetSection("AzureAd")); #endif #elif (IndividualB2CAuth) #if (GenerateApi) - string[] initialScopes = Configuration.GetValue("DownstreamApi:Scopes")?.Split(' '); + var initialScopes = Configuration.GetValue("DownstreamApi:Scopes")?.Split(' '); #endif services.AddAuthentication(OpenIdConnectDefaults.AuthenticationScheme) - .AddMicrosoftIdentityWebApp(Configuration.GetSection("AzureAdB2C")) #if (GenerateApi) - .EnableTokenAcquisitionToCallDownstreamApi(initialScopes) - .AddDownstreamWebApi("DownstreamApi", Configuration.GetSection("DownstreamApi")) - .AddInMemoryTokenCaches(); + .AddMicrosoftIdentityWebApp(Configuration.GetSection("AzureAdB2C")) + .EnableTokenAcquisitionToCallDownstreamApi(initialScopes) + .AddDownstreamWebApi("DownstreamApi", Configuration.GetSection("DownstreamApi")) + .AddInMemoryTokenCaches(); #else - ; + .AddMicrosoftIdentityWebApp(Configuration.GetSection("AzureAdB2C")); #endif #endif #if (OrganizationalAuth) diff --git a/src/ProjectTemplates/Web.ProjectTemplates/content/StarterWeb-CSharp/Startup.cs b/src/ProjectTemplates/Web.ProjectTemplates/content/StarterWeb-CSharp/Startup.cs index 1b5371c74b17..a3dfd3ad6860 100644 --- a/src/ProjectTemplates/Web.ProjectTemplates/content/StarterWeb-CSharp/Startup.cs +++ b/src/ProjectTemplates/Web.ProjectTemplates/content/StarterWeb-CSharp/Startup.cs @@ -64,36 +64,37 @@ public void ConfigureServices(IServiceCollection services) .AddEntityFrameworkStores(); #elif (OrganizationalAuth) #if (GenerateApiOrGraph) - string[] initialScopes = Configuration.GetValue("DownstreamApi:Scopes")?.Split(' '); + var initialScopes = Configuration.GetValue("DownstreamApi:Scopes")?.Split(' '); #endif services.AddAuthentication(OpenIdConnectDefaults.AuthenticationScheme) - .AddMicrosoftIdentityWebApp(Configuration.GetSection("AzureAd")) #if (GenerateApiOrGraph) - .EnableTokenAcquisitionToCallDownstreamApi(initialScopes) + .AddMicrosoftIdentityWebApp(Configuration.GetSection("AzureAd")) + .EnableTokenAcquisitionToCallDownstreamApi(initialScopes) #if (GenerateApi) - .AddDownstreamWebApi("DownstreamApi", Configuration.GetSection("DownstreamApi")) + .AddDownstreamWebApi("DownstreamApi", Configuration.GetSection("DownstreamApi")) #endif #if (GenerateGraph) - .AddMicrosoftGraph(Configuration.GetSection("DownstreamApi")) + .AddMicrosoftGraph(Configuration.GetSection("DownstreamApi")) #endif - .AddInMemoryTokenCaches(); + .AddInMemoryTokenCaches(); #else - ; + .AddMicrosoftIdentityWebApp(Configuration.GetSection("AzureAd")); #endif #elif (IndividualB2CAuth) #if (GenerateApi) - string[] initialScopes = Configuration.GetValue("DownstreamApi:Scopes")?.Split(' '); + var initialScopes = Configuration.GetValue("DownstreamApi:Scopes")?.Split(' '); #endif services.AddAuthentication(OpenIdConnectDefaults.AuthenticationScheme) .AddMicrosoftIdentityWebApp(Configuration.GetSection("AzureAdB2C")) #if (GenerateApi) - .EnableTokenAcquisitionToCallDownstreamApi(initialScopes) - .AddDownstreamWebApi("DownstreamApi", Configuration.GetSection("DownstreamApi")) - .AddInMemoryTokenCaches(); + .AddMicrosoftIdentityWebApp(Configuration.GetSection("AzureAdB2C")) + .EnableTokenAcquisitionToCallDownstreamApi(initialScopes) + .AddDownstreamWebApi("DownstreamApi", Configuration.GetSection("DownstreamApi")) + .AddInMemoryTokenCaches(); #else - ; + .AddMicrosoftIdentityWebApp(Configuration.GetSection("AzureAdB2C")); #endif #endif #if (OrganizationalAuth) diff --git a/src/ProjectTemplates/Web.ProjectTemplates/content/WebApi-CSharp/.template.config/dotnetcli.host.json b/src/ProjectTemplates/Web.ProjectTemplates/content/WebApi-CSharp/.template.config/dotnetcli.host.json index 61d78ca619df..b16a1f9a27a1 100644 --- a/src/ProjectTemplates/Web.ProjectTemplates/content/WebApi-CSharp/.template.config/dotnetcli.host.json +++ b/src/ProjectTemplates/Web.ProjectTemplates/content/WebApi-CSharp/.template.config/dotnetcli.host.json @@ -34,7 +34,7 @@ }, "DefaultScope": { "longName": "default-scope", - "shortName": "s" + "shortName": "" }, "Framework": { "longName": "framework" diff --git a/src/ProjectTemplates/Web.ProjectTemplates/content/WebApi-CSharp/Controllers/WeatherForecastController.cs b/src/ProjectTemplates/Web.ProjectTemplates/content/WebApi-CSharp/Controllers/WeatherForecastController.cs index f39a23474a9d..08dcdbbd66e6 100644 --- a/src/ProjectTemplates/Web.ProjectTemplates/content/WebApi-CSharp/Controllers/WeatherForecastController.cs +++ b/src/ProjectTemplates/Web.ProjectTemplates/content/WebApi-CSharp/Controllers/WeatherForecastController.cs @@ -36,7 +36,7 @@ public class WeatherForecastController : ControllerBase private readonly ILogger _logger; - // The Web API will only accept tokens 1) for users, and 2) having the access_as_user scope for this API + // The Web API will only accept tokens 1) for users, and 2) having the "api-scope" scope for this API static readonly string[] scopeRequiredByApi = new string[] { "api-scope" }; #if (GenerateApi) @@ -57,12 +57,12 @@ public async Task> Get() var response = await _downstreamWebApi.CallWebApiForUserAsync("DownstreamApi").ConfigureAwait(false); if (response.StatusCode == System.Net.HttpStatusCode.OK) { - string apiResult = await response.Content.ReadAsStringAsync().ConfigureAwait(false); + var apiResult = await response.Content.ReadAsStringAsync().ConfigureAwait(false); // Do something } else { - string error = await response.Content.ReadAsStringAsync().ConfigureAwait(false); + var error = await response.Content.ReadAsStringAsync().ConfigureAwait(false); throw new HttpRequestException($"Invalid status code in the HttpResponseMessage: {response.StatusCode}: {error}"); } diff --git a/src/ProjectTemplates/Web.ProjectTemplates/content/WebApi-CSharp/Startup.cs b/src/ProjectTemplates/Web.ProjectTemplates/content/WebApi-CSharp/Startup.cs index 7b961302e262..938266e68253 100644 --- a/src/ProjectTemplates/Web.ProjectTemplates/content/WebApi-CSharp/Startup.cs +++ b/src/ProjectTemplates/Web.ProjectTemplates/content/WebApi-CSharp/Startup.cs @@ -9,8 +9,8 @@ #endif using Microsoft.AspNetCore.Mvc; #if (OrganizationalAuth || IndividualB2CAuth) -using Microsoft.AspNetCore.Authentication.JwtBearer; using Microsoft.AspNetCore.Authentication; +using Microsoft.AspNetCore.Authentication.JwtBearer; using Microsoft.Identity.Web; #endif using Microsoft.Extensions.Configuration; @@ -40,28 +40,28 @@ public void ConfigureServices(IServiceCollection services) { #if (OrganizationalAuth) services.AddAuthentication(JwtBearerDefaults.AuthenticationScheme) - .AddMicrosoftIdentityWebApi(Configuration.GetSection("AzureAd")) #if (GenerateApiOrGraph) - .EnableTokenAcquisitionToCallDownstreamApi() + .AddMicrosoftIdentityWebApi(Configuration.GetSection("AzureAd")) + .EnableTokenAcquisitionToCallDownstreamApi() #if (GenerateApi) - .AddDownstreamWebApi("DownstreamApi", Configuration.GetSection("DownstreamApi")) + .AddDownstreamWebApi("DownstreamApi", Configuration.GetSection("DownstreamApi")) #endif #if (GenerateGraph) - .AddMicrosoftGraph(Configuration.GetSection("DownstreamApi")) + .AddMicrosoftGraph(Configuration.GetSection("DownstreamApi")) #endif - .AddInMemoryTokenCaches(); + .AddInMemoryTokenCaches(); #else - ; + .AddMicrosoftIdentityWebApi(Configuration.GetSection("AzureAd")); #endif #elif (IndividualB2CAuth) services.AddAuthentication(JwtBearerDefaults.AuthenticationScheme) - .AddMicrosoftIdentityWebApi(Configuration.GetSection("AzureAdB2C")) #if (GenerateApi) - .EnableTokenAcquisitionToCallDownstreamApi() - .AddDownstreamWebApi("DownstreamApi", Configuration.GetSection("DownstreamApi")) - .AddInMemoryTokenCaches(); + .AddMicrosoftIdentityWebApi(Configuration.GetSection("AzureAdB2C")) + .EnableTokenAcquisitionToCallDownstreamApi() + .AddDownstreamWebApi("DownstreamApi", Configuration.GetSection("DownstreamApi")) + .AddInMemoryTokenCaches(); #else - ; + .AddMicrosoftIdentityWebApi(Configuration.GetSection("AzureAdB2C")); #endif #endif From 817908388ea490416b755b172c7b708abc4046cd Mon Sep 17 00:00:00 2001 From: Jean-Marc Prieur Date: Thu, 27 Aug 2020 22:28:30 +0200 Subject: [PATCH 4/7] PR comments --- .../Server/Controllers/WeatherForecastController.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/ProjectTemplates/Web.ProjectTemplates/content/ComponentsWebAssembly-CSharp/Server/Controllers/WeatherForecastController.cs b/src/ProjectTemplates/Web.ProjectTemplates/content/ComponentsWebAssembly-CSharp/Server/Controllers/WeatherForecastController.cs index a8cd916c8d9c..85fd07dddd35 100644 --- a/src/ProjectTemplates/Web.ProjectTemplates/content/ComponentsWebAssembly-CSharp/Server/Controllers/WeatherForecastController.cs +++ b/src/ProjectTemplates/Web.ProjectTemplates/content/ComponentsWebAssembly-CSharp/Server/Controllers/WeatherForecastController.cs @@ -58,12 +58,12 @@ public async Task> Get() var response = await _downstreamWebApi.CallWebApiForUserAsync("DownstreamApi").ConfigureAwait(false); if (response.StatusCode == System.Net.HttpStatusCode.OK) { - string apiResult = await response.Content.ReadAsStringAsync().ConfigureAwait(false); + var apiResult = await response.Content.ReadAsStringAsync().ConfigureAwait(false); // Do something } else { - string error = await response.Content.ReadAsStringAsync().ConfigureAwait(false); + var error = await response.Content.ReadAsStringAsync().ConfigureAwait(false); throw new HttpRequestException($"Invalid status code in the HttpResponseMessage: {response.StatusCode}: {error}"); } From 205691be5a43fc877df2e38237a05b2719129d76 Mon Sep 17 00:00:00 2001 From: Jean-Marc Prieur Date: Fri, 28 Aug 2020 19:57:39 +0200 Subject: [PATCH 5/7] Feedback from @JunTaoLuo, @captainsafia, @Tratcher --- .../content/BlazorServerWeb-CSharp/Startup.cs | 4 ++-- .../Server/Controllers/WeatherForecastController.cs | 2 +- .../content/ComponentsWebAssembly-CSharp/Server/Startup.cs | 2 +- .../content/RazorPagesWeb-CSharp/Pages/Index.cshtml.cs | 6 +++--- .../content/RazorPagesWeb-CSharp/Startup.cs | 6 +++--- .../content/StarterWeb-CSharp/Controllers/HomeController.cs | 6 +++--- .../content/StarterWeb-CSharp/Startup.cs | 3 +-- .../WebApi-CSharp/Controllers/WeatherForecastController.cs | 2 +- .../Web.ProjectTemplates/content/WebApi-CSharp/Startup.cs | 2 +- 9 files changed, 16 insertions(+), 17 deletions(-) diff --git a/src/ProjectTemplates/Web.ProjectTemplates/content/BlazorServerWeb-CSharp/Startup.cs b/src/ProjectTemplates/Web.ProjectTemplates/content/BlazorServerWeb-CSharp/Startup.cs index 737f218353db..3802eb045ac9 100644 --- a/src/ProjectTemplates/Web.ProjectTemplates/content/BlazorServerWeb-CSharp/Startup.cs +++ b/src/ProjectTemplates/Web.ProjectTemplates/content/BlazorServerWeb-CSharp/Startup.cs @@ -88,7 +88,7 @@ public void ConfigureServices(IServiceCollection services) #endif .AddInMemoryTokenCaches(); #else - .AddMicrosoftIdentityWebApp(Configuration.GetSection("AzureAd")); + .AddMicrosoftIdentityWebApp(Configuration.GetSection("AzureAd")); #endif #elif (IndividualB2CAuth) #if (GenerateApi) @@ -107,7 +107,7 @@ public void ConfigureServices(IServiceCollection services) #endif #if (OrganizationalAuth || IndividualB2CAuth) services.AddControllersWithViews() - .AddMicrosoftIdentityUI(); + .AddMicrosoftIdentityUI(); services.AddAuthorization(options => { diff --git a/src/ProjectTemplates/Web.ProjectTemplates/content/ComponentsWebAssembly-CSharp/Server/Controllers/WeatherForecastController.cs b/src/ProjectTemplates/Web.ProjectTemplates/content/ComponentsWebAssembly-CSharp/Server/Controllers/WeatherForecastController.cs index 85fd07dddd35..b2e43394472c 100644 --- a/src/ProjectTemplates/Web.ProjectTemplates/content/ComponentsWebAssembly-CSharp/Server/Controllers/WeatherForecastController.cs +++ b/src/ProjectTemplates/Web.ProjectTemplates/content/ComponentsWebAssembly-CSharp/Server/Controllers/WeatherForecastController.cs @@ -55,7 +55,7 @@ public async Task> Get() { HttpContext.VerifyUserHasAnyAcceptedScope(scopeRequiredByApi); - var response = await _downstreamWebApi.CallWebApiForUserAsync("DownstreamApi").ConfigureAwait(false); + using var response = await _downstreamWebApi.CallWebApiForUserAsync("DownstreamApi").ConfigureAwait(false); if (response.StatusCode == System.Net.HttpStatusCode.OK) { var apiResult = await response.Content.ReadAsStringAsync().ConfigureAwait(false); diff --git a/src/ProjectTemplates/Web.ProjectTemplates/content/ComponentsWebAssembly-CSharp/Server/Startup.cs b/src/ProjectTemplates/Web.ProjectTemplates/content/ComponentsWebAssembly-CSharp/Server/Startup.cs index cadd67d408d2..3ebd062721ba 100644 --- a/src/ProjectTemplates/Web.ProjectTemplates/content/ComponentsWebAssembly-CSharp/Server/Startup.cs +++ b/src/ProjectTemplates/Web.ProjectTemplates/content/ComponentsWebAssembly-CSharp/Server/Startup.cs @@ -75,7 +75,7 @@ public void ConfigureServices(IServiceCollection services) #endif .AddInMemoryTokenCaches(); #else - .AddMicrosoftIdentityWebApi(Configuration.GetSection("AzureAd")); + .AddMicrosoftIdentityWebApi(Configuration.GetSection("AzureAd")); #endif #elif (IndividualB2CAuth) services.AddAuthentication(JwtBearerDefaults.AuthenticationScheme) diff --git a/src/ProjectTemplates/Web.ProjectTemplates/content/RazorPagesWeb-CSharp/Pages/Index.cshtml.cs b/src/ProjectTemplates/Web.ProjectTemplates/content/RazorPagesWeb-CSharp/Pages/Index.cshtml.cs index dfb724c5a48e..35b806d386ca 100644 --- a/src/ProjectTemplates/Web.ProjectTemplates/content/RazorPagesWeb-CSharp/Pages/Index.cshtml.cs +++ b/src/ProjectTemplates/Web.ProjectTemplates/content/RazorPagesWeb-CSharp/Pages/Index.cshtml.cs @@ -36,15 +36,15 @@ public IndexModel(ILogger logger, public async Task OnGet() { - var response = await _downstreamWebApi.CallWebApiForUserAsync("DownstreamApi").ConfigureAwait(false); + using var response = await _downstreamWebApi.CallWebApiForUserAsync("DownstreamApi").ConfigureAwait(false); if (response.StatusCode == System.Net.HttpStatusCode.OK) { - string apiResult = await response.Content.ReadAsStringAsync().ConfigureAwait(false); + var apiResult = await response.Content.ReadAsStringAsync().ConfigureAwait(false); ViewData["ApiResult"] = apiResult; } else { - string error = await response.Content.ReadAsStringAsync().ConfigureAwait(false); + var error = await response.Content.ReadAsStringAsync().ConfigureAwait(false); throw new HttpRequestException($"Invalid status code in the HttpResponseMessage: {response.StatusCode}: {error}"); } } diff --git a/src/ProjectTemplates/Web.ProjectTemplates/content/RazorPagesWeb-CSharp/Startup.cs b/src/ProjectTemplates/Web.ProjectTemplates/content/RazorPagesWeb-CSharp/Startup.cs index f2ccb947a73b..d78a9e3e66f0 100644 --- a/src/ProjectTemplates/Web.ProjectTemplates/content/RazorPagesWeb-CSharp/Startup.cs +++ b/src/ProjectTemplates/Web.ProjectTemplates/content/RazorPagesWeb-CSharp/Startup.cs @@ -78,7 +78,7 @@ public void ConfigureServices(IServiceCollection services) #endif .AddInMemoryTokenCaches(); #else - .AddMicrosoftIdentityWebApp(Configuration.GetSection("AzureAd")); + .AddMicrosoftIdentityWebApp(Configuration.GetSection("AzureAd")); #endif #elif (IndividualB2CAuth) #if (GenerateApi) @@ -104,10 +104,10 @@ public void ConfigureServices(IServiceCollection services) }); services.AddRazorPages() .AddMvcOptions(options => {}) - .AddMicrosoftIdentityUI(); + .AddMicrosoftIdentityUI(); #elif (IndividualB2CAuth) services.AddRazorPages() - .AddMicrosoftIdentityUI(); + .AddMicrosoftIdentityUI(); #else services.AddRazorPages(); #endif diff --git a/src/ProjectTemplates/Web.ProjectTemplates/content/StarterWeb-CSharp/Controllers/HomeController.cs b/src/ProjectTemplates/Web.ProjectTemplates/content/StarterWeb-CSharp/Controllers/HomeController.cs index 366fba743e6a..4aea265a2e8f 100644 --- a/src/ProjectTemplates/Web.ProjectTemplates/content/StarterWeb-CSharp/Controllers/HomeController.cs +++ b/src/ProjectTemplates/Web.ProjectTemplates/content/StarterWeb-CSharp/Controllers/HomeController.cs @@ -41,15 +41,15 @@ public HomeController(ILogger logger, [AuthorizeForScopes(ScopeKeySection = "DownstreamApi:Scopes")] public async Task Index() { - var response = await _downstreamWebApi.CallWebApiForUserAsync("DownstreamApi").ConfigureAwait(false); + using var response = await _downstreamWebApi.CallWebApiForUserAsync("DownstreamApi").ConfigureAwait(false); if (response.StatusCode == System.Net.HttpStatusCode.OK) { - string apiResult = await response.Content.ReadAsStringAsync().ConfigureAwait(false); + var apiResult = await response.Content.ReadAsStringAsync().ConfigureAwait(false); ViewData["ApiResult"] = apiResult; } else { - string error = await response.Content.ReadAsStringAsync().ConfigureAwait(false); + var error = await response.Content.ReadAsStringAsync().ConfigureAwait(false); throw new HttpRequestException($"Invalid status code in the HttpResponseMessage: {response.StatusCode}: {error}"); } return View(); diff --git a/src/ProjectTemplates/Web.ProjectTemplates/content/StarterWeb-CSharp/Startup.cs b/src/ProjectTemplates/Web.ProjectTemplates/content/StarterWeb-CSharp/Startup.cs index a3dfd3ad6860..a745a63580e0 100644 --- a/src/ProjectTemplates/Web.ProjectTemplates/content/StarterWeb-CSharp/Startup.cs +++ b/src/ProjectTemplates/Web.ProjectTemplates/content/StarterWeb-CSharp/Startup.cs @@ -79,7 +79,7 @@ public void ConfigureServices(IServiceCollection services) #endif .AddInMemoryTokenCaches(); #else - .AddMicrosoftIdentityWebApp(Configuration.GetSection("AzureAd")); + .AddMicrosoftIdentityWebApp(Configuration.GetSection("AzureAd")); #endif #elif (IndividualB2CAuth) #if (GenerateApi) @@ -87,7 +87,6 @@ public void ConfigureServices(IServiceCollection services) #endif services.AddAuthentication(OpenIdConnectDefaults.AuthenticationScheme) - .AddMicrosoftIdentityWebApp(Configuration.GetSection("AzureAdB2C")) #if (GenerateApi) .AddMicrosoftIdentityWebApp(Configuration.GetSection("AzureAdB2C")) .EnableTokenAcquisitionToCallDownstreamApi(initialScopes) diff --git a/src/ProjectTemplates/Web.ProjectTemplates/content/WebApi-CSharp/Controllers/WeatherForecastController.cs b/src/ProjectTemplates/Web.ProjectTemplates/content/WebApi-CSharp/Controllers/WeatherForecastController.cs index 08dcdbbd66e6..0cf62870ff7c 100644 --- a/src/ProjectTemplates/Web.ProjectTemplates/content/WebApi-CSharp/Controllers/WeatherForecastController.cs +++ b/src/ProjectTemplates/Web.ProjectTemplates/content/WebApi-CSharp/Controllers/WeatherForecastController.cs @@ -54,7 +54,7 @@ public async Task> Get() { HttpContext.VerifyUserHasAnyAcceptedScope(scopeRequiredByApi); - var response = await _downstreamWebApi.CallWebApiForUserAsync("DownstreamApi").ConfigureAwait(false); + using var response = await _downstreamWebApi.CallWebApiForUserAsync("DownstreamApi").ConfigureAwait(false); if (response.StatusCode == System.Net.HttpStatusCode.OK) { var apiResult = await response.Content.ReadAsStringAsync().ConfigureAwait(false); diff --git a/src/ProjectTemplates/Web.ProjectTemplates/content/WebApi-CSharp/Startup.cs b/src/ProjectTemplates/Web.ProjectTemplates/content/WebApi-CSharp/Startup.cs index 938266e68253..e1fedd249a8d 100644 --- a/src/ProjectTemplates/Web.ProjectTemplates/content/WebApi-CSharp/Startup.cs +++ b/src/ProjectTemplates/Web.ProjectTemplates/content/WebApi-CSharp/Startup.cs @@ -51,7 +51,7 @@ public void ConfigureServices(IServiceCollection services) #endif .AddInMemoryTokenCaches(); #else - .AddMicrosoftIdentityWebApi(Configuration.GetSection("AzureAd")); + .AddMicrosoftIdentityWebApi(Configuration.GetSection("AzureAd")); #endif #elif (IndividualB2CAuth) services.AddAuthentication(JwtBearerDefaults.AuthenticationScheme) From a75f06ef526918a1151e8134403180c4e5119d9d Mon Sep 17 00:00:00 2001 From: John Luo Date: Fri, 28 Aug 2020 11:30:31 -0700 Subject: [PATCH 6/7] Whitespace --- .../content/BlazorServerWeb-CSharp/Startup.cs | 4 ++-- .../content/RazorPagesWeb-CSharp/Startup.cs | 6 +++--- .../content/StarterWeb-CSharp/Startup.cs | 4 ++-- 3 files changed, 7 insertions(+), 7 deletions(-) diff --git a/src/ProjectTemplates/Web.ProjectTemplates/content/BlazorServerWeb-CSharp/Startup.cs b/src/ProjectTemplates/Web.ProjectTemplates/content/BlazorServerWeb-CSharp/Startup.cs index 3802eb045ac9..652c82a57099 100644 --- a/src/ProjectTemplates/Web.ProjectTemplates/content/BlazorServerWeb-CSharp/Startup.cs +++ b/src/ProjectTemplates/Web.ProjectTemplates/content/BlazorServerWeb-CSharp/Startup.cs @@ -107,7 +107,7 @@ public void ConfigureServices(IServiceCollection services) #endif #if (OrganizationalAuth || IndividualB2CAuth) services.AddControllersWithViews() - .AddMicrosoftIdentityUI(); + .AddMicrosoftIdentityUI(); services.AddAuthorization(options => { @@ -119,7 +119,7 @@ public void ConfigureServices(IServiceCollection services) services.AddRazorPages(); #if (OrganizationalAuth || IndividualB2CAuth) services.AddServerSideBlazor() - .AddMicrosoftIdentityConsentHandler(); + .AddMicrosoftIdentityConsentHandler(); #else services.AddServerSideBlazor(); #endif diff --git a/src/ProjectTemplates/Web.ProjectTemplates/content/RazorPagesWeb-CSharp/Startup.cs b/src/ProjectTemplates/Web.ProjectTemplates/content/RazorPagesWeb-CSharp/Startup.cs index d78a9e3e66f0..0114d620818c 100644 --- a/src/ProjectTemplates/Web.ProjectTemplates/content/RazorPagesWeb-CSharp/Startup.cs +++ b/src/ProjectTemplates/Web.ProjectTemplates/content/RazorPagesWeb-CSharp/Startup.cs @@ -103,11 +103,11 @@ public void ConfigureServices(IServiceCollection services) options.FallbackPolicy = options.DefaultPolicy; }); services.AddRazorPages() - .AddMvcOptions(options => {}) - .AddMicrosoftIdentityUI(); + .AddMvcOptions(options => {}) + .AddMicrosoftIdentityUI(); #elif (IndividualB2CAuth) services.AddRazorPages() - .AddMicrosoftIdentityUI(); + .AddMicrosoftIdentityUI(); #else services.AddRazorPages(); #endif diff --git a/src/ProjectTemplates/Web.ProjectTemplates/content/StarterWeb-CSharp/Startup.cs b/src/ProjectTemplates/Web.ProjectTemplates/content/StarterWeb-CSharp/Startup.cs index a745a63580e0..1308fe4cab66 100644 --- a/src/ProjectTemplates/Web.ProjectTemplates/content/StarterWeb-CSharp/Startup.cs +++ b/src/ProjectTemplates/Web.ProjectTemplates/content/StarterWeb-CSharp/Startup.cs @@ -5,9 +5,9 @@ #if (OrganizationalAuth || IndividualB2CAuth) using Microsoft.AspNetCore.Authentication; using Microsoft.AspNetCore.Authentication.OpenIdConnect; +using Microsoft.AspNetCore.Authorization; using Microsoft.Identity.Web; using Microsoft.Identity.Web.UI; -using Microsoft.AspNetCore.Authorization; #endif using Microsoft.AspNetCore.Builder; #if (IndividualLocalAuth) @@ -110,7 +110,7 @@ public void ConfigureServices(IServiceCollection services) #endif #if (OrganizationalAuth || IndividualB2CAuth) services.AddRazorPages() - .AddMicrosoftIdentityUI(); + .AddMicrosoftIdentityUI(); #endif } From d3b2704a863679d10b1b23727782dcad8c4c21e1 Mon Sep 17 00:00:00 2001 From: John Luo Date: Fri, 28 Aug 2020 17:16:46 -0700 Subject: [PATCH 7/7] Microsoft.Graph is brought in transitively by MS.ID.Web --- eng/Dependencies.props | 1 - eng/Versions.props | 1 - .../Web.ProjectTemplates/BlazorServerWeb-CSharp.csproj.in | 1 - .../ComponentsWebAssembly-CSharp.Server.csproj.in | 1 - .../Web.ProjectTemplates/RazorPagesWeb-CSharp.csproj.in | 1 - .../Web.ProjectTemplates/StarterWeb-CSharp.csproj.in | 1 - .../Web.ProjectTemplates/WebApi-CSharp.csproj.in | 1 - 7 files changed, 7 deletions(-) diff --git a/eng/Dependencies.props b/eng/Dependencies.props index d86ab879db42..c242829120b3 100644 --- a/eng/Dependencies.props +++ b/eng/Dependencies.props @@ -168,7 +168,6 @@ and are generated based on the last package release. - diff --git a/eng/Versions.props b/eng/Versions.props index 642ef69ae6f6..b9e867c0e8a7 100644 --- a/eng/Versions.props +++ b/eng/Versions.props @@ -262,7 +262,6 @@ 2.1.90 0.3.1-preview 0.3.1-preview - 3.8.0 $(MessagePackPackageVersion) 4.10.0 0.11.2 diff --git a/src/ProjectTemplates/Web.ProjectTemplates/BlazorServerWeb-CSharp.csproj.in b/src/ProjectTemplates/Web.ProjectTemplates/BlazorServerWeb-CSharp.csproj.in index c47e9753cd6d..bfebf3e0f20e 100644 --- a/src/ProjectTemplates/Web.ProjectTemplates/BlazorServerWeb-CSharp.csproj.in +++ b/src/ProjectTemplates/Web.ProjectTemplates/BlazorServerWeb-CSharp.csproj.in @@ -25,7 +25,6 @@ - diff --git a/src/ProjectTemplates/Web.ProjectTemplates/ComponentsWebAssembly-CSharp.Server.csproj.in b/src/ProjectTemplates/Web.ProjectTemplates/ComponentsWebAssembly-CSharp.Server.csproj.in index 20efa3e9e2ab..c424c7f61450 100644 --- a/src/ProjectTemplates/Web.ProjectTemplates/ComponentsWebAssembly-CSharp.Server.csproj.in +++ b/src/ProjectTemplates/Web.ProjectTemplates/ComponentsWebAssembly-CSharp.Server.csproj.in @@ -40,7 +40,6 @@ - diff --git a/src/ProjectTemplates/Web.ProjectTemplates/RazorPagesWeb-CSharp.csproj.in b/src/ProjectTemplates/Web.ProjectTemplates/RazorPagesWeb-CSharp.csproj.in index 13f3a992758d..d7d8786b54d2 100644 --- a/src/ProjectTemplates/Web.ProjectTemplates/RazorPagesWeb-CSharp.csproj.in +++ b/src/ProjectTemplates/Web.ProjectTemplates/RazorPagesWeb-CSharp.csproj.in @@ -25,7 +25,6 @@ - diff --git a/src/ProjectTemplates/Web.ProjectTemplates/StarterWeb-CSharp.csproj.in b/src/ProjectTemplates/Web.ProjectTemplates/StarterWeb-CSharp.csproj.in index 205c905eb649..2f77d16ec447 100644 --- a/src/ProjectTemplates/Web.ProjectTemplates/StarterWeb-CSharp.csproj.in +++ b/src/ProjectTemplates/Web.ProjectTemplates/StarterWeb-CSharp.csproj.in @@ -25,7 +25,6 @@ - diff --git a/src/ProjectTemplates/Web.ProjectTemplates/WebApi-CSharp.csproj.in b/src/ProjectTemplates/Web.ProjectTemplates/WebApi-CSharp.csproj.in index fd6d56ec32d3..064078a06d2f 100644 --- a/src/ProjectTemplates/Web.ProjectTemplates/WebApi-CSharp.csproj.in +++ b/src/ProjectTemplates/Web.ProjectTemplates/WebApi-CSharp.csproj.in @@ -10,7 +10,6 @@ -