Skip to content

Commit 6dd62bd

Browse files
authored
[Preview 5] Update Blazor WASM security topics (#17994)
1 parent 3fa8470 commit 6dd62bd

16 files changed

Lines changed: 273 additions & 125 deletions

aspnetcore/includes/blazor-security/authentication-component.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ The page produced by the `Authentication` component (*Pages/Authentication.razor
22

33
The `RemoteAuthenticatorView` component:
44

5-
* Is provided by the `Microsoft.AspNetCore.Components.WebAssembly.Authentication` package.
5+
* Is provided by the [Microsoft.AspNetCore.Components.WebAssembly.Authentication](https://www.nuget.org/packages/Microsoft.AspNetCore.Components.WebAssembly.Authentication/) package.
66
* Manages performing the appropriate actions at each stage of authentication.
77

88
```razor

aspnetcore/includes/blazor-security/fetchdata-component.md

Lines changed: 6 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -17,10 +17,9 @@ If the request failed because the token couldn't be provisioned without user int
1717
@page "/fetchdata"
1818
@using Microsoft.AspNetCore.Authorization
1919
@using Microsoft.AspNetCore.Components.WebAssembly.Authentication
20-
@inject IAccessTokenProvider AuthenticationService
21-
@inject NavigationManager Navigation
22-
@using {APPLICATION NAMESPACE}.Shared
20+
@using {APP NAMESPACE}.Shared
2321
@attribute [Authorize]
22+
@inject HttpClient Http
2423
2524
...
2625
@@ -29,23 +28,14 @@ If the request failed because the token couldn't be provisioned without user int
2928
3029
protected override async Task OnInitializedAsync()
3130
{
32-
var httpClient = new HttpClient();
33-
httpClient.BaseAddress = new Uri(Navigation.BaseUri);
34-
35-
var tokenResult = await AuthenticationService.RequestAccessToken();
36-
37-
if (tokenResult.TryGetToken(out var token))
31+
try
3832
{
39-
httpClient.DefaultRequestHeaders.Add("Authorization",
40-
$"Bearer {token.Value}");
41-
forecasts = await httpClient.GetFromJsonAsync<WeatherForecast[]>(
42-
"WeatherForecast");
33+
forecasts = await Http.GetFromJsonAsync<WeatherForecast[]>("WeatherForecast");
4334
}
44-
else
35+
catch (AccessTokenNotAvailableException exception)
4536
{
46-
Navigation.NavigateTo(tokenResult.RedirectUrl);
37+
exception.Redirect();
4738
}
48-
4939
}
5040
}
5141
```

aspnetcore/includes/blazor-security/imports-hosted.razor

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
@using Microsoft.AspNetCore.Components.Forms
55
@using Microsoft.AspNetCore.Components.Routing
66
@using Microsoft.AspNetCore.Components.Web
7+
@using Microsoft.AspNetCore.Components.WebAssembly.Http
78
@using Microsoft.JSInterop
89
@using {APPLICATION ASSEMBLY}.Client
910
@using {APPLICATION ASSEMBLY}.Client.Shared

aspnetcore/includes/blazor-security/imports-standalone.razor

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
@using Microsoft.AspNetCore.Components.Forms
55
@using Microsoft.AspNetCore.Components.Routing
66
@using Microsoft.AspNetCore.Components.Web
7+
@using Microsoft.AspNetCore.Components.WebAssembly.Http
78
@using Microsoft.JSInterop
89
@using {APPLICATION ASSEMBLY}
910
@using {APPLICATION ASSEMBLY}.Shared

aspnetcore/includes/blazor-security/redirecttologin-component.md

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -9,8 +9,8 @@ The `RedirectToLogin` component (*Shared/RedirectToLogin.razor*):
99
@code {
1010
protected override void OnInitialized()
1111
{
12-
Navigation.NavigateTo(
13-
$"authentication/login?returnUrl={Uri.EscapeDataString(Navigation.Uri)}");
12+
Navigation.NavigateTo($"authentication/login?returnUrl=" +
13+
Uri.EscapeDataString(Navigation.Uri));
1414
}
1515
}
1616
```

aspnetcore/includes/blazor-security/troubleshoot.md

Lines changed: 15 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
## Troubleshoot
22

3-
### Cookies and site data
3+
### Cookies and site dataAppXq0fevzme2pys62n3e0fbqa7peapykr8v
44

55
Cookies and site data can persist across app updates and interfere with testing and troubleshooting. Clear the following when making app code changes, user account changes with the provider, or provider app configuration changes:
66

@@ -10,8 +10,20 @@ Cookies and site data can persist across app updates and interfere with testing
1010

1111
One approach to prevent lingering cookies and site data from interfering with testing and troubleshooting is to:
1212

13-
* Use a browser for testing that you can configure to delete all cookie and site data each time the browser is closed.
14-
* Close the browser between any change to the app, test user, or provider configuration.
13+
* Configure a browser
14+
* Use a browser for testing that you can configure to delete all cookie and site data each time the browser is closed.
15+
* Make sure that the browser is closed manually or by the IDE between any change to the app, test user, or provider configuration.
16+
* Use a custom command to open a browser in incognito or private mode in Visual Studio:
17+
* Open **Browse With** dialog box from Visual Studio's **Run** button.
18+
* Select the **Add** button.
19+
* Provide the path to your browser in the **Program** field.
20+
* In the **Arguments** field, provide the command-line option that the browser uses to open in incognito or private mode and the URL of the app. For example:
21+
* Google Chrome &ndash; `--incognito --new-window https://localhost:5001`
22+
* Mozilla Firefox &ndash; `-private -url https://localhost:5001`
23+
* Provide a name in the **Friendly name** field. For example, `Firefox PRIVATE`.
24+
* Select the **OK** button.
25+
* To avoid having to select the browser profile for each iteration of testing with an app, set the profile as the default with the **Set as Default** button.
26+
* Make sure that the browser is closed by the IDE between any change to the app, test user, or provider configuration.
1527

1628
### Run the Server app
1729

aspnetcore/includes/blazor-security/usermanager-signinmanager.md

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -23,10 +23,10 @@ using Microsoft.AspNetCore.Authorization;
2323
using Microsoft.AspNetCore.Mvc;
2424
using Microsoft.AspNetCore.Identity;
2525
using Microsoft.Extensions.Logging;
26-
using BlazorAppIdentityServer.Server.Models;
27-
using BlazorAppIdentityServer.Shared;
26+
using {APP NAMESPACE}.Server.Models;
27+
using {APP NAMESPACE}.Shared;
2828

29-
namespace BlazorAppIdentityServer.Server.Controllers
29+
namespace {APP NAMESPACE}.Server.Controllers
3030
{
3131
[Authorize]
3232
[ApiController]

aspnetcore/security/blazor/webassembly/additional-scenarios.md

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ description:
55
monikerRange: '>= aspnetcore-3.1'
66
ms.author: riande
77
ms.custom: mvc
8-
ms.date: 04/23/2020
8+
ms.date: 04/24/2020
99
no-loc: [Blazor, SignalR]
1010
uid: security/blazor/webassembly/additional-scenarios
1111
---
@@ -17,9 +17,6 @@ By [Javier Calvarro Nelson](https://github.com/javiercn)
1717

1818
[!INCLUDE[](~/includes/blazorwasm-3.2-template-article-notice.md)]
1919

20-
> [!NOTE]
21-
> The guidance in this article applies to ASP.NET Core 3.2 Preview 4. This topic will be updated to cover Preview 5 on Friday, April 24.
22-
2320
## Request additional access tokens
2421

2522
Most apps only require an access token to interact with the protected resources that they use. In some scenarios, an app might require more than one token in order to interact with two or more resources.
@@ -38,7 +35,7 @@ builder.Services.AddMsalAuthentication(options =>
3835
}
3936
```
4037

41-
The `IAccessTokenProvider.RequestToken` method provides an overload that allows an app to provision a token with a given set of scopes, as seen in the following example:
38+
The `IAccessTokenProvider.RequestToken` method provides an overload that allows an app to provision an access token with a given set of scopes, as seen in the following example:
4239

4340
```csharp
4441
var tokenResult = await AuthenticationService.RequestAccessToken(
@@ -85,7 +82,8 @@ builder.Services.AddHttpClient("BlazorWithIdentityApp1.ServerAPI",
8582
client => client.BaseAddress = new Uri(builder.HostEnvironment.BaseAddress))
8683
.AddHttpMessageHandler<BaseAddressAuthorizationMessageHandler>();
8784

88-
builder.Services.AddTransient(sp => sp.GetRequiredService<IHttpClientFactory>().CreateClient("BlazorWithIdentityApp1.ServerAPI"));
85+
builder.Services.AddTransient(sp => sp.GetRequiredService<IHttpClientFactory>()
86+
.CreateClient("BlazorWithIdentityApp1.ServerAPI"));
8987
```
9088

9189
Where the client is created with `CreateClient` in the preceding example, the `HttpClient` is supplied instances that include access tokens when making requests to the server project.
@@ -508,7 +506,8 @@ public void ConfigureServices(IServiceCollection services)
508506
...
509507

510508
services.AddRazorPages();
511-
services.AddScoped<AuthenticationStateProvider, ServerAuthenticationStateProvider>();
509+
services.AddScoped<AuthenticationStateProvider,
510+
ServerAuthenticationStateProvider>();
512511
services.AddScoped<SignOutSessionStateManager>();
513512

514513
Client.Program.ConfigureCommonServices(services);
@@ -534,7 +533,8 @@ In the Server app, create a *Pages* folder if it doesn't exist. Create a *_Host.
534533
<app>
535534
@if (!HttpContext.Request.Path.StartsWithSegments("/authentication"))
536535
{
537-
<component type="typeof(Wasm.Authentication.Client.App)" render-mode="Static" />
536+
<component type="typeof(Wasm.Authentication.Client.App)"
537+
render-mode="Static" />
538538
}
539539
else
540540
{

aspnetcore/security/blazor/webassembly/hosted-with-azure-active-directory-b2c.md

Lines changed: 60 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ description:
55
monikerRange: '>= aspnetcore-3.1'
66
ms.author: riande
77
ms.custom: mvc
8-
ms.date: 04/23/2020
8+
ms.date: 04/24/2020
99
no-loc: [Blazor, SignalR]
1010
uid: security/blazor/webassembly/hosted-with-azure-active-directory-b2c
1111
---
@@ -17,9 +17,6 @@ By [Javier Calvarro Nelson](https://github.com/javiercn) and [Luke Latham](https
1717

1818
[!INCLUDE[](~/includes/blazorwasm-3.2-template-article-notice.md)]
1919

20-
> [!NOTE]
21-
> The guidance in this article applies to ASP.NET Core 3.2 Preview 4. This topic will be updated to cover Preview 5 on Friday, April 24.
22-
2320
This article describes how to create a Blazor WebAssembly standalone app that uses [Azure Active Directory (AAD) B2C](/azure/active-directory-b2c/overview) for authentication.
2421

2522
## Register apps in AAD B2C and create solution
@@ -122,7 +119,7 @@ The support for authenticating and authorizing calls to ASP.NET Core Web APIs is
122119

123120
```xml
124121
<PackageReference Include="Microsoft.AspNetCore.Authentication.AzureADB2C.UI"
125-
Version="3.1.0" />
122+
Version="{VERSION}" />
126123
```
127124

128125
### Authentication service support
@@ -166,13 +163,26 @@ The *appsettings.json* file contains the options to configure the JWT bearer han
166163
{
167164
"AzureAd": {
168165
"Instance": "https://{ORGANIZATION}.b2clogin.com/",
169-
"ClientId": "{API CLIENT ID}",
166+
"ClientId": "{SERVER API APP CLIENT ID}",
170167
"Domain": "{DOMAIN}",
171168
"SignUpSignInPolicyId": "{SIGN UP OR SIGN IN POLICY}"
172169
}
173170
}
174171
```
175172

173+
Example:
174+
175+
```json
176+
{
177+
"AzureAd": {
178+
"Instance": "https://contoso.b2clogin.com/",
179+
"ClientId": "41451fa7-82d9-4673-8fa5-69eff5a761fd",
180+
"Domain": "contoso.onmicrosoft.com",
181+
"SignUpSignInPolicyId": "B2C_1_signupsignin1",
182+
}
183+
}
184+
```
185+
176186
### WeatherForecast controller
177187

178188
The WeatherForecast controller (*Controllers/WeatherForecastController.cs*) exposes a protected API with the `[Authorize]` attribute applied to the controller. It's **important** to understand that:
@@ -215,6 +225,19 @@ The `Microsoft.Authentication.WebAssembly.Msal` package transitively adds the `M
215225

216226
### Authentication service support
217227

228+
Support for `HttpClient` instances is added that include access tokens when making requests to the server project.
229+
230+
*Program.cs*:
231+
232+
```csharp
233+
builder.Services.AddHttpClient("{APP ASSEMBLY}.ServerAPI", client =>
234+
client.BaseAddress = new Uri(builder.HostEnvironment.BaseAddress))
235+
.AddHttpMessageHandler<BaseAddressAuthorizationMessageHandler>();
236+
237+
builder.Services.AddTransient(sp => sp.GetRequiredService<IHttpClientFactory>()
238+
.CreateClient("{APP ASSEMBLY}.ServerAPI"));
239+
```
240+
218241
Support for authenticating users is registered in the service container with the `AddMsalAuthentication` extension method provided by the `Microsoft.Authentication.WebAssembly.Msal` package. This method sets up all of the services required for the app to interact with the Identity Provider (IP).
219242

220243
*Program.cs*:
@@ -225,14 +248,40 @@ builder.Services.AddMsalAuthentication(options =>
225248
var authentication = options.ProviderOptions.Authentication;
226249
authentication.Authority =
227250
"{AAD B2C INSTANCE}{DOMAIN}/{SIGN UP OR SIGN IN POLICY}";
228-
authentication.ClientId = "{CLIENT ID}";
251+
authentication.ClientId = "{CLIENT APP CLIENT ID}";
229252
authentication.ValidateAuthority = false;
253+
254+
builder.Configuration.Bind("AzureAdB2C", options.ProviderOptions.Authentication);
230255
options.ProviderOptions.DefaultAccessTokenScopes.Add("{SCOPE URI}");
231256
});
232257
```
233258

234259
The `AddMsalAuthentication` method accepts a callback to configure the parameters required to authenticate an app. The values required for configuring the app can be obtained from the Azure Portal AAD configuration when you register the app.
235260

261+
Configuration is supplied by the *wwwroot/appsettings.json* file:
262+
263+
```json
264+
{
265+
"AzureAdB2C": {
266+
"Authority": "{AAD B2C INSTANCE}{DOMAIN}/{SIGN UP OR SIGN IN POLICY}",
267+
"ClientId": "{CLIENT APP CLIENT ID}",
268+
"ValidateAuthority": false
269+
}
270+
}
271+
```
272+
273+
Example:
274+
275+
```json
276+
{
277+
"AzureAdB2C": {
278+
"Authority": "https://contoso.b2clogin.com/contoso.onmicrosoft.com/B2C_1_signupsignin1",
279+
"ClientId": "4369008b-21fa-427c-abaa-9b53bf58e538",
280+
"ValidateAuthority": false
281+
}
282+
}
283+
```
284+
236285
### Access token scopes
237286

238287
The default access token scopes represent the list of access token scopes that are:
@@ -263,11 +312,11 @@ builder.Services.AddMsalAuthentication(options =>
263312
> "{API CLIENT ID OR CUSTOM VALUE}/{SCOPE NAME}");
264313
> ```
265314
266-
For more information, see <xref:security/blazor/webassembly/additional-scenarios#request-additional-access-tokens>.
315+
For more information, see the following sections of the *Additional scenarios* article:
316+
317+
* [Request additional access tokens](xref:security/blazor/webassembly/additional-scenarios#request-additional-access-tokens)
318+
* [Attach tokens to outgoing requests](xref:security/blazor/webassembly/additional-scenarios#attach-tokens-to-outgoing-requests)
267319
268-
<!--
269-
For more information, see <xref:security/blazor/webassembly/additional-scenarios#attach-tokens-to-outgoing-requests>.
270-
-->
271320
272321
### Imports file
273322

0 commit comments

Comments
 (0)