Skip to content

Commit 6b4b93e

Browse files
authored
Blazor Server auth topic updates for 5.0 (#20119)
1 parent f81eefa commit 6b4b93e

2 files changed

Lines changed: 187 additions & 21 deletions

File tree

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

Lines changed: 172 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -5,36 +5,189 @@ description: Learn how to configure Blazor Server for additional security scenar
55
monikerRange: '>= aspnetcore-3.1'
66
ms.author: riande
77
ms.custom: mvc
8-
ms.date: 06/04/2020
8+
ms.date: 10/06/2020
99
no-loc: ["ASP.NET Core Identity", cookie, Cookie, Blazor, "Blazor Server", "Blazor WebAssembly", "Identity", "Let's Encrypt", Razor, SignalR]
1010
uid: blazor/security/server/additional-scenarios
1111
---
1212
# ASP.NET Core Blazor Server additional security scenarios
1313

1414
By [Javier Calvarro Nelson](https://github.com/javiercn)
1515

16-
## Pass tokens to a Blazor Server app
16+
::: moniker range=">= aspnetcore-5.0"
1717

18-
Tokens available outside of the Razor components in a Blazor Server app can be passed to components with the approach described in this section. For sample code, including a complete `Startup.ConfigureServices` example, see the [Passing tokens to a server-side Blazor application](https://github.com/javiercn/blazor-server-aad-sample).
18+
<h2 id="pass-tokens-to-a-blazor-server-app">Pass tokens to a Blazor Server app</h2>
19+
20+
Tokens available outside of the Razor components in a Blazor Server app can be passed to components with the approach described in this section.
1921

2022
Authenticate the Blazor Server app as you would with a regular Razor Pages or MVC app. Provision and save the tokens to the authentication cookie. For example:
2123

2224
```csharp
2325
using Microsoft.AspNetCore.Authentication.OpenIdConnect;
26+
using Microsoft.IdentityModel.Protocols.OpenIdConnect;
2427

2528
...
2629

2730
services.Configure<OpenIdConnectOptions>(AzureADDefaults.OpenIdScheme, options =>
2831
{
29-
options.ResponseType = "code";
32+
options.ResponseType = OpenIdConnectResponseType.Code;
3033
options.SaveTokens = true;
3134

3235
options.Scope.Add("offline_access");
33-
options.Scope.Add("{SCOPE}");
34-
options.Resource = "{RESOURCE}";
3536
});
3637
```
3738

39+
Optionally, additional scopes are added with `options.Scope.Add("{SCOPE}");`, where the placeholder `{SCOPE}` is the additional scope to add.
40+
41+
Define a **scoped** token provider service that can be used within the Blazor app to resolve the tokens from [dependency injection (DI)](xref:blazor/fundamentals/dependency-injection):
42+
43+
```csharp
44+
public class TokenProvider
45+
{
46+
public string AccessToken { get; set; }
47+
public string RefreshToken { get; set; }
48+
}
49+
```
50+
51+
In `Startup.ConfigureServices`, add services for:
52+
53+
* `IHttpClientFactory`
54+
* `TokenProvider`
55+
56+
```csharp
57+
services.AddHttpClient();
58+
services.AddScoped<TokenProvider>();
59+
```
60+
61+
Define a class to pass in the initial app state with the access and refresh tokens:
62+
63+
```csharp
64+
public class InitialApplicationState
65+
{
66+
public string AccessToken { get; set; }
67+
public string RefreshToken { get; set; }
68+
}
69+
```
70+
71+
In the `_Host.cshtml` file, create and instance of `InitialApplicationState` and pass it as a parameter to the app:
72+
73+
```cshtml
74+
@using Microsoft.AspNetCore.Authentication
75+
76+
...
77+
78+
@{
79+
var tokens = new InitialApplicationState
80+
{
81+
AccessToken = await HttpContext.GetTokenAsync("access_token"),
82+
RefreshToken = await HttpContext.GetTokenAsync("refresh_token")
83+
};
84+
}
85+
86+
<component type="typeof(App)" param-InitialState="tokens"
87+
render-mode="ServerPrerendered" />
88+
```
89+
90+
In the `App` component (`App.razor`), resolve the service and initialize it with the data from the parameter:
91+
92+
```razor
93+
@inject TokenProvider TokenProvider
94+
95+
...
96+
97+
@code {
98+
[Parameter]
99+
public InitialApplicationState InitialState { get; set; }
100+
101+
protected override Task OnInitializedAsync()
102+
{
103+
TokenProvider.AccessToken = InitialState.AccessToken;
104+
TokenProvider.RefreshToken = InitialState.RefreshToken;
105+
106+
return base.OnInitializedAsync();
107+
}
108+
}
109+
```
110+
111+
Add a package reference to the app for the [`Microsoft.AspNet.WebApi.Client`](https://www.nuget.org/packages/Microsoft.AspNet.WebApi.Client) NuGet package.
112+
113+
In the service that makes a secure API request, inject the token provider and retrieve the token for the API request:
114+
115+
```csharp
116+
using System;
117+
using System.Net.Http;
118+
using System.Threading.Tasks;
119+
120+
public class WeatherForecastService
121+
{
122+
private readonly HttpClient client;
123+
private readonly TokenProvider tokenProvider;
124+
125+
public WeatherForecastService(IHttpClientFactory clientFactory,
126+
TokenProvider tokenProvider)
127+
{
128+
client = clientFactory.CreateClient();
129+
this.tokenProvider = tokenProvider;
130+
}
131+
132+
public async Task<WeatherForecast[]> GetForecastAsync()
133+
{
134+
var token = tokenProvider.AccessToken;
135+
var request = new HttpRequestMessage(HttpMethod.Get,
136+
"https://localhost:5003/WeatherForecast");
137+
request.Headers.Add("Authorization", $"Bearer {token}");
138+
var response = await client.SendAsync(request);
139+
response.EnsureSuccessStatusCode();
140+
141+
return await response.Content.ReadAsAsync<WeatherForecast[]>();
142+
}
143+
}
144+
```
145+
146+
<h2 id="set-the-authentication-scheme">Set the authentication scheme</h2>
147+
148+
For an app that uses more than one Authentication Middleware and thus has more than one authentication scheme, the scheme that Blazor uses can be explicitly set in the endpoint configuration of `Startup.Configure`. The following example sets the Azure Active Directory scheme:
149+
150+
```csharp
151+
endpoints.MapBlazorHub().RequireAuthorization(
152+
new AuthorizeAttribute
153+
{
154+
AuthenticationSchemes = AzureADDefaults.AuthenticationScheme
155+
});
156+
```
157+
158+
::: moniker-end
159+
160+
::: moniker range="< aspnetcore-5.0"
161+
162+
<h2 id="pass-tokens-to-a-blazor-server-app">Pass tokens to a Blazor Server app</h2>
163+
164+
Tokens available outside of the Razor components in a Blazor Server app can be passed to components with the approach described in this section.
165+
166+
Authenticate the Blazor Server app as you would with a regular Razor Pages or MVC app. Provision and save the tokens to the authentication cookie. For example:
167+
168+
```csharp
169+
using Microsoft.AspNetCore.Authentication.OpenIdConnect;
170+
using Microsoft.IdentityModel.Protocols.OpenIdConnect;
171+
172+
...
173+
174+
services.Configure<OpenIdConnectOptions>(AzureADDefaults.OpenIdScheme, options =>
175+
{
176+
options.ResponseType = OpenIdConnectResponseType.Code;
177+
options.SaveTokens = true;
178+
179+
options.Scope.Add("offline_access");
180+
});
181+
```
182+
183+
Optionally, additional scopes are added with `options.Scope.Add("{SCOPE}");`, where the placeholder `{SCOPE}` is the additional scope to add.
184+
185+
Optionally, the resource is specified with `options.Resource = "{RESOURCE}";`, where the placeholder `{RESOURCE}` is the resource. For example:
186+
187+
```csharp
188+
options.Resource = "https://graph.microsoft.com";
189+
```
190+
38191
Define a class to pass in the initial app state with the access and refresh tokens:
39192

40193
```csharp
@@ -107,9 +260,9 @@ In the `App` component (`App.razor`), resolve the service and initialize it with
107260
}
108261
```
109262

110-
Add a package reference to the app for the [Microsoft.AspNet.WebApi.Client](https://www.nuget.org/packages/Microsoft.AspNet.WebApi.Client) NuGet package.
263+
Add a package reference to the app for the [`Microsoft.AspNet.WebApi.Client`](https://www.nuget.org/packages/Microsoft.AspNet.WebApi.Client) NuGet package.
111264

112-
In the service that makes a secure API request, inject the token provider and retrieve the token to call the API:
265+
In the service that makes a secure API request, inject the token provider and retrieve the token for the API request:
113266

114267
```csharp
115268
using System;
@@ -118,32 +271,31 @@ using System.Threading.Tasks;
118271

119272
public class WeatherForecastService
120273
{
121-
private readonly TokenProvider store;
274+
private readonly HttpClient client;
275+
private readonly TokenProvider tokenProvider;
122276

123277
public WeatherForecastService(IHttpClientFactory clientFactory,
124278
TokenProvider tokenProvider)
125279
{
126-
Client = clientFactory.CreateClient();
127-
store = tokenProvider;
280+
client = clientFactory.CreateClient();
281+
this.tokenProvider = tokenProvider;
128282
}
129283

130-
public HttpClient Client { get; }
131-
132-
public async Task<WeatherForecast[]> GetForecastAsync(DateTime startDate)
284+
public async Task<WeatherForecast[]> GetForecastAsync()
133285
{
134-
var token = store.AccessToken;
286+
var token = tokenProvider.AccessToken;
135287
var request = new HttpRequestMessage(HttpMethod.Get,
136288
"https://localhost:5003/WeatherForecast");
137289
request.Headers.Add("Authorization", $"Bearer {token}");
138-
var response = await Client.SendAsync(request);
290+
var response = await client.SendAsync(request);
139291
response.EnsureSuccessStatusCode();
140292

141293
return await response.Content.ReadAsAsync<WeatherForecast[]>();
142294
}
143295
}
144296
```
145297

146-
## Set the authentication scheme
298+
<h2 id="set-the-authentication-scheme">Set the authentication scheme</h2>
147299

148300
For an app that uses more than one Authentication Middleware and thus has more than one authentication scheme, the scheme that Blazor uses can be explicitly set in the endpoint configuration of `Startup.Configure`. The following example sets the Azure Active Directory scheme:
149301

@@ -157,7 +309,7 @@ endpoints.MapBlazorHub().RequireAuthorization(
157309

158310
## Use OpenID Connect (OIDC) v2.0 endpoints
159311

160-
The authentication library and Blazor templates use OpenID Connect (OIDC) v1.0 endpoints. To use a v2.0 endpoint, configure the <xref:Microsoft.AspNetCore.Builder.OpenIdConnectOptions.Authority?displayProperty=nameWithType> option in the <xref:Microsoft.AspNetCore.Builder.OpenIdConnectOptions>:
312+
In versions of ASP.NET Core prior to 5.0, the authentication library and Blazor templates use OpenID Connect (OIDC) v1.0 endpoints. To use a v2.0 endpoint with versions of ASP.NET Core prior to 5.0, configure the <xref:Microsoft.AspNetCore.Builder.OpenIdConnectOptions.Authority?displayProperty=nameWithType> option in the <xref:Microsoft.AspNetCore.Builder.OpenIdConnectOptions>:
161313

162314
```csharp
163315
services.Configure<OpenIdConnectOptions>(AzureADDefaults.OpenIdScheme,
@@ -215,3 +367,5 @@ If tacking on a segment to the authority isn't appropriate for the app's OIDC pr
215367
```
216368

217369
You can find the App ID URI to use in the OIDC provider app registration description.
370+
371+
::: moniker-end

aspnetcore/blazor/security/server/index.md

Lines changed: 15 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -5,15 +5,15 @@ description: Learn how to secure Blazor Server apps as ASP.NET Core applications
55
monikerRange: '>= aspnetcore-3.1'
66
ms.author: riande
77
ms.custom: mvc
8-
ms.date: 05/02/2020
8+
ms.date: 10/06/2020
99
no-loc: ["ASP.NET Core Identity", cookie, Cookie, Blazor, "Blazor Server", "Blazor WebAssembly", "Identity", "Let's Encrypt", Razor, SignalR]
1010
uid: blazor/security/server/index
1111
---
1212
# Secure ASP.NET Core Blazor Server apps
1313

1414
By [Luke Latham](https://github.com/guardrex)
1515

16-
Blazor Server apps are configured for security in the same manner as ASP.NET Core apps. For more information, see the articles under <xref:security/index>. Topics under this overview apply specifically to Blazor Server.
16+
Blazor Server apps are configured for security in the same manner as ASP.NET Core apps. For more information, see the articles under <xref:security/index>. Topics under this overview apply specifically to Blazor Server.
1717

1818
## Blazor Server project template
1919

@@ -92,7 +92,14 @@ Using the `-o|--output` option, the command uses the value provided for the `{AP
9292
* Create a folder for the project.
9393
* Name the project.
9494

95-
For more information, see the [`dotnet new`](/dotnet/core/tools/dotnet-new) command in the .NET Core Guide.
95+
For more information:
96+
97+
* See the [`dotnet new`](/dotnet/core/tools/dotnet-new) command in the .NET Core Guide.
98+
* Execute the help command for the Blazor Server template (`blazorserver`) in a command shell:
99+
100+
```dotnetcli
101+
dotnet new blazorserver --help
102+
```
96103

97104
---
98105

@@ -102,3 +109,8 @@ Scaffold Identity into a Blazor Server project:
102109

103110
* [Without existing authorization](xref:security/authentication/scaffold-identity#scaffold-identity-into-a-blazor-server-project-without-existing-authorization).
104111
* [With authorization](xref:security/authentication/scaffold-identity#scaffold-identity-into-a-blazor-server-project-with-authorization).
112+
113+
## Additional resources
114+
115+
* [Quickstart: Add sign-in with Microsoft to an ASP.NET Core web app](/azure/active-directory/develop/quickstart-v2-aspnet-core-webapp)
116+
* [Quickstart: Protect an ASP.NET Core web API with Microsoft identity platform](/azure/active-directory/develop/quickstart-v2-aspnet-core-web-api)

0 commit comments

Comments
 (0)