diff --git a/eng/Dependencies.props b/eng/Dependencies.props
index d86ab879db..c242829120 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 ea2917827a..4cd8dc2f0d 100644
--- a/eng/Versions.props
+++ b/eng/Versions.props
@@ -260,9 +260,8 @@
4.0.4
4.0.4
2.1.90
- 0.2.3-preview
- 0.2.3-preview
- 3.8.0
+ 0.3.1-preview
+ 0.3.1-preview
$(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 c47e9753cd..bfebf3e0f2 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 20efa3e9e2..c424c7f614 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 13f3a99275..d7d8786b54 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 205c905eb6..2f77d16ec4 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 fd6d56ec32..064078a06d 100644
--- a/src/ProjectTemplates/Web.ProjectTemplates/WebApi-CSharp.csproj.in
+++ b/src/ProjectTemplates/Web.ProjectTemplates/WebApi-CSharp.csproj.in
@@ -10,7 +10,6 @@
-
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 463e0eb7ba..020748fbc4 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 858bf6f09f..d3bad28a25 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 fbb0de85f6..0000000000
--- 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 f917fbed58..0000000000
--- 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 5d6c643ca4..0000000000
--- 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 72c0379b57..652c82a570 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.Linq;
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,33 +73,36 @@ namespace BlazorServerWeb_CSharp
.AddEntityFrameworkStores();
#elif (OrganizationalAuth)
#if (GenerateApiOrGraph)
- string[] scopes = Configuration.GetValue("CalledApi:CalledApiScopes")?.Split(' ');
+ var initialScopes = Configuration.GetValue("DownstreamApi:Scopes")?.Split(' ');
+
#endif
+ services.AddAuthentication(OpenIdConnectDefaults.AuthenticationScheme)
#if (GenerateApiOrGraph)
- services.AddMicrosoftWebAppAuthentication(Configuration, "AzureAd")
- .AddMicrosoftWebAppCallsWebApi(Configuration, scopes, "AzureAd")
- .AddInMemoryTokenCaches();
-#else
- services.AddMicrosoftWebAppAuthentication(Configuration, "AzureAd");
-#endif
+ .AddMicrosoftIdentityWebApp(Configuration.GetSection("AzureAd"))
+ .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
+ .AddMicrosoftIdentityWebApp(Configuration.GetSection("AzureAd"));
#endif
#elif (IndividualB2CAuth)
#if (GenerateApi)
- string[] scopes = Configuration.GetValue("CalledApi:CalledApiScopes")?.Split(' ');
-#endif
-#if (GenerateApi)
- services.AddMicrosoftWebAppAuthentication(Configuration, "AzureAdB2C")
- .AddMicrosoftWebAppCallsWebApi(Configuration, scopes, "AzureAdB2C")
- .AddInMemoryTokenCaches();
+ var initialScopes = Configuration.GetValue("DownstreamApi:Scopes")?.Split(' ');
- services.AddDownstreamWebApiService(Configuration);
+#endif
+ services.AddAuthentication(OpenIdConnectDefaults.AuthenticationScheme)
+#if (GenerateApi)
+ .AddMicrosoftIdentityWebApp(Configuration.GetSection("AzureAdB2C"))
+ .EnableTokenAcquisitionToCallDownstreamApi(initialScopes)
+ .AddDownstreamWebApi("DownstreamApi", Configuration.GetSection("DownstreamApi"))
+ .AddInMemoryTokenCaches();
#else
- services.AddMicrosoftWebAppAuthentication(Configuration, "AzureAdB2C");
+ .AddMicrosoftIdentityWebApp(Configuration.GetSection("AzureAdB2C"));
#endif
#endif
#if (OrganizationalAuth || IndividualB2CAuth)
diff --git a/src/ProjectTemplates/Web.ProjectTemplates/content/BlazorServerWeb-CSharp/appsettings.json b/src/ProjectTemplates/Web.ProjectTemplates/content/BlazorServerWeb-CSharp/appsettings.json
index b2b1a5fa42..1c0516223b 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": {
-//#if (MultiOrgAuth)
-// "Instance": "https:////login.microsoftonline.com/common",
-//#elseif (SingleOrgAuth)
// "Instance": "https:////login.microsoftonline.com/",
+//#if (MultiOrgAuth)
+// "TenantId": "common",
+//#elseif (SingleOrgAuth)
// "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 74be1e1bff..0e8fd2766f 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": {
@@ -470,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": {
@@ -487,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",
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 99971dfae8..b2e4339447 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 System.Net.Http;
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 @@ namespace ComponentsWebAssembly_CSharp.Server.Controllers
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 @@ namespace ComponentsWebAssembly_CSharp.Server.Controllers
{
HttpContext.VerifyUserHasAnyAcceptedScope(scopeRequiredByApi);
- string downstreamApiResult = await _downstreamWebApi.CallWebApiAsync();
+ using var response = await _downstreamWebApi.CallWebApiForUserAsync("DownstreamApi").ConfigureAwait(false);
+ if (response.StatusCode == System.Net.HttpStatusCode.OK)
+ {
+ var apiResult = await response.Content.ReadAsStringAsync().ConfigureAwait(false);
+ // Do something
+ }
+ else
+ {
+ var 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 @@ namespace ComponentsWebAssembly_CSharp.Server.Controllers
{
_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 0c7d0fcb96..0000000000
--- 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 6702cc3371..0000000000
--- 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 a6cc2b080e..0000000000
--- 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 80c2560a8e..3ebd062721 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 @@ using Microsoft.AspNetCore.Authentication;
#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 @@ namespace ComponentsWebAssembly_CSharp.Server
.AddIdentityServerJwt();
#endif
#if (OrganizationalAuth)
+ services.AddAuthentication(JwtBearerDefaults.AuthenticationScheme)
#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
+ .AddMicrosoftIdentityWebApi(Configuration.GetSection("AzureAd"))
+ .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
+ .AddMicrosoftIdentityWebApi(Configuration.GetSection("AzureAd"));
#endif
#elif (IndividualB2CAuth)
+ services.AddAuthentication(JwtBearerDefaults.AuthenticationScheme)
#if (GenerateApi)
- services.AddMicrosoftWebApiAuthentication(Configuration, "AzureAdB2C")
- .AddMicrosoftWebApiCallsWebApi(Configuration, "AzureAdB2C")
- .AddInMemoryTokenCaches();
-
- services.AddDownstreamWebApiService(Configuration);
+ .AddMicrosoftIdentityWebApi(Configuration.GetSection("AzureAdB2C"))
+ .EnableTokenAcquisitionToCallDownstreamApi()
+ .AddDownstreamWebApi("DownstreamApi", Configuration.GetSection("DownstreamApi"))
+ .AddInMemoryTokenCaches();
#else
- services.AddMicrosoftWebApiAuthentication(Configuration, "AzureAdB2C");
+ .AddMicrosoftIdentityWebApi(Configuration.GetSection("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 da1c94c1b9..b5f991734b 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": {
-//#if (!SingleOrgAuth)
-// "Instance": "https:////login.microsoftonline.com/common",
-//#else
// "Instance": "https:////login.microsoftonline.com/",
+//#if (!SingleOrgAuth)
+// "TenantId": "common",
+//#else
// "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 f939274638..a13d60b66a 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 bac6493345..35b806d386 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 @@ using Microsoft.Extensions.Logging;
namespace Company.WebApplication1.Pages
{
#if (GenerateApiOrGraph)
- [AuthorizeForScopes(ScopeKeySection = "CalledApi:CalledApiScopes")]
+ [AuthorizeForScopes(ScopeKeySection = "DownstreamApi:Scopes")]
#endif
public class IndexModel : PageModel
{
@@ -36,7 +36,17 @@ namespace Company.WebApplication1.Pages
public async Task OnGet()
{
- ViewData["ApiResult"] = await _downstreamWebApi.CallWebApiAsync();
+ using var response = await _downstreamWebApi.CallWebApiForUserAsync("DownstreamApi").ConfigureAwait(false);
+ if (response.StatusCode == System.Net.HttpStatusCode.OK)
+ {
+ var apiResult = await response.Content.ReadAsStringAsync().ConfigureAwait(false);
+ ViewData["ApiResult"] = apiResult;
+ }
+ else
+ {
+ var 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 4806ca192d..0000000000
--- 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 2e805a9688..0000000000
--- 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 8042149d82..0000000000
--- 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 c181d5a49c..0114d62081 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.Linq;
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 @@ namespace Company.WebApplication1
.AddEntityFrameworkStores();
#elif (OrganizationalAuth)
#if (GenerateApiOrGraph)
- services.AddMicrosoftWebAppAuthentication(Configuration, "AzureAd")
- .AddMicrosoftWebAppCallsWebApi(Configuration, "AzureAd")
- .AddInMemoryTokenCaches();
-#else
- services.AddMicrosoftWebAppAuthentication(Configuration, "AzureAd");
+ var initialScopes = Configuration.GetValue("DownstreamApi:Scopes")?.Split(' ');
+
#endif
+ services.AddAuthentication(OpenIdConnectDefaults.AuthenticationScheme)
+#if (GenerateApiOrGraph)
+ .AddMicrosoftIdentityWebApp(Configuration.GetSection("AzureAd"))
+ .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
+ .AddMicrosoftIdentityWebApp(Configuration.GetSection("AzureAd"));
#endif
#elif (IndividualB2CAuth)
#if (GenerateApi)
- services.AddMicrosoftWebAppAuthentication(Configuration, "AzureAdB2C")
- .AddMicrosoftWebAppCallsWebApi(Configuration, "AzureAdB2C")
- .AddInMemoryTokenCaches();
+ var initialScopes = Configuration.GetValue("DownstreamApi:Scopes")?.Split(' ');
- services.AddDownstreamWebApiService(Configuration);
+#endif
+ services.AddAuthentication(OpenIdConnectDefaults.AuthenticationScheme)
+#if (GenerateApi)
+ .AddMicrosoftIdentityWebApp(Configuration.GetSection("AzureAdB2C"))
+ .EnableTokenAcquisitionToCallDownstreamApi(initialScopes)
+ .AddDownstreamWebApi("DownstreamApi", Configuration.GetSection("DownstreamApi"))
+ .AddInMemoryTokenCaches();
#else
- services.AddMicrosoftWebAppAuthentication(Configuration, "AzureAdB2C");
+ .AddMicrosoftIdentityWebApp(Configuration.GetSection("AzureAdB2C"));
#endif
#endif
#if (OrganizationalAuth)
diff --git a/src/ProjectTemplates/Web.ProjectTemplates/content/RazorPagesWeb-CSharp/appsettings.json b/src/ProjectTemplates/Web.ProjectTemplates/content/RazorPagesWeb-CSharp/appsettings.json
index c0dd4d1ab9..634fb6db8a 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": {
-//#if (MultiOrgAuth)
-// "Instance": "https:////login.microsoftonline.com/common",
-//#elseif (SingleOrgAuth)
// "Instance": "https:////login.microsoftonline.com/",
+//#if (MultiOrgAuth)
+// "TenantId": "common",
+//#elseif (SingleOrgAuth)
// "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 f0e86147b2..ac54ee3f91 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 cd05892b06..4aea265a2e 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 @@ namespace Company.WebApplication1.Controllers
_downstreamWebApi = downstreamWebApi;
}
- [AuthorizeForScopes(ScopeKeySection = "CalledApi:CalledApiScopes")]
+ [AuthorizeForScopes(ScopeKeySection = "DownstreamApi:Scopes")]
public async Task Index()
{
- ViewData["ApiResult"] = await _downstreamWebApi.CallWebApiAsync();
-
+ using var response = await _downstreamWebApi.CallWebApiForUserAsync("DownstreamApi").ConfigureAwait(false);
+ if (response.StatusCode == System.Net.HttpStatusCode.OK)
+ {
+ var apiResult = await response.Content.ReadAsStringAsync().ConfigureAwait(false);
+ ViewData["ApiResult"] = apiResult;
+ }
+ else
+ {
+ var 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 @@ namespace Company.WebApplication1.Controllers
_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 4806ca192d..0000000000
--- 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 2e805a9688..0000000000
--- 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 8042149d82..0000000000
--- 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 f0d19f0712..1308fe4cab 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.Linq;
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)
@@ -74,28 +64,36 @@ namespace Company.WebApplication1
.AddEntityFrameworkStores();
#elif (OrganizationalAuth)
#if (GenerateApiOrGraph)
- services.AddMicrosoftWebAppAuthentication(Configuration, "AzureAd")
- .AddMicrosoftWebAppCallsWebApi(Configuration, "AzureAd")
- .AddInMemoryTokenCaches();
-#else
- services.AddMicrosoftWebAppAuthentication(Configuration, "AzureAd");
+ var initialScopes = Configuration.GetValue("DownstreamApi:Scopes")?.Split(' ');
+
#endif
+ services.AddAuthentication(OpenIdConnectDefaults.AuthenticationScheme)
+#if (GenerateApiOrGraph)
+ .AddMicrosoftIdentityWebApp(Configuration.GetSection("AzureAd"))
+ .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
+ .AddMicrosoftIdentityWebApp(Configuration.GetSection("AzureAd"));
#endif
#elif (IndividualB2CAuth)
#if (GenerateApi)
- services.AddMicrosoftWebAppAuthentication(Configuration, "AzureAdB2C")
- .AddMicrosoftWebAppCallsWebApi(Configuration, "AzureAdB2C")
- .AddInMemoryTokenCaches();
+ var initialScopes = Configuration.GetValue("DownstreamApi:Scopes")?.Split(' ');
- services.AddDownstreamWebApiService(Configuration);
+#endif
+ services.AddAuthentication(OpenIdConnectDefaults.AuthenticationScheme)
+#if (GenerateApi)
+ .AddMicrosoftIdentityWebApp(Configuration.GetSection("AzureAdB2C"))
+ .EnableTokenAcquisitionToCallDownstreamApi(initialScopes)
+ .AddDownstreamWebApi("DownstreamApi", Configuration.GetSection("DownstreamApi"))
+ .AddInMemoryTokenCaches();
#else
- services.AddMicrosoftWebAppAuthentication(Configuration, "AzureAdB2C");
+ .AddMicrosoftIdentityWebApp(Configuration.GetSection("AzureAdB2C"));
#endif
#endif
#if (OrganizationalAuth)
diff --git a/src/ProjectTemplates/Web.ProjectTemplates/content/StarterWeb-CSharp/appsettings.json b/src/ProjectTemplates/Web.ProjectTemplates/content/StarterWeb-CSharp/appsettings.json
index f72b1ef631..f6582afb07 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": {
-//#if (MultiOrgAuth)
-// "Instance": "https:////login.microsoftonline.com/common",
-//#elseif (SingleOrgAuth)
// "Instance": "https:////login.microsoftonline.com/",
+//#if (MultiOrgAuth)
+// "TenantId": "common",
+//#elseif (SingleOrgAuth)
// "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 af0458974c..b16a1f9a27 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": ""
+ },
"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 f11b772861..78260944d2 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 584c64869c..0cf62870ff 100644
--- a/src/ProjectTemplates/Web.ProjectTemplates/content/WebApi-CSharp/Controllers/WeatherForecastController.cs
+++ b/src/ProjectTemplates/Web.ProjectTemplates/content/WebApi-CSharp/Controllers/WeatherForecastController.cs
@@ -36,8 +36,8 @@ namespace Company.WebApplication1.Controllers
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" };
+ // 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)
private readonly IDownstreamWebApi _downstreamWebApi;
@@ -54,7 +54,17 @@ namespace Company.WebApplication1.Controllers
{
HttpContext.VerifyUserHasAnyAcceptedScope(scopeRequiredByApi);
- string downstreamApiResult = await _downstreamWebApi.CallWebApiAsync();
+ using var response = await _downstreamWebApi.CallWebApiForUserAsync("DownstreamApi").ConfigureAwait(false);
+ if (response.StatusCode == System.Net.HttpStatusCode.OK)
+ {
+ var apiResult = await response.Content.ReadAsStringAsync().ConfigureAwait(false);
+ // Do something
+ }
+ else
+ {
+ var 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 4806ca192d..0000000000
--- 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 2e805a9688..0000000000
--- 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 8042149d82..0000000000
--- 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 0e935d107a..e1fedd249a 100644
--- a/src/ProjectTemplates/Web.ProjectTemplates/content/WebApi-CSharp/Startup.cs
+++ b/src/ProjectTemplates/Web.ProjectTemplates/content/WebApi-CSharp/Startup.cs
@@ -10,14 +10,8 @@ using Microsoft.AspNetCore.HttpsPolicy;
using Microsoft.AspNetCore.Mvc;
#if (OrganizationalAuth || IndividualB2CAuth)
using Microsoft.AspNetCore.Authentication;
-#endif
-#if (OrganizationalAuth)
+using Microsoft.AspNetCore.Authentication.JwtBearer;
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 @@ namespace Company.WebApplication1
public void ConfigureServices(IServiceCollection services)
{
#if (OrganizationalAuth)
+ services.AddAuthentication(JwtBearerDefaults.AuthenticationScheme)
#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
+ .AddMicrosoftIdentityWebApi(Configuration.GetSection("AzureAd"))
+ .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
+ .AddMicrosoftIdentityWebApi(Configuration.GetSection("AzureAd"));
#endif
#elif (IndividualB2CAuth)
+ services.AddAuthentication(JwtBearerDefaults.AuthenticationScheme)
#if (GenerateApi)
- services.AddMicrosoftWebApiAuthentication(Configuration, "AzureAdB2C")
- .AddMicrosoftWebApiCallsWebApi(Configuration, "AzureAdB2C")
- .AddInMemoryTokenCaches();
-
- services.AddDownstreamWebApiService(Configuration);
+ .AddMicrosoftIdentityWebApi(Configuration.GetSection("AzureAdB2C"))
+ .EnableTokenAcquisitionToCallDownstreamApi()
+ .AddDownstreamWebApi("DownstreamApi", Configuration.GetSection("DownstreamApi"))
+ .AddInMemoryTokenCaches();
#else
- services.AddMicrosoftWebApiAuthentication(Configuration, "AzureAdB2C");
+ .AddMicrosoftIdentityWebApi(Configuration.GetSection("AzureAdB2C"));
#endif
#endif
@@ -98,6 +89,7 @@ namespace Company.WebApplication1
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 cd6a5721b6..2b965fcc65 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": {
-//#if (!SingleOrgAuth)
-// "Instance": "https:////login.microsoftonline.com/common",
-//#else
// "Instance": "https:////login.microsoftonline.com/",
+//#if (!SingleOrgAuth)
+// "TenantId": "common",
+//#else
// "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": {