Merge in 'release/5.0-preview8' changes

This commit is contained in:
dotnet-bot 2020-07-29 17:33:14 +00:00
commit c60c278512
32 changed files with 939 additions and 146 deletions

View File

@ -245,8 +245,8 @@
<IdentityServer4StoragePackageVersion>3.0.0</IdentityServer4StoragePackageVersion>
<IdentityServer4EntityFrameworkStoragePackageVersion>3.0.0</IdentityServer4EntityFrameworkStoragePackageVersion>
<MessagePackPackageVersion>2.1.90</MessagePackPackageVersion>
<MicrosoftIdentityWebPackageVersion>0.2.0-preview</MicrosoftIdentityWebPackageVersion>
<MicrosoftIdentityWebUIPackageVersion>0.2.0-preview</MicrosoftIdentityWebUIPackageVersion>
<MicrosoftIdentityWebPackageVersion>0.2.1-preview</MicrosoftIdentityWebPackageVersion>
<MicrosoftIdentityWebUIPackageVersion>0.2.1-preview</MicrosoftIdentityWebUIPackageVersion>
<MicrosoftGraphPackageVersion>3.8.0</MicrosoftGraphPackageVersion>
<MessagePackAnalyzerPackageVersion>$(MessagePackPackageVersion)</MessagePackAnalyzerPackageVersion>
<MoqPackageVersion>4.10.0</MoqPackageVersion>

View File

@ -188,5 +188,30 @@ namespace Templates.Test
Browser.Exists(By.CssSelector("table>tbody>tr"));
Browser.Equal(5, () => Browser.FindElements(By.CssSelector("p+table>tbody>tr")).Count);
}
[Theory]
[QuarantinedTest]
[InlineData("IndividualB2C", null)]
[InlineData("IndividualB2C", new string[] { "--called-api-url \"https://graph.microsoft.com\"", "--called-api-scopes user.readwrite" })]
[InlineData("SingleOrg", null)]
[InlineData("SingleOrg", new string[] { "--called-api-url \"https://graph.microsoft.com\"", "--called-api-scopes user.readwrite" })]
[InlineData("SingleOrg", new string[] { "--calls-graph" })]
public async Task BlazorServerTemplat_IdentityWeb_BuildAndPublish(string auth, string[] args)
{
Project = await ProjectFactory.GetOrCreateProject("blazorserveridweb" + Guid.NewGuid().ToString().Substring(0, 10).ToLower(), Output);
var createResult = await Project.RunDotNetNewAsync("blazorserver", auth: auth, args: args);
Assert.True(0 == createResult.ExitCode, ErrorMessages.GetFailedProcessMessage("create/restore", Project, createResult));
var publishResult = await Project.RunDotNetPublishAsync();
Assert.True(0 == publishResult.ExitCode, ErrorMessages.GetFailedProcessMessage("publish", Project, publishResult));
// Run dotnet build after publish. The reason is that one uses Config = Debug and the other uses Config = Release
// The output from publish will go into bin/Release/netcoreappX.Y/publish and won't be affected by calling build
// later, while the opposite is not true.
var buildResult = await Project.RunDotNetBuildAsync();
Assert.True(0 == buildResult.ExitCode, ErrorMessages.GetFailedProcessMessage("build", Project, buildResult));
}
}
}

View File

@ -418,6 +418,27 @@ namespace Templates.Test
"--default-scope", "full",
"--app-id-uri", "ApiUri",
"--api-client-id", "1234123413241324"),
new TemplateInstance(
"blazorwasmhostedaadgraph", "-ho",
"-au", "SingleOrg",
"--calls-graph",
"--domain", "my-domain",
"--tenant-id", "tenantId",
"--client-id", "clientId",
"--default-scope", "full",
"--app-id-uri", "ApiUri",
"--api-client-id", "1234123413241324"),
new TemplateInstance(
"blazorwasmhostedaadapi", "-ho",
"-au", "SingleOrg",
"--called-api-url", "\"https://graph.microsoft.com\"",
"--called-api-scopes", "user.readwrite",
"--domain", "my-domain",
"--tenant-id", "tenantId",
"--client-id", "clientId",
"--default-scope", "full",
"--app-id-uri", "ApiUri",
"--api-client-id", "1234123413241324"),
new TemplateInstance(
"blazorwasmstandaloneaadb2c",
"-au", "IndividualB2C",

View File

@ -17,14 +17,15 @@
<!--#endif -->
<!--#if (IndividualAuth || OrganizationalAuth) -->
<ItemGroup>
<PackageReference Include="Microsoft.AspNetCore.Authentication.AzureAD.UI" Version="${MicrosoftAspNetCoreAuthenticationAzureADUIPackageVersion}" Condition="'$(OrganizationalAuth)' == 'True'" />
<PackageReference Include="Microsoft.AspNetCore.Authentication.AzureADB2C.UI" Version="${MicrosoftAspNetCoreAuthenticationAzureADB2CUIPackageVersion}" Condition="'$(IndividualB2CAuth)' == 'True'" />
<PackageReference Include="Microsoft.AspNetCore.Diagnostics.EntityFrameworkCore" Version="${MicrosoftAspNetCoreDiagnosticsEntityFrameworkCorePackageVersion}" Condition=" '$(IndividualLocalAuth)' == 'True' " />
<PackageReference Include="Microsoft.AspNetCore.Identity.EntityFrameworkCore" Version="${MicrosoftAspNetCoreIdentityEntityFrameworkCorePackageVersion}" Condition=" '$(IndividualLocalAuth)' == 'True' " />
<PackageReference Include="Microsoft.AspNetCore.Identity.UI" Version="${MicrosoftAspNetCoreIdentityUIPackageVersion}" Condition=" '$(IndividualLocalAuth)' == 'True' " />
<PackageReference Include="Microsoft.EntityFrameworkCore.SqlServer" Version="${MicrosoftEntityFrameworkCoreSqlServerPackageVersion}" Condition=" '$(IndividualLocalAuth)' == 'True' AND '$(UseLocalDB)' == 'True'" />
<PackageReference Include="Microsoft.EntityFrameworkCore.Sqlite" Version="${MicrosoftEntityFrameworkCoreSqlitePackageVersion}" Condition=" '$(IndividualLocalAuth)' == 'True' AND '$(UseLocalDB)' != 'True'" />
<PackageReference Include="Microsoft.EntityFrameworkCore.Tools" Version="${MicrosoftEntityFrameworkCoreToolsPackageVersion}" Condition=" '$(IndividualLocalAuth)' == 'True' " />
<PackageReference Include="Microsoft.Identity.Web" Version="${MicrosoftIdentityWebPackageVersion}" Condition=" '$(IndividualB2CAuth)' == 'True' OR '$(OrganizationalAuth)' == 'True'" />
<PackageReference Include="Microsoft.Identity.Web.UI" Version="${MicrosoftIdentityWebUIPackageVersion}" Condition=" '$(IndividualB2CAuth)' == 'True' OR '$(OrganizationalAuth)' == 'True'" />
<PackageReference Include="Microsoft.Graph" Version="${MicrosoftGraphPackageVersion}" Condition=" '$(GenerateGraph)' == 'True' "/>
</ItemGroup>
<!--#endif -->

View File

@ -38,8 +38,9 @@
<!--#endif -->
<!--#if (OrganizationalAuth || IndividualB2CAuth) -->
<ItemGroup>
<PackageReference Include="Microsoft.AspNetCore.Authentication.AzureAD.UI" Version="${MicrosoftAspNetCoreAuthenticationAzureADUIPackageVersion}" Condition="'$(OrganizationalAuth)' == 'True'" />
<PackageReference Include="Microsoft.AspNetCore.Authentication.AzureADB2C.UI" Version="${MicrosoftAspNetCoreAuthenticationAzureADB2CUIPackageVersion}" Condition="'$(IndividualB2CAuth)' == 'True'" />
<PackageReference Include="Microsoft.Identity.Web" Version="${MicrosoftIdentityWebPackageVersion}" />
<PackageReference Include="Microsoft.Identity.Web.UI" Version="${MicrosoftIdentityWebUIPackageVersion}" />
<PackageReference Include="Microsoft.Graph" Version="${MicrosoftGraphPackageVersion}" Condition=" '$(GenerateGraph)' == 'True' "/>
</ItemGroup>
<!--#endif -->

View File

@ -67,6 +67,18 @@
"NoHttps": {
"longName": "no-https",
"shortName": ""
},
"CalledApiUrl": {
"longName": "called-api-url",
"shortName": ""
},
"CalledApiScopes": {
"longName": "called-api-scopes",
"shortName": ""
},
"CallsMicrosoftGraph": {
"longName": "calls-graph",
"shortName": ""
}
},
"usageExamples": [

View File

@ -127,6 +127,52 @@
"Shared/LoginDisplay.IndividualB2CAuth.razor",
"Shared/LoginDisplay.OrganizationalAuth.razor"
]
},
{
"condition": "(!GenerateApi)",
"exclude": [
"Services/DownstreamWebApi.cs",
"Pages/CallWebApi.razor"
]
},
{
"condition": "(!GenerateGraph)",
"exclude": [
"Services/MicrosoftGraphServiceExtensions.cs",
"Services/TokenAcquisitionCredentialProvider.cs",
"Shared/NavMenu.CallsMicrosoftGraph.razor",
"Pages/ShowProfile.razor"
]
},
{
"condition": "(!GenerateApiOrGraph)",
"rename": {
"Shared/NavMenu.NoGraphOrApi.razor": "Shared/NavMenu.razor"
},
"exclude": [
"Shared/NavMenu.CallsMicrosoftGraph.razor",
"Shared/NavMenu.CallsWebApi.razor"
]
},
{
"condition": "(GenerateGraph)",
"rename": {
"Shared/NavMenu.CallsMicrosoftGraph.razor": "Shared/NavMenu.razor"
},
"exclude": [
"Shared/NavMenu.NoGraphOrApi.razor",
"Shared/NavMenu.CallsWebApi.razor"
]
},
{
"condition": "(GenerateApi)",
"rename": {
"Shared/NavMenu.CallsWebApi.razor": "Shared/NavMenu.razor"
},
"exclude": [
"Shared/NavMenu.NoGraphOrApi.razor",
"Shared/NavMenu.CallsMicrosoftGraph.razor"
]
}
]
}
@ -174,21 +220,28 @@
"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)."
},
"SignedOutCallbackPath": {
"type": "parameter",
"datatype": "string",
"defaultValue": "/signout/B2C_1_susi",
"replaces": "/signout/MySignUpSignInPolicyId",
"description": "The global signout callback (use with IndividualB2C auth)."
},
"ResetPasswordPolicyId": {
"type": "parameter",
"datatype": "string",
"defaultValue": "",
"defaultValue": "b2c_1_reset",
"replaces": "MyResetPasswordPolicyId",
"description": "The reset password policy ID for this project (use with IndividualB2C auth)."
},
"EditProfilePolicyId": {
"type": "parameter",
"datatype": "string",
"defaultValue": "",
"defaultValue": "b2c_1_edit_profile",
"replaces": "MyEditProfilePolicyId",
"description": "The edit profile policy ID for this project (use with IndividualB2C auth)."
},
@ -352,6 +405,37 @@
"format": "yyyy"
}
},
"CalledApiUrl": {
"type": "parameter",
"datatype": "string",
"replaces": "[WebApiUrl]",
"defaultValue" : "https://graph.microsoft.com/beta",
"description": "URL of the API to call from the web app. This option only applies if --auth SingleOrg, --auth MultiOrg or --auth IndividualB2C is specified."
},
"CallsMicrosoftGraph": {
"type": "parameter",
"datatype": "bool",
"defaultValue": "false",
"description": "Specifies if the web app calls Microsoft Graph. This option only applies if --auth SingleOrg or --auth MultiOrg is specified."
},
"CalledApiScopes": {
"type": "parameter",
"datatype": "string",
"replaces" : "user.read",
"description": "Scopes to request to call the API from the web app. This option only applies if --auth SingleOrg, --auth MultiOrg or --auth IndividualB2C is specified."
},
"GenerateApi": {
"type": "computed",
"value": "((IndividualB2CAuth || OrganizationalAuth) && (CalledApiUrl != \"https://graph.microsoft.com/beta\" || CalledApiScopes != \"user.read\"))"
},
"GenerateGraph": {
"type": "computed",
"value": "(OrganizationalAuth && CallsMicrosoftGraph)"
},
"GenerateApiOrGraph": {
"type": "computed",
"value": "(GenerateApi || GenerateGraph)"
},
"skipRestore": {
"type": "parameter",
"datatype": "bool",

View File

@ -7,10 +7,10 @@
@if (SignInManager.IsSignedIn(User))
{
<li class="nav-item">
<a class="nav-link text-dark" asp-area="Identity" asp-page="/Account/Manage/Index" title="Manage">Hello @User.Identity.Name!</a>
<a class="nav-link text-dark" asp-area="MicrosoftIdentity" asp-page="/Account/Manage/Index" title="Manage">Hello @User.Identity.Name!</a>
</li>
<li class="nav-item">
<form class="form-inline" asp-area="Identity" asp-page="/Account/Logout" asp-route-returnUrl="/" method="post">
<form class="form-inline" asp-area="MicrosoftIdentity" asp-page="/Account/Logout" asp-route-returnUrl="/" method="post">
<button type="submit" class="nav-link btn btn-link text-dark">Logout</button>
</form>
</li>
@ -18,10 +18,10 @@
else
{
<li class="nav-item">
<a class="nav-link text-dark" asp-area="Identity" asp-page="/Account/Register">Register</a>
<a class="nav-link text-dark" asp-area="MicrosoftIdentity" asp-page="/Account/Register">Register</a>
</li>
<li class="nav-item">
<a class="nav-link text-dark" asp-area="Identity" asp-page="/Account/Login">Login</a>
<a class="nav-link text-dark" asp-area="MicrosoftIdentity" asp-page="/Account/Login">Login</a>
</li>
}
</ul>

View File

@ -0,0 +1,37 @@
@page "/callwebapi"
@using BlazorServerWeb_CSharp
@using Microsoft.Identity.Web
@inject IDownstreamWebApi downstreamAPI
@inject MicrosoftIdentityConsentAndConditionalAccessHandler ConsentHandler
<h1>Call an API</h1>
<p>This component demonstrates fetching data from a Web API.</p>
@if (apiResult == null)
{
<p><em>Loading...</em></p>
}
else
{
<h2>API Result</h2>
@apiResult
}
@code {
private string apiResult;
protected override async Task OnInitializedAsync()
{
try
{
apiResult = await downstreamAPI.CallWebApiAsync("me");
}
catch (Exception ex)
{
ConsentHandler.HandleException(ex);
}
}
}

View File

@ -0,0 +1,84 @@
@page "/showprofile"
@using Microsoft.Identity.Web
@using Microsoft.Graph
@inject Microsoft.Graph.GraphServiceClient GraphServiceClient
@inject MicrosoftIdentityConsentAndConditionalAccessHandler ConsentHandler
<h1>Me</h1>
<p>This component demonstrates fetching data from a service.</p>
@if (user == null)
{
<p><em>Loading...</em></p>
}
else
{
<table class="table table-striped table-condensed" style="font-family: monospace">
<tr>
<th>Property</th>
<th>Value</th>
</tr>
<tr>
<td>Name</td>
<td>@user.DisplayName</td>
</tr>
<tr>
<td>Photo</td>
<td>
@{
if (photo != null)
{
<img style="margin: 5px 0; width: 150px" src="data:image/jpeg;base64, @photo" />
}
else
{
<h3>NO PHOTO</h3>
<p>Check user profile in Azure Active Directory to add a photo.</p>
}
}
</td>
</tr>
</table>
}
@code {
User user;
string photo;
protected override async Task OnInitializedAsync()
{
try
{
user = await GraphServiceClient.Me.Request().GetAsync();
photo = await GetPhoto();
}
catch (Exception ex)
{
ConsentHandler.HandleException(ex);
}
}
protected async Task<string> GetPhoto()
{
string photo;
try
{
using (var photoStream = await GraphServiceClient.Me.Photo.Content.Request().GetAsync())
{
byte[] photoByte = ((System.IO.MemoryStream)photoStream).ToArray();
photo = Convert.ToBase64String(photoByte);
this.StateHasChanged();
}
}
catch (Exception)
{
photo = null;
}
return photo;
}
}

View File

@ -1,9 +1,7 @@
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Threading.Tasks;
using Microsoft.AspNetCore;
using Microsoft.AspNetCore.Hosting;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.Hosting;

View File

@ -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 BlazorServerWeb_CSharp
{
public interface IDownstreamWebApi
{
Task<string> 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<IDownstreamWebApi, DownstreamWebApi>();
}
}
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;
}
/// <summary>
/// Calls the Web API with the required scopes
/// </summary>
/// <param name="requireScopes">[Optional] Scopes required to call the Web API. If
/// not specified, uses scopes from the configuration</param>
/// <param name="relativeEndpoint">Endpoint relative to the CalledApiUrl configuration</param>
/// <returns>A JSON string representing the result of calling the Web API</returns>
public async Task<string> 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;
}
}
}

View File

@ -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 BlazorServerWeb_CSharp
{
public static class MicrosoftGraphServiceExtensions
{
/// <summary>
/// Adds the Microsoft Graph client as a singleton.
/// </summary>
/// <param name="services">Service collection.</param>
/// <param name="initialScopes">Initial scopes.</param>
/// <param name="graphBaseUrl">Base URL for Microsoft graph. This can be
/// changed for instance for applications running in national clouds</param>
public static IServiceCollection AddMicrosoftGraph(this IServiceCollection services,
IEnumerable<string> initialScopes,
string graphBaseUrl = "https://graph.microsoft.com/v1.0")
{
services.AddTokenAcquisition(true);
services.AddSingleton<GraphServiceClient, GraphServiceClient>(serviceProvider =>
{
var tokenAquisitionService = serviceProvider.GetService<ITokenAcquisition>();
GraphServiceClient client = string.IsNullOrWhiteSpace(graphBaseUrl) ?
new GraphServiceClient(new TokenAcquisitionCredentialProvider(tokenAquisitionService, initialScopes)) :
new GraphServiceClient(graphBaseUrl, new TokenAcquisitionCredentialProvider(tokenAquisitionService, initialScopes));
return client;
});
return services;
}
}
}

View File

@ -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 BlazorServerWeb_CSharp
{
internal class TokenAcquisitionCredentialProvider : IAuthenticationProvider
{
public TokenAcquisitionCredentialProvider(ITokenAcquisition tokenAcquisition, IEnumerable<string> initialScopes)
{
_tokenAcquisition = tokenAcquisition;
_initialScopes = initialScopes;
}
ITokenAcquisition _tokenAcquisition;
IEnumerable<string> _initialScopes;
public async Task AuthenticateRequestAsync(HttpRequestMessage request)
{
request.Headers.Add("Authorization",
$"Bearer {await _tokenAcquisition.GetAccessTokenForUserAsync(_initialScopes)}");
}
}
}

View File

@ -1,21 +1,21 @@
@using Microsoft.AspNetCore.Authentication.AzureADB2C.UI
@using Microsoft.Identity.Web
@using Microsoft.Extensions.Options
@inject IOptionsMonitor<AzureADB2COptions> AzureADB2COptions
@inject IOptionsMonitor<MicrosoftIdentityOptions> microsoftIdentityOptions
<AuthorizeView>
<Authorized>
@if (canEditProfile)
{
<a href="AzureADB2C/Account/EditProfile">Hello, @context.User.Identity.Name!</a>
<a href="MicrosoftIdentity/Account/EditProfile">Hello, @context.User.Identity.Name!</a>
}
else
{
<text>Hello, @context.User.Identity.Name!</text>
}
<a href="AzureADB2C/Account/SignOut">Log out</a>
<a href="MicrosoftIdentity/Account/SignOut">Log out</a>
</Authorized>
<NotAuthorized>
<a href="AzureADB2C/Account/SignIn">Log in</a>
<a href="MicrosoftIdentity/Account/SignIn">Log in</a>
</NotAuthorized>
</AuthorizeView>
@ -24,7 +24,7 @@
protected override void OnInitialized()
{
var options = AzureADB2COptions.Get(AzureADB2CDefaults.AuthenticationScheme);
var options = microsoftIdentityOptions.CurrentValue;
canEditProfile = !string.IsNullOrEmpty(options.EditProfilePolicyId);
}
}

View File

@ -1,9 +1,9 @@
<AuthorizeView>
<Authorized>
Hello, @context.User.Identity.Name!
<a href="AzureAD/Account/SignOut">Log out</a>
<a href="MicrosoftIdentity/Account/SignOut">Log out</a>
</Authorized>
<NotAuthorized>
<a href="AzureAD/Account/SignIn">Log in</a>
<a href="MicrosoftIdentity/Account/SignIn">Log in</a>
</NotAuthorized>
</AuthorizeView>

View File

@ -0,0 +1,42 @@
<div class="top-row pl-4 navbar navbar-dark">
<a class="navbar-brand" href="">BlazorServerWeb-CSharp</a>
<button class="navbar-toggler" @onclick="ToggleNavMenu">
<span class="navbar-toggler-icon"></span>
</button>
</div>
<div class="@NavMenuCssClass" @onclick="ToggleNavMenu">
<ul class="nav flex-column">
<li class="nav-item px-3">
<NavLink class="nav-link" href="" Match="NavLinkMatch.All">
<span class="oi oi-home" aria-hidden="true"></span> Home
</NavLink>
</li>
<li class="nav-item px-3">
<NavLink class="nav-link" href="counter">
<span class="oi oi-plus" aria-hidden="true"></span> Counter
</NavLink>
</li>
<li class="nav-item px-3">
<NavLink class="nav-link" href="fetchdata">
<span class="oi oi-list-rich" aria-hidden="true"></span> Fetch data
</NavLink>
</li>
<li class="nav-item px-3">
<NavLink class="nav-link" href="showprofile">
<span class="oi oi-list-rich" aria-hidden="true"></span> Show profile
</NavLink>
</li>
</ul>
</div>
@code {
private bool collapseNavMenu = true;
private string NavMenuCssClass => collapseNavMenu ? "collapse" : null;
private void ToggleNavMenu()
{
collapseNavMenu = !collapseNavMenu;
}
}

View File

@ -0,0 +1,42 @@
<div class="top-row pl-4 navbar navbar-dark">
<a class="navbar-brand" href="">BlazorServerWeb-CSharp</a>
<button class="navbar-toggler" @onclick="ToggleNavMenu">
<span class="navbar-toggler-icon"></span>
</button>
</div>
<div class="@NavMenuCssClass" @onclick="ToggleNavMenu">
<ul class="nav flex-column">
<li class="nav-item px-3">
<NavLink class="nav-link" href="" Match="NavLinkMatch.All">
<span class="oi oi-home" aria-hidden="true"></span> Home
</NavLink>
</li>
<li class="nav-item px-3">
<NavLink class="nav-link" href="counter">
<span class="oi oi-plus" aria-hidden="true"></span> Counter
</NavLink>
</li>
<li class="nav-item px-3">
<NavLink class="nav-link" href="fetchdata">
<span class="oi oi-list-rich" aria-hidden="true"></span> Fetch data
</NavLink>
</li>
<li class="nav-item px-3">
<NavLink class="nav-link" href="callwebapi">
<span class="oi oi-list-rich" aria-hidden="true"></span> Call Web API
</NavLink>
</li>
</ul>
</div>
@code {
private bool collapseNavMenu = true;
private string NavMenuCssClass => collapseNavMenu ? "collapse" : null;
private void ToggleNavMenu()
{
collapseNavMenu = !collapseNavMenu;
}
}

View File

@ -4,17 +4,16 @@ using System.Linq;
using System.Threading.Tasks;
#if (OrganizationalAuth || IndividualB2CAuth)
using Microsoft.AspNetCore.Authentication;
using Microsoft.Identity.Web;
using Microsoft.Identity.Web.UI;
using Microsoft.Identity.Web.TokenCacheProviders.InMemory;
#endif
#if (OrganizationalAuth)
using Microsoft.AspNetCore.Authentication.AzureAD.UI;
#if (MultiOrgAuth)
using Microsoft.AspNetCore.Authentication.OpenIdConnect;
#endif
using Microsoft.AspNetCore.Authorization;
#endif
#if (IndividualB2CAuth)
using Microsoft.AspNetCore.Authentication.AzureADB2C.UI;
#endif
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Components;
#if (IndividualLocalAuth)
@ -42,6 +41,9 @@ using Microsoft.IdentityModel.Tokens;
using BlazorServerWeb_CSharp.Areas.Identity;
#endif
using BlazorServerWeb_CSharp.Data;
#if (GenerateGraph)
using Microsoft.Graph;
#endif
namespace BlazorServerWeb_CSharp
{
@ -70,59 +72,39 @@ namespace BlazorServerWeb_CSharp
services.AddDefaultIdentity<IdentityUser>(options => options.SignIn.RequireConfirmedAccount = true)
.AddEntityFrameworkStores<ApplicationDbContext>();
#elif (OrganizationalAuth)
#pragma warning disable CS0618 // Type or member is obsolete
services.AddAuthentication(AzureADDefaults.AuthenticationScheme)
.AddAzureAD(options => Configuration.Bind("AzureAd", options));
#pragma warning restore CS0618 // Type or member is obsolete
#if (MultiOrgAuth)
#pragma warning disable CS0618 // Type or member is obsolete
services.Configure<OpenIdConnectOptions>(AzureADDefaults.OpenIdScheme, options =>
#pragma warning restore CS0618 // Type or member is obsolete
{
options.TokenValidationParameters = new TokenValidationParameters
{
// Instead of using the default validation (validating against a single issuer value, as we do in
// line of business apps), we inject our own multitenant validation logic
ValidateIssuer = false,
// If the app is meant to be accessed by entire organizations, add your issuer validation logic here.
//IssuerValidator = (issuer, securityToken, validationParameters) => {
// if (myIssuerValidationLogic(issuer)) return issuer;
//}
};
options.Events = new OpenIdConnectEvents
{
OnTicketReceived = context =>
{
// If your authentication logic is based on users then add your logic here
return Task.CompletedTask;
},
OnAuthenticationFailed = context =>
{
context.Response.Redirect("/Error");
context.HandleResponse(); // Suppress the exception
return Task.CompletedTask;
},
// If your application needs to authenticate single users, add your user validation below.
//OnTokenValidated = context =>
//{
// return myUserValidationLogic(context.Ticket.Principal);
//}
};
});
#if (GenerateApiOrGraph)
string[] scopes = Configuration.GetValue<string>("CalledApi:CalledApiScopes")?.Split(' ');
#endif
#if (GenerateApiOrGraph)
services.AddMicrosoftWebAppAuthentication(Configuration, "AzureAd")
.AddMicrosoftWebAppCallsWebApi(Configuration, scopes, "AzureAd")
.AddInMemoryTokenCaches();
#else
services.AddMicrosoftWebAppAuthentication(Configuration, "AzureAd");
#endif
#if (GenerateApi)
services.AddDownstreamWebApiService(Configuration);
#endif
#if (GenerateGraph)
services.AddMicrosoftGraph(scopes, Configuration.GetValue<string>("CalledApi:CalledApiUrl"));
#endif
#elif (IndividualB2CAuth)
#pragma warning disable CS0618 // Type or member is obsolete
services.AddAuthentication(AzureADB2CDefaults.AuthenticationScheme)
.AddAzureADB2C(options => Configuration.Bind("AzureAdB2C", options));
#pragma warning restore CS0618 // Type or member is obsolete
#if (GenerateApi)
string[] scopes = Configuration.GetValue<string>("CalledApi:CalledApiScopes")?.Split(' ');
#endif
#if (OrganizationalAuth)
services.AddControllersWithViews();
#if (GenerateApi)
services.AddMicrosoftWebAppAuthentication(Configuration, "AzureAdB2C")
.AddMicrosoftWebAppCallsWebApi(Configuration, scopes, "AzureAdB2C")
.AddInMemoryTokenCaches();
services.AddDownstreamWebApiService(Configuration);
#else
services.AddMicrosoftWebAppAuthentication(Configuration, "AzureAdB2C");
#endif
#endif
#if (OrganizationalAuth || IndividualB2CAuth)
services.AddControllersWithViews()
.AddMicrosoftIdentityUI();
services.AddAuthorization(options =>
{
@ -132,7 +114,12 @@ namespace BlazorServerWeb_CSharp
#endif
services.AddRazorPages();
#if (OrganizationalAuth || IndividualB2CAuth)
services.AddServerSideBlazor()
.AddMicrosoftIdentityConsentHandler();
#else
services.AddServerSideBlazor();
#endif
#if (IndividualLocalAuth)
services.AddScoped<AuthenticationStateProvider, RevalidatingIdentityAuthenticationStateProvider<IdentityUser>>();
#endif

View File

@ -5,6 +5,12 @@
// "ClientId": "11111111-1111-1111-11111111111111111",
// "CallbackPath": "/signin-oidc",
// "Domain": "qualified.domain.name",
// "SignedOutCallbackPath": "/signout/MySignUpSignInPolicyId",
//#if (GenerateApi)
// "ClientSecret": "secret-from-app-registration",
// "ClientCertificates" : [
// ],
//#endif
// "SignUpSignInPolicyId": "MySignUpSignInPolicyId",
// "ResetPasswordPolicyId": "MyResetPasswordPolicyId",
// "EditProfilePolicyId": "MyEditProfilePolicyId"
@ -19,18 +25,36 @@
// "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
////#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 <App ID URI>/.default, where <App ID URI> 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
////#if (IndividualLocalAuth)
// "ConnectionStrings": {
////#if (UseLocalDB)
//#if (UseLocalDB)
// "DefaultConnection": "Server=(localdb)\\mssqllocaldb;Database=aspnet-BlazorServerWeb-CSharp-53bc9b9d-9d6a-45d4-8429-2a2761773502;Trusted_Connection=True;MultipleActiveResultSets=true"
////#else
//#else
// "DefaultConnection": "DataSource=app.db;Cache=Shared"
//#endif
// },
//#endif
////#endif
"Logging": {
"LogLevel": {
"Default": "Information",

View File

@ -77,6 +77,18 @@
"NoHttps": {
"longName": "no-https",
"shortName": ""
},
"CalledApiUrl": {
"longName": "called-api-url",
"shortName": ""
},
"CalledApiScopes": {
"longName": "called-api-scopes",
"shortName": ""
},
"CallsMicrosoftGraph": {
"longName": "calls-graph",
"shortName": ""
}
}
}

View File

@ -211,6 +211,19 @@
"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"
]
}
]
}
@ -297,7 +310,7 @@
"datatype": "string",
"defaultValue": "https://login.microsoftonline.com/",
"replaces": "https:////login.microsoftonline.com/",
"description": "The Azure Active Directory instance to connect to (use with SingleOrg)."
"description": "The Azure Active Directory instance to connect to (use with SingleOrg auth)."
},
"ClientId": {
"type": "parameter",
@ -452,6 +465,37 @@
"parameters": {
"format": "yyyy"
}
},
"CalledApiUrl": {
"type": "parameter",
"datatype": "string",
"replaces": "[WebApiUrl]",
"defaultValue" : "https://graph.microsoft.com/v1.0/me",
"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": {
"type": "parameter",
"datatype": "bool",
"defaultValue": "false",
"description": "Specifies if the web app calls Microsoft Graph. This option only applies if --auth SingleOrg or --auth MultiOrg is specified."
},
"CalledApiScopes": {
"type": "parameter",
"datatype": "string",
"replaces" : "user.read",
"description": "Scopes to request to call the API from the web app. This option only applies if --auth SingleOrg, --auth MultiOrg or --auth IndividualB2C without and ASP.NET Core host is specified."
},
"GenerateApi": {
"type": "computed",
"value": "(( (IndividualB2CAuth && !Hosted) || OrganizationalAuth) && (CalledApiUrl != \"https://graph.microsoft.com/v1.0/me\" || CalledApiScopes != \"user.read\"))"
},
"GenerateGraph": {
"type": "computed",
"value": "(OrganizationalAuth && CallsMicrosoftGraph)"
},
"GenerateApiOrGraph": {
"type": "computed",
"value": "(GenerateApi || GenerateGraph)"
}
},
"tags": {

View File

@ -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,28 @@ namespace ComponentsWebAssembly_CSharp.Server.Controllers
"Freezing", "Bracing", "Chilly", "Cool", "Mild", "Warm", "Balmy", "Hot", "Sweltering", "Scorching"
};
private readonly ILogger<WeatherForecastController> logger;
private readonly ILogger<WeatherForecastController> _logger;
public WeatherForecastController(ILogger<WeatherForecastController> 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<WeatherForecastController> logger,
IDownstreamWebApi downstreamWebApi)
{
this.logger = logger;
_logger = logger;
_downstreamWebApi = downstreamWebApi;
}
[HttpGet]
public IEnumerable<WeatherForecast> Get()
public async Task<IEnumerable<WeatherForecast>> Get()
{
HttpContext.VerifyUserHasAnyAcceptedScope(scopeRequiredByApi);
string downstreamApiResult = await _downstreamWebApi.CallWebApiAsync();
var rng = new Random();
return Enumerable.Range(1, 5).Select(index => new WeatherForecast
{
@ -42,5 +66,54 @@ namespace ComponentsWebAssembly_CSharp.Server.Controllers
})
.ToArray();
}
#elseif (GenerateGraph)
private readonly GraphServiceClient _graphServiceClient;
public WeatherForecastController(ILogger<WeatherForecastController> logger,
GraphServiceClient graphServiceClient)
{
_logger = logger;
_graphServiceClient = graphServiceClient;
}
[HttpGet]
public async Task<IEnumerable<WeatherForecast>> 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<WeatherForecastController> logger)
{
_logger = logger;
}
[HttpGet]
public IEnumerable<WeatherForecast> Get()
{
#if (OrganizationalAuth || IndividualB2CAuth)
HttpContext.VerifyUserHasAnyAcceptedScope(scopeRequiredByApi);
#endif
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();
}
#endif
}
}

View File

@ -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<string> 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<IDownstreamWebApi, DownstreamWebApi>();
}
}
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;
}
/// <summary>
/// Calls the Web API with the required scopes
/// </summary>
/// <param name="requireScopes">[Optional] Scopes required to call the Web API. If
/// not specified, uses scopes from the configuration</param>
/// <param name="relativeEndpoint">Endpoint relative to the CalledApiUrl configuration</param>
/// <returns>A JSON string representing the result of calling the Web API</returns>
public async Task<string> 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;
}
}
}

View File

@ -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
{
/// <summary>
/// Adds the Microsoft Graph client as a singleton.
/// </summary>
/// <param name="services">Service collection.</param>
/// <param name="initialScopes">Initial scopes.</param>
/// <param name="graphBaseUrl">Base URL for Microsoft graph. This can be
/// changed for instance for applications running in national clouds</param>
public static IServiceCollection AddMicrosoftGraph(this IServiceCollection services,
IEnumerable<string> initialScopes,
string graphBaseUrl = "https://graph.microsoft.com/v1.0")
{
services.AddTokenAcquisition(true);
services.AddSingleton<GraphServiceClient, GraphServiceClient>(serviceProvider =>
{
var tokenAquisitionService = serviceProvider.GetService<ITokenAcquisition>();
GraphServiceClient client = string.IsNullOrWhiteSpace(graphBaseUrl) ?
new GraphServiceClient(new TokenAcquisitionCredentialProvider(tokenAquisitionService, initialScopes)) :
new GraphServiceClient(graphBaseUrl, new TokenAcquisitionCredentialProvider(tokenAquisitionService, initialScopes));
return client;
});
return services;
}
}
}

View File

@ -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<string> initialScopes)
{
_tokenAcquisition = tokenAcquisition;
_initialScopes = initialScopes;
}
ITokenAcquisition _tokenAcquisition;
IEnumerable<string> _initialScopes;
public async Task AuthenticateRequestAsync(HttpRequestMessage request)
{
request.Headers.Add("Authorization",
$"Bearer {await _tokenAcquisition.GetAccessTokenForUserAsync(_initialScopes)}");
}
}
}

View File

@ -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 System.Linq;
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 @@ namespace ComponentsWebAssembly_CSharp.Server
{
#if (IndividualLocalAuth)
services.AddDbContext<ApplicationDbContext>(options =>
#if (UseLocalDB)
#if (UseLocalDB)
options.UseSqlServer(
Configuration.GetConnectionString("DefaultConnection")));
#else
#else
options.UseSqlite(
Configuration.GetConnectionString("DefaultConnection")));
#endif
#endif
services.AddDefaultIdentity<ApplicationUser>(options => options.SignIn.RequireConfirmedAccount = true)
.AddEntityFrameworkStores<ApplicationDbContext>();
@ -65,15 +61,32 @@ namespace ComponentsWebAssembly_CSharp.Server
.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<string>("CalledApi:CalledApiScopes")?.Split(' '),
Configuration.GetValue<string>("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();

View File

@ -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 <App ID URI>/.default, where <App ID URI> 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": {

View File

@ -71,13 +71,12 @@ namespace Company.WebApplication1
services.AddDefaultIdentity<IdentityUser>(options => options.SignIn.RequireConfirmedAccount = true)
.AddEntityFrameworkStores<ApplicationDbContext>();
#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 @@ namespace Company.WebApplication1
Configuration.GetValue<string>("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 @@ namespace Company.WebApplication1
options.FallbackPolicy = options.DefaultPolicy;
});
services.AddRazorPages()
.AddMvcOptions(options => {})
.AddMicrosoftIdentityUI();
.AddMvcOptions(options => {})
.AddMicrosoftIdentityUI();
#elif (IndividualB2CAuth)
services.AddRazorPages()
.AddMicrosoftIdentityUI();
.AddMicrosoftIdentityUI();
#else
services.AddRazorPages();
#endif

View File

@ -71,13 +71,12 @@ namespace Company.WebApplication1
services.AddDefaultIdentity<IdentityUser>(options => options.SignIn.RequireConfirmedAccount = true)
.AddEntityFrameworkStores<ApplicationDbContext>();
#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 @@ namespace Company.WebApplication1
Configuration.GetValue<string>("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 @@ namespace Company.WebApplication1
#endif
#if (OrganizationalAuth || IndividualB2CAuth)
services.AddRazorPages()
.AddMicrosoftIdentityUI();
.AddMicrosoftIdentityUI();
#endif
}

View File

@ -41,15 +41,15 @@ namespace Company.WebApplication1
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 @@ namespace Company.WebApplication1
Configuration.GetValue<string>("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