+
-
diff --git a/src/ProjectTemplates/Web.ProjectTemplates/content/ComponentsWebAssembly-CSharp/Client/Shared/MainLayout.razor.css b/src/ProjectTemplates/Web.ProjectTemplates/content/ComponentsWebAssembly-CSharp/Client/Shared/MainLayout.razor.css
new file mode 100644
index 000000000000..61a993539a38
--- /dev/null
+++ b/src/ProjectTemplates/Web.ProjectTemplates/content/ComponentsWebAssembly-CSharp/Client/Shared/MainLayout.razor.css
@@ -0,0 +1,70 @@
+.page {
+ position: relative;
+ display: flex;
+ flex-direction: column;
+}
+
+.main {
+ flex: 1;
+}
+
+.sidebar {
+ background-image: linear-gradient(180deg, rgb(5, 39, 103) 0%, #3a0647 70%);
+}
+
+.top-row {
+ background-color: #f7f7f7;
+ border-bottom: 1px solid #d6d5d5;
+ justify-content: flex-end;
+ height: 3.5rem;
+ display: flex;
+ align-items: center;
+}
+
+ .top-row ::deep a, .top-row .btn-link {
+ white-space: nowrap;
+ margin-left: 1.5rem;
+ }
+
+ .top-row a:first-child {
+ overflow: hidden;
+ text-overflow: ellipsis;
+ }
+
+@media (max-width: 767.98px) {
+ .top-row:not(.auth) {
+ display: none;
+ }
+
+ .top-row.auth {
+ justify-content: space-between;
+ }
+
+ .top-row a, .top-row .btn-link {
+ margin-left: 0;
+ }
+}
+
+@media (min-width: 768px) {
+ .page {
+ flex-direction: row;
+ }
+
+ .sidebar {
+ width: 250px;
+ height: 100vh;
+ position: sticky;
+ top: 0;
+ }
+
+ .top-row {
+ position: sticky;
+ top: 0;
+ z-index: 1;
+ }
+
+ .main > div {
+ padding-left: 2rem !important;
+ padding-right: 1.5rem !important;
+ }
+}
diff --git a/src/ProjectTemplates/Web.ProjectTemplates/content/ComponentsWebAssembly-CSharp/Client/Shared/NavMenu.razor.css b/src/ProjectTemplates/Web.ProjectTemplates/content/ComponentsWebAssembly-CSharp/Client/Shared/NavMenu.razor.css
new file mode 100644
index 000000000000..622671ec4c87
--- /dev/null
+++ b/src/ProjectTemplates/Web.ProjectTemplates/content/ComponentsWebAssembly-CSharp/Client/Shared/NavMenu.razor.css
@@ -0,0 +1,62 @@
+.navbar-toggler {
+ background-color: rgba(255, 255, 255, 0.1);
+}
+
+.top-row {
+ height: 3.5rem;
+ background-color: rgba(0,0,0,0.4);
+}
+
+.navbar-brand {
+ font-size: 1.1rem;
+}
+
+.oi {
+ width: 2rem;
+ font-size: 1.1rem;
+ vertical-align: text-top;
+ top: -2px;
+}
+
+.nav-item {
+ font-size: 0.9rem;
+ padding-bottom: 0.5rem;
+}
+
+ .nav-item:first-of-type {
+ padding-top: 1rem;
+ }
+
+ .nav-item:last-of-type {
+ padding-bottom: 1rem;
+ }
+
+ .nav-item ::deep a {
+ color: #d7d7d7;
+ border-radius: 4px;
+ height: 3rem;
+ display: flex;
+ align-items: center;
+ line-height: 3rem;
+ }
+
+.nav-item ::deep a.active {
+ background-color: rgba(255,255,255,0.25);
+ color: white;
+}
+
+.nav-item ::deep a:hover {
+ background-color: rgba(255,255,255,0.1);
+ color: white;
+}
+
+@media (min-width: 768px) {
+ .navbar-toggler {
+ display: none;
+ }
+
+ .collapse {
+ /* Never collapse the sidebar for wide screens */
+ display: block;
+ }
+}
diff --git a/src/ProjectTemplates/Web.ProjectTemplates/content/ComponentsWebAssembly-CSharp/Client/wwwroot/css/app.css b/src/ProjectTemplates/Web.ProjectTemplates/content/ComponentsWebAssembly-CSharp/Client/wwwroot/css/app.css
index 76b9667ff54a..82fc22a39385 100644
--- a/src/ProjectTemplates/Web.ProjectTemplates/content/ComponentsWebAssembly-CSharp/Client/wwwroot/css/app.css
+++ b/src/ProjectTemplates/Web.ProjectTemplates/content/ComponentsWebAssembly-CSharp/Client/wwwroot/css/app.css
@@ -14,98 +14,10 @@ a, .btn-link {
border-color: #1861ac;
}
-app {
- position: relative;
- display: flex;
- flex-direction: column;
-}
-
-.top-row {
- height: 3.5rem;
- display: flex;
- align-items: center;
- z-index: 10;
-}
-
-.main {
- flex: 1;
-}
-
- .main .top-row {
- background-color: #f7f7f7;
- border-bottom: 1px solid #d6d5d5;
- justify-content: flex-end;
- }
-
- .main .top-row > a, .main .top-row .btn-link {
- white-space: nowrap;
- margin-left: 1.5rem;
- }
-
-.main .top-row a:first-child {
- overflow: hidden;
- text-overflow: ellipsis;
-}
-
-.sidebar {
- background-image: linear-gradient(180deg, rgb(5, 39, 103) 0%, #3a0647 70%);
-}
-
- .sidebar .top-row {
- background-color: rgba(0,0,0,0.4);
- }
-
- .sidebar .navbar-brand {
- font-size: 1.1rem;
- }
-
- .sidebar .oi {
- width: 2rem;
- font-size: 1.1rem;
- vertical-align: text-top;
- top: -2px;
- }
-
- .sidebar .nav-item {
- font-size: 0.9rem;
- padding-bottom: 0.5rem;
- }
-
- .sidebar .nav-item:first-of-type {
- padding-top: 1rem;
- }
-
- .sidebar .nav-item:last-of-type {
- padding-bottom: 1rem;
- }
-
- .sidebar .nav-item a {
- color: #d7d7d7;
- border-radius: 4px;
- height: 3rem;
- display: flex;
- align-items: center;
- line-height: 3rem;
- }
-
- .sidebar .nav-item a.active {
- background-color: rgba(255,255,255,0.25);
- color: white;
- }
-
- .sidebar .nav-item a:hover {
- background-color: rgba(255,255,255,0.1);
- color: white;
- }
-
.content {
padding-top: 1.1rem;
}
-.navbar-toggler {
- background-color: rgba(255, 255, 255, 0.1);
-}
-
.valid.modified:not([type=checkbox]) {
outline: 1px solid #26b050;
}
@@ -130,56 +42,9 @@ app {
z-index: 1000;
}
-#blazor-error-ui .dismiss {
- cursor: pointer;
- position: absolute;
- right: 0.75rem;
- top: 0.5rem;
-}
-
-@media (max-width: 767.98px) {
- .main .top-row:not(.auth) {
- display: none;
- }
-
- .main .top-row.auth {
- justify-content: space-between;
- }
-
- .main .top-row a, .main .top-row .btn-link {
- margin-left: 0;
- }
-}
-
-@media (min-width: 768px) {
- app {
- flex-direction: row;
- }
-
- .sidebar {
- width: 250px;
- height: 100vh;
- position: sticky;
- top: 0;
- }
-
- .main .top-row {
- position: sticky;
- top: 0;
- z-index: 1;
- }
-
- .main > div {
- padding-left: 2rem !important;
- padding-right: 1.5rem !important;
- }
-
- .navbar-toggler {
- display: none;
+ #blazor-error-ui .dismiss {
+ cursor: pointer;
+ position: absolute;
+ right: 0.75rem;
+ top: 0.5rem;
}
-
- .sidebar .collapse {
- /* Never collapse the sidebar for wide screens */
- display: block;
- }
-}
diff --git a/src/ProjectTemplates/Web.ProjectTemplates/content/ComponentsWebAssembly-CSharp/Client/wwwroot/index.html b/src/ProjectTemplates/Web.ProjectTemplates/content/ComponentsWebAssembly-CSharp/Client/wwwroot/index.html
index 7e39788a581e..d29be69affc5 100644
--- a/src/ProjectTemplates/Web.ProjectTemplates/content/ComponentsWebAssembly-CSharp/Client/wwwroot/index.html
+++ b/src/ProjectTemplates/Web.ProjectTemplates/content/ComponentsWebAssembly-CSharp/Client/wwwroot/index.html
@@ -8,6 +8,7 @@
+
@@ -15,7 +16,7 @@
-
Loading...
+
Loading...
An unhandled error has occurred.
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 117055576dec..e39c9297a7aa 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
@@ -1,13 +1,25 @@
-using ComponentsWebAssembly_CSharp.Shared;
-using System;
+using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
#if (!NoAuth)
using Microsoft.AspNetCore.Authorization;
#endif
+#if (GenerateApi)
+using Microsoft.Extensions.Configuration;
+using Microsoft.Identity.Web;
+using System.Net;
+using System.Net.Http;
+#endif
+#if (GenerateGraph)
+using Microsoft.Graph;
+#endif
using Microsoft.AspNetCore.Mvc;
+#if (OrganizationalAuth || IndividualB2CAuth)
+using Microsoft.Identity.Web.Resource;
+#endif
using Microsoft.Extensions.Logging;
+using ComponentsWebAssembly_CSharp.Shared;
namespace ComponentsWebAssembly_CSharp.Server.Controllers
{
@@ -23,16 +35,76 @@ public class WeatherForecastController : ControllerBase
"Freezing", "Bracing", "Chilly", "Cool", "Mild", "Warm", "Balmy", "Hot", "Sweltering", "Scorching"
};
- private readonly ILogger
logger;
+ 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" };
+
+#if (GenerateApi)
+ private readonly IDownstreamWebApi _downstreamWebApi;
+
+ public WeatherForecastController(ILogger logger,
+ IDownstreamWebApi downstreamWebApi)
+ {
+ _logger = logger;
+ _downstreamWebApi = downstreamWebApi;
+ }
+
+ [HttpGet]
+ public async Task> Get()
+ {
+ HttpContext.VerifyUserHasAnyAcceptedScope(scopeRequiredByApi);
+
+ string downstreamApiResult = await _downstreamWebApi.CallWebApiAsync();
+
+ var rng = new Random();
+ return Enumerable.Range(1, 5).Select(index => new WeatherForecast
+ {
+ Date = DateTime.Now.AddDays(index),
+ TemperatureC = rng.Next(-20, 55),
+ Summary = Summaries[rng.Next(Summaries.Length)]
+ })
+ .ToArray();
+ }
+
+#elseif (GenerateGraph)
+ private readonly GraphServiceClient _graphServiceClient;
+
+ public WeatherForecastController(ILogger logger,
+ GraphServiceClient graphServiceClient)
+ {
+ _logger = logger;
+ _graphServiceClient = graphServiceClient;
+ }
+
+ [HttpGet]
+ public async Task> Get()
+ {
+ HttpContext.VerifyUserHasAnyAcceptedScope(scopeRequiredByApi);
+ var user = await _graphServiceClient.Me.Request().GetAsync();
+ var rng = new Random();
+ return Enumerable.Range(1, 5).Select(index => new WeatherForecast
+ {
+ Date = DateTime.Now.AddDays(index),
+ TemperatureC = rng.Next(-20, 55),
+ Summary = Summaries[rng.Next(Summaries.Length)]
+ })
+ .ToArray();
+ }
+#else
public WeatherForecastController(ILogger logger)
{
- this.logger = logger;
+ _logger = logger;
}
[HttpGet]
public IEnumerable Get()
{
+#if (OrganizationalAuth || IndividualB2CAuth)
+ HttpContext.VerifyUserHasAnyAcceptedScope(scopeRequiredByApi);
+
+#endif
var rng = new Random();
return Enumerable.Range(1, 5).Select(index => new WeatherForecast
{
@@ -42,5 +114,6 @@ public IEnumerable Get()
})
.ToArray();
}
+#endif
}
}
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
new file mode 100644
index 000000000000..0c7d0fcb9674
--- /dev/null
+++ b/src/ProjectTemplates/Web.ProjectTemplates/content/ComponentsWebAssembly-CSharp/Server/Services/DownstreamWebApi.cs
@@ -0,0 +1,72 @@
+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
new file mode 100644
index 000000000000..6702cc33714f
--- /dev/null
+++ b/src/ProjectTemplates/Web.ProjectTemplates/content/ComponentsWebAssembly-CSharp/Server/Services/MicrosoftGraphServiceExtensions.cs
@@ -0,0 +1,36 @@
+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
new file mode 100644
index 000000000000..a6cc2b080ec4
--- /dev/null
+++ b/src/ProjectTemplates/Web.ProjectTemplates/content/ComponentsWebAssembly-CSharp/Server/Services/TokenAcquisitionCredentialProvider.cs
@@ -0,0 +1,27 @@
+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 e52120ff08ce..5899ece40e8a 100644
--- a/src/ProjectTemplates/Web.ProjectTemplates/content/ComponentsWebAssembly-CSharp/Server/Startup.cs
+++ b/src/ProjectTemplates/Web.ProjectTemplates/content/ComponentsWebAssembly-CSharp/Server/Startup.cs
@@ -1,17 +1,10 @@
#if (OrganizationalAuth || IndividualB2CAuth || IndividualLocalAuth)
using Microsoft.AspNetCore.Authentication;
#endif
-#if (OrganizationalAuth)
-using Microsoft.AspNetCore.Authentication.AzureAD.UI;
-#endif
-#if (IndividualB2CAuth)
-using Microsoft.AspNetCore.Authentication.AzureADB2C.UI;
-#endif
using Microsoft.AspNetCore.Builder;
-#if (IndividualLocalAuth)
-using Microsoft.AspNetCore.Components.Authorization;
-using Microsoft.AspNetCore.Identity;
-using Microsoft.AspNetCore.Identity.UI;
+#if (OrganizationalAuth || IndividualB2CAuth)
+using Microsoft.Identity.Web;
+using Microsoft.Identity.Web.TokenCacheProviders.InMemory;
#endif
#if (RequiresHttps)
using Microsoft.AspNetCore.HttpsPolicy;
@@ -29,6 +22,9 @@
using ComponentsWebAssembly_CSharp.Server.Data;
using ComponentsWebAssembly_CSharp.Server.Models;
#endif
+#if (GenerateGraph)
+using Microsoft.Graph;
+#endif
namespace ComponentsWebAssembly_CSharp.Server
{
@@ -47,13 +43,13 @@ public void ConfigureServices(IServiceCollection services)
{
#if (IndividualLocalAuth)
services.AddDbContext(options =>
- #if (UseLocalDB)
+#if (UseLocalDB)
options.UseSqlServer(
Configuration.GetConnectionString("DefaultConnection")));
- #else
+#else
options.UseSqlite(
Configuration.GetConnectionString("DefaultConnection")));
- #endif
+#endif
services.AddDefaultIdentity(options => options.SignIn.RequireConfirmedAccount = true)
.AddEntityFrameworkStores();
@@ -65,15 +61,32 @@ public void ConfigureServices(IServiceCollection services)
.AddIdentityServerJwt();
#endif
#if (OrganizationalAuth)
-#pragma warning disable CS0618 // Type or member is obsolete
- services.AddAuthentication(AzureADDefaults.BearerAuthenticationScheme)
- .AddAzureADBearer(options => Configuration.Bind("AzureAd", options));
-#pragma warning restore CS0618 // Type or member is obsolete
+#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
+#if (GenerateApi)
+ services.AddDownstreamWebApiService(Configuration);
+#endif
+#if (GenerateGraph)
+ services.AddMicrosoftGraph(Configuration.GetValue("CalledApi:CalledApiScopes")?.Split(' '),
+ Configuration.GetValue("CalledApi:CalledApiUrl"));
+#endif
#elif (IndividualB2CAuth)
-#pragma warning disable CS0618 // Type or member is obsolete
- services.AddAuthentication(AzureADB2CDefaults.BearerAuthenticationScheme)
- .AddAzureADB2CBearer(options => Configuration.Bind("AzureAdB2C", options));
-#pragma warning restore CS0618 // Type or member is obsolete
+#if (GenerateApi)
+ services.AddMicrosoftWebApiAuthentication(Configuration, "AzureAdB2C")
+ .AddMicrosoftWebApiCallsWebApi(Configuration, "AzureAdB2C")
+ .AddInMemoryTokenCaches();
+
+ services.AddDownstreamWebApiService(Configuration);
+#else
+ services.AddMicrosoftWebApiAuthentication(Configuration, "AzureAdB2C");
+#endif
#endif
services.AddControllersWithViews();
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 fe7926d97302..da1c94c1b9a7 100644
--- a/src/ProjectTemplates/Web.ProjectTemplates/content/ComponentsWebAssembly-CSharp/Server/appsettings.json
+++ b/src/ProjectTemplates/Web.ProjectTemplates/content/ComponentsWebAssembly-CSharp/Server/appsettings.json
@@ -12,23 +12,51 @@
// "Instance": "https:////aadB2CInstance.b2clogin.com/",
// "ClientId": "11111111-1111-1111-11111111111111111",
// "Domain": "qualified.domain.name",
+//#if (GenerateApi)
+// "ClientSecret": "secret-from-app-registration",
+// "ClientCertificates" : [
+// ],
+//#endif
// "SignUpSignInPolicyId": "MySignUpSignInPolicyId"
// },
////#elseif (OrganizationalAuth)
// "AzureAd": {
+//#if (!SingleOrgAuth)
+// "Instance": "https:////login.microsoftonline.com/common",
+//#else
// "Instance": "https:////login.microsoftonline.com/",
// "Domain": "qualified.domain.name",
// "TenantId": "22222222-2222-2222-2222-222222222222",
+//#endif
// "ClientId": "11111111-1111-1111-11111111111111111",
+//#if (GenerateApiOrGraph)
+// "ClientSecret": "secret-from-app-registration",
+// "ClientCertificates" : [
+// ],
+//#endif
+// "CallbackPath": "/signin-oidc"
+// },
+////#endif
+////#if (GenerateApiOrGraph)
+// "CalledApi": {
+// /*
+// 'CalledApiScopes' 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]"
// },
////#endif
"Logging": {
- "LogLevel": {
+ "LogLevel": {
"Default": "Information",
"Microsoft": "Warning",
"Microsoft.Hosting.Lifetime": "Information"
- }
- },
+ }
+ },
////#if (IndividualLocalAuth)
// "IdentityServer": {
// "Clients": {
diff --git a/src/ProjectTemplates/Web.ProjectTemplates/content/RazorPagesWeb-CSharp/Startup.cs b/src/ProjectTemplates/Web.ProjectTemplates/content/RazorPagesWeb-CSharp/Startup.cs
index 242f93d1ee1b..d71d362b793a 100644
--- a/src/ProjectTemplates/Web.ProjectTemplates/content/RazorPagesWeb-CSharp/Startup.cs
+++ b/src/ProjectTemplates/Web.ProjectTemplates/content/RazorPagesWeb-CSharp/Startup.cs
@@ -71,13 +71,12 @@ public void ConfigureServices(IServiceCollection services)
services.AddDefaultIdentity(options => options.SignIn.RequireConfirmedAccount = true)
.AddEntityFrameworkStores();
#elif (OrganizationalAuth)
- services.AddMicrosoftWebAppAuthentication(Configuration, "AzureAd")
#if (GenerateApiOrGraph)
- .AddMicrosoftWebAppCallsWebApi(Configuration,
- "AzureAd")
- .AddInMemoryTokenCaches();
+ services.AddMicrosoftWebAppAuthentication(Configuration, "AzureAd")
+ .AddMicrosoftWebAppCallsWebApi(Configuration, "AzureAd")
+ .AddInMemoryTokenCaches();
#else
- ;
+ services.AddMicrosoftWebAppAuthentication(Configuration, "AzureAd");
#endif
#if (GenerateApi)
services.AddDownstreamWebApiService(Configuration);
@@ -87,15 +86,14 @@ public void ConfigureServices(IServiceCollection services)
Configuration.GetValue("CalledApi:CalledApiUrl"));
#endif
#elif (IndividualB2CAuth)
- services.AddMicrosoftWebAppAuthentication(Configuration, "AzureAdB2C")
#if (GenerateApi)
- .AddMicrosoftWebAppCallsWebApi(Configuration,
- "AzureAdB2C")
- .AddInMemoryTokenCaches();
+ services.AddMicrosoftWebAppAuthentication(Configuration, "AzureAdB2C")
+ .AddMicrosoftWebAppCallsWebApi(Configuration, "AzureAdB2C")
+ .AddInMemoryTokenCaches();
services.AddDownstreamWebApiService(Configuration);
#else
- ;
+ services.AddMicrosoftWebAppAuthentication(Configuration, "AzureAdB2C");
#endif
#endif
#if (OrganizationalAuth)
@@ -106,11 +104,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 72af4c5e5112..dc4a413e8dab 100644
--- a/src/ProjectTemplates/Web.ProjectTemplates/content/StarterWeb-CSharp/Startup.cs
+++ b/src/ProjectTemplates/Web.ProjectTemplates/content/StarterWeb-CSharp/Startup.cs
@@ -71,13 +71,12 @@ public void ConfigureServices(IServiceCollection services)
services.AddDefaultIdentity(options => options.SignIn.RequireConfirmedAccount = true)
.AddEntityFrameworkStores();
#elif (OrganizationalAuth)
- services.AddMicrosoftWebAppAuthentication(Configuration, "AzureAd")
#if (GenerateApiOrGraph)
- .AddMicrosoftWebAppCallsWebApi(Configuration,
- "AzureAd")
- .AddInMemoryTokenCaches();
+ services.AddMicrosoftWebAppAuthentication(Configuration, "AzureAd")
+ .AddMicrosoftWebAppCallsWebApi(Configuration, "AzureAd")
+ .AddInMemoryTokenCaches();
#else
- ;
+ services.AddMicrosoftWebAppAuthentication(Configuration, "AzureAd");
#endif
#if (GenerateApi)
services.AddDownstreamWebApiService(Configuration);
@@ -87,15 +86,14 @@ public void ConfigureServices(IServiceCollection services)
Configuration.GetValue("CalledApi:CalledApiUrl"));
#endif
#elif (IndividualB2CAuth)
- services.AddMicrosoftWebAppAuthentication(Configuration, "AzureAdB2C")
#if (GenerateApi)
- .AddMicrosoftWebAppCallsWebApi(Configuration,
- "AzureAdB2C")
- .AddInMemoryTokenCaches();
+ services.AddMicrosoftWebAppAuthentication(Configuration, "AzureAdB2C")
+ .AddMicrosoftWebAppCallsWebApi(Configuration, "AzureAdB2C")
+ .AddInMemoryTokenCaches();
services.AddDownstreamWebApiService(Configuration);
#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/WebApi-CSharp/Startup.cs b/src/ProjectTemplates/Web.ProjectTemplates/content/WebApi-CSharp/Startup.cs
index a67f026b9881..71568e5da054 100644
--- a/src/ProjectTemplates/Web.ProjectTemplates/content/WebApi-CSharp/Startup.cs
+++ b/src/ProjectTemplates/Web.ProjectTemplates/content/WebApi-CSharp/Startup.cs
@@ -41,15 +41,15 @@ public Startup(IConfiguration configuration)
public void ConfigureServices(IServiceCollection services)
{
#if (OrganizationalAuth)
+#if (GenerateApiOrGraph)
// Adds Microsoft Identity platform (AAD v2.0) support to protect this Api
services.AddMicrosoftWebApiAuthentication(Configuration, "AzureAd")
-#if (GenerateApiOrGraph)
- .AddMicrosoftWebApiCallsWebApi(Configuration,
- "AzureAd")
- .AddInMemoryTokenCaches();
+ .AddMicrosoftWebApiCallsWebApi(Configuration, "AzureAd")
+ .AddInMemoryTokenCaches();
#else
- ;
+ // Adds Microsoft Identity platform (AAD v2.0) support to protect this Api
+ services.AddMicrosoftWebApiAuthentication(Configuration, "AzureAd");
#endif
#if (GenerateApi)
services.AddDownstreamWebApiService(Configuration);
@@ -59,15 +59,14 @@ public void ConfigureServices(IServiceCollection services)
Configuration.GetValue("CalledApi:CalledApiUrl"));
#endif
#elif (IndividualB2CAuth)
- services.AddMicrosoftWebApiAuthentication(Configuration, "AzureAdB2C")
#if (GenerateApi)
- .AddMicrosoftWebApiCallsWebApi(Configuration,
- "AzureAdB2C")
- .AddInMemoryTokenCaches();
+ services.AddMicrosoftWebApiAuthentication(Configuration, "AzureAdB2C")
+ .AddMicrosoftWebApiCallsWebApi(Configuration, "AzureAdB2C")
+ .AddInMemoryTokenCaches();
services.AddDownstreamWebApiService(Configuration);
#else
- ;
+ services.AddMicrosoftWebApiAuthentication(Configuration, "AzureAdB2C");
#endif
#endif
diff --git a/src/ProjectTemplates/test/Infrastructure/TemplateTests.props.in b/src/ProjectTemplates/test/Infrastructure/TemplateTests.props.in
index c3e1a9634d09..be883d274c29 100644
--- a/src/ProjectTemplates/test/Infrastructure/TemplateTests.props.in
+++ b/src/ProjectTemplates/test/Infrastructure/TemplateTests.props.in
@@ -2,7 +2,6 @@
true
${RestoreAdditionalProjectSources}
-
$(MSBuildThisFileDirectory)runtimeconfig.norollforward.json
diff --git a/src/ProjectTemplates/test/template-baselines.json b/src/ProjectTemplates/test/template-baselines.json
index bf2f88c6a7cc..e77037842c72 100644
--- a/src/ProjectTemplates/test/template-baselines.json
+++ b/src/ProjectTemplates/test/template-baselines.json
@@ -921,7 +921,9 @@
"Properties/launchSettings.json",
"Shared/LoginDisplay.razor",
"Shared/MainLayout.razor",
+ "Shared/MainLayout.razor.css",
"Shared/NavMenu.razor",
+ "Shared/NavMenu.razor.css",
"Shared/SurveyPrompt.razor",
"wwwroot/favicon.ico",
"wwwroot/css/site.css",
@@ -958,7 +960,9 @@
"Properties/launchSettings.json",
"Shared/LoginDisplay.razor",
"Shared/MainLayout.razor",
+ "Shared/MainLayout.razor.css",
"Shared/NavMenu.razor",
+ "Shared/NavMenu.razor.css",
"Shared/SurveyPrompt.razor",
"wwwroot/favicon.ico",
"wwwroot/css/site.css",
@@ -995,7 +999,9 @@
"Properties/launchSettings.json",
"Shared/LoginDisplay.razor",
"Shared/MainLayout.razor",
+ "Shared/MainLayout.razor.css",
"Shared/NavMenu.razor",
+ "Shared/NavMenu.razor.css",
"Shared/SurveyPrompt.razor",
"wwwroot/favicon.ico",
"wwwroot/css/site.css",
@@ -1031,7 +1037,9 @@
"Pages/_Host.cshtml",
"Properties/launchSettings.json",
"Shared/MainLayout.razor",
+ "Shared/MainLayout.razor.css",
"Shared/NavMenu.razor",
+ "Shared/NavMenu.razor.css",
"Shared/SurveyPrompt.razor",
"wwwroot/favicon.ico",
"wwwroot/css/site.css",
@@ -1068,7 +1076,9 @@
"Properties/launchSettings.json",
"Shared/LoginDisplay.razor",
"Shared/MainLayout.razor",
+ "Shared/MainLayout.razor.css",
"Shared/NavMenu.razor",
+ "Shared/NavMenu.razor.css",
"Shared/SurveyPrompt.razor",
"wwwroot/favicon.ico",
"wwwroot/css/site.css",
@@ -1105,7 +1115,9 @@
"Properties/launchSettings.json",
"Shared/LoginDisplay.razor",
"Shared/MainLayout.razor",
+ "Shared/MainLayout.razor.css",
"Shared/NavMenu.razor",
+ "Shared/NavMenu.razor.css",
"Shared/SurveyPrompt.razor",
"wwwroot/favicon.ico",
"wwwroot/css/site.css",
diff --git a/src/Razor/Microsoft.NET.Sdk.Razor/src/build/netstandard2.0/Microsoft.NET.Sdk.Razor.ScopedCss.targets b/src/Razor/Microsoft.NET.Sdk.Razor/src/build/netstandard2.0/Microsoft.NET.Sdk.Razor.ScopedCss.targets
index 5a20c17ad101..379958af8072 100644
--- a/src/Razor/Microsoft.NET.Sdk.Razor/src/build/netstandard2.0/Microsoft.NET.Sdk.Razor.ScopedCss.targets
+++ b/src/Razor/Microsoft.NET.Sdk.Razor/src/build/netstandard2.0/Microsoft.NET.Sdk.Razor.ScopedCss.targets
@@ -58,6 +58,7 @@ Integration with static web assets:
<_ScopedCssExtension>.rz.scp.css
$(ResolveStaticWebAssetsInputsDependsOn);_CollectAllScopedCssAssets;AddScopedCssBundle
$(ResolveCurrentProjectStaticWebAssetsInputsDependsOn);_AddGeneratedScopedCssFiles
+ $(GetCurrentProjectStaticWebAssetsDependsOn);IncludeScopedCssBundle;
@@ -68,7 +69,7 @@ Integration with static web assets:
NOTE: This target is called as part of an incremental build scenario in VS. Do not perform any work
outside of calculating RazorComponent items in this target.
-->
-
+
@@ -207,6 +208,33 @@ Integration with static web assets:
+
+
+ <_ScopedCssOutputPath>$(_ScopedCssIntermediatePath)_framework\scoped.styles.css
+ <_ScopedCssOutputFullPath>$([System.IO.Path]::Combine('$(MSBuildProjectFileDirectory)', '$(_ScopedCssIntermediatePath)_framework\scoped.styles.css'))
+
+
+
+
+
+
+
+ $(PackageId)
+ $(_ScopedCssIntermediatePath)
+ $(StaticWebAssetBasePath)
+ _framework/scoped.styles.css
+
+ <_ExternalStaticWebAsset Include="$(_ScopedCssOutputPath)" Condition="@(_AllScopedCss) != ''">
+ generated
+ $(PackageId)
+ $(_ScopedCssIntermediatePath)
+ $(StaticWebAssetBasePath)
+ _framework/scoped.styles.css
+
+
+
+