Skip to content

Commit 7926160

Browse files
authored
Add pivot for Server-rendering sample (#34611)
1 parent 2f669ea commit 7926160

File tree

2 files changed

+190
-5
lines changed

2 files changed

+190
-5
lines changed

aspnetcore/blazor/security/blazor-web-app-with-oidc.md

Lines changed: 186 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@ This article describes how to secure a Blazor Web App with [OpenID Connect (OIDC
1717

1818
:::zone pivot="without-bff-pattern"
1919

20-
This version of the article covers implementing OIDC without adopting the [Backend for Frontend (BFF) pattern](/azure/architecture/patterns/backends-for-frontends). The BFF pattern is useful for making authenticated requests to external services. Change the article version selector to **OIDC with BFF pattern** if the app's specification calls for adopting the BFF pattern.
20+
This version of the article covers implementing OIDC without adopting the [Backend for Frontend (BFF) pattern](/azure/architecture/patterns/backends-for-frontends) with an app that adopts global Interactive Auto rendering (server and `.Client` projects). The BFF pattern is useful for making authenticated requests to external services. Change the article version selector to **OIDC with BFF pattern** if the app's specification calls for adopting the BFF pattern.
2121

2222
The following specification is covered:
2323

@@ -92,10 +92,10 @@ The following <xref:Microsoft.AspNetCore.Authentication.OpenIdConnect.OpenIdConn
9292
oidcOptions.Scope.Add(OpenIdConnectScope.OpenIdProfile);
9393
```
9494

95-
* <xref:Microsoft.AspNetCore.Authentication.RemoteAuthenticationOptions.SaveTokens%2A>: Defines whether access and refresh tokens should be stored in the <xref:Microsoft.AspNetCore.Authentication.AuthenticationProperties> after a successful authorization. This property is set to `false` to reduce the size of the final authentication cookie.
95+
* <xref:Microsoft.AspNetCore.Authentication.RemoteAuthenticationOptions.SaveTokens%2A>: Defines whether access and refresh tokens should be stored in the <xref:Microsoft.AspNetCore.Authentication.AuthenticationProperties> after a successful authorization. This property is set to `true` so the refresh token gets stored for non-interactive token refresh.
9696

9797
```csharp
98-
oidcOptions.SaveTokens = false;
98+
oidcOptions.SaveTokens = true;
9999
```
100100

101101
* Scope for offline access (<xref:Microsoft.AspNetCore.Authentication.OpenIdConnect.OpenIdConnectOptions.Scope%2A>): The `offline_access` scope is required for the refresh token.
@@ -251,6 +251,189 @@ The sample app only provides a user name and email for display purposes. It does
251251

252252
:::zone-end
253253

254+
:::zone pivot="without-bff-pattern-server"
255+
256+
This version of the article covers implementing OIDC without adopting the [Backend for Frontend (BFF) pattern](/azure/architecture/patterns/backends-for-frontends) with an app that adopts global Interactive Server rendering (single project). The BFF pattern is useful for making authenticated requests to external services. Change the article version selector to **OIDC with BFF pattern** if the app's specification calls for adopting the BFF pattern with global Interactive Auto rendering.
257+
258+
The following specification is covered:
259+
260+
* The Blazor Web App uses [the Server render mode with global interactivity](xref:blazor/components/render-modes).
261+
* This app is a starting point for any OIDC authentication flow. OIDC is configured manually in the app and doesn't rely upon [Microsoft Entra ID](https://www.microsoft.com/security/business/microsoft-entra) or [Microsoft Identity Web](/entra/msal/dotnet/microsoft-identity-web/) packages, nor does the sample app require [Microsoft Azure](https://azure.microsoft.com/) hosting. However, the sample app can be used with Entra, Microsoft Identity Web, and hosted in Azure.
262+
* Automatic non-interactive token refresh.
263+
264+
For an alternative experience using [Microsoft Authentication Library for .NET](/entra/msal/dotnet/), [Microsoft Identity Web](/entra/msal/dotnet/microsoft-identity-web/), and [Microsoft Entra ID](https://www.microsoft.com/security/business/identity-access/microsoft-entra-id), see <xref:blazor/security/blazor-web-app-entra>.
265+
266+
## Sample app
267+
268+
The sample app consists of a single server-side Blazor Web App project (`BlazorWebAppOidcServer`).
269+
270+
Access the sample app through the latest version folder from the repository's root with the following link. The project is in the `BlazorWebAppOidcServer` folder for .NET 8 or later.
271+
272+
[View or download sample code](https://github.com/dotnet/blazor-samples) ([how to download](xref:blazor/fundamentals/index#sample-apps))
273+
274+
## Configuration
275+
276+
This section explains how to configure the sample app.
277+
278+
> [!NOTE]
279+
> For Microsoft Entra ID or Azure AD B2C, you can use <xref:Microsoft.Identity.Web.AppBuilderExtension.AddMicrosoftIdentityWebApp%2A> from [Microsoft Identity Web](/entra/msal/dotnet/microsoft-identity-web/) ([`Microsoft.Identity.Web` NuGet package](https://www.nuget.org/packages/Microsoft.Identity.Web), [API documentation](<xref:Microsoft.Identity.Web?displayProperty=fullName>)), which adds both the OIDC and Cookie authentication handlers with the appropriate defaults. The sample app and the guidance in this section doesn't use Microsoft Identity Web. The guidance demonstrates how to configure the OIDC handler *manually* for any OIDC provider. For more information on implementing Microsoft Identity Web, see the linked resources.
280+
281+
### Establish the client secret
282+
283+
[!INCLUDE[](~/blazor/security/includes/secure-authentication-flows.md)]
284+
285+
For local development testing, use the [Secret Manager tool](xref:security/app-secrets) to store the app's client secret under the configuration key `Authentication:Schemes:MicrosoftOidc:ClientSecret`.
286+
287+
> [!NOTE]
288+
> If the app uses Microsoft Entra ID or Azure AD B2C, create a client secret in the app's registration in the Entra or Azure portal (**Manage** > **Certificates & secrets** > **New client secret**). Use the **Value** of the new secret in the following guidance.
289+
290+
The [sample app](#sample-app) hasn't been initialized for the Secret Manager tool. Use a command shell, such as the Developer PowerShell command shell in Visual Studio, to execute the following command. Before executing the command, change the directory with the `cd` command to the project's directory. The command establishes a user secrets identifier (`<UserSecretsId>` in the app's project file):
291+
292+
```dotnetcli
293+
dotnet user-secrets init
294+
```
295+
296+
Execute the following command to set the client secret. The `{SECRET}` placeholder is the client secret obtained from the app's registration:
297+
298+
```dotnetcli
299+
dotnet user-secrets set "Authentication:Schemes:MicrosoftOidc:ClientSecret" "{SECRET}"
300+
```
301+
302+
If using Visual Studio, you can confirm the secret is set by right-clicking the project in **Solution Explorer** and selecting **Manage User Secrets**.
303+
304+
### Configure the app
305+
306+
The following <xref:Microsoft.AspNetCore.Authentication.OpenIdConnect.OpenIdConnectOptions> configuration is found in the project's `Program` file on the call to <xref:Microsoft.Extensions.DependencyInjection.OpenIdConnectExtensions.AddOpenIdConnect%2A>:
307+
308+
* <xref:Microsoft.AspNetCore.Builder.RemoteAuthenticationOptions.SignInScheme%2A>: Sets the authentication scheme corresponding to the middleware responsible of persisting user's identity after a successful authentication. The OIDC handler needs to use a sign-in scheme that's capable of persisting user credentials across requests. The following line is present merely for demonstration purposes. If omitted, <xref:Microsoft.AspNetCore.Authentication.AuthenticationOptions.DefaultSignInScheme%2A> is used as a fallback value.
309+
310+
```csharp
311+
oidcOptions.SignInScheme = CookieAuthenticationDefaults.AuthenticationScheme;
312+
```
313+
314+
* Scopes for `openid` and `profile` (<xref:Microsoft.AspNetCore.Authentication.OpenIdConnect.OpenIdConnectOptions.Scope%2A>) (Optional): The `openid` and `profile` scopes are also configured by default because they're required for the OIDC handler to work, but these may need to be re-added if scopes are included in the `Authentication:Schemes:MicrosoftOidc:Scope` configuration. For general configuration guidance, see <xref:fundamentals/configuration/index> and <xref:blazor/fundamentals/configuration>.
315+
316+
```csharp
317+
oidcOptions.Scope.Add(OpenIdConnectScope.OpenIdProfile);
318+
```
319+
320+
* <xref:Microsoft.AspNetCore.Authentication.RemoteAuthenticationOptions.SaveTokens%2A>: Defines whether access and refresh tokens should be stored in the <xref:Microsoft.AspNetCore.Authentication.AuthenticationProperties> after a successful authorization. This property is set to `true` so the refresh token gets stored for non-interactive token refresh.
321+
322+
```csharp
323+
oidcOptions.SaveTokens = true;
324+
325+
* Scope for offline access (<xref:Microsoft.AspNetCore.Authentication.OpenIdConnect.OpenIdConnectOptions.Scope%2A>): The `offline_access` scope is required for the refresh token.
326+
327+
```csharp
328+
oidcOptions.Scope.Add(OpenIdConnectScope.OfflineAccess);
329+
```
330+
331+
* <xref:Microsoft.AspNetCore.Authentication.OpenIdConnect.OpenIdConnectOptions.Authority%2A> and <xref:Microsoft.AspNetCore.Authentication.OpenIdConnect.OpenIdConnectOptions.ClientId%2A>: Sets the Authority and Client ID for OIDC calls.
332+
333+
```csharp
334+
oidcOptions.Authority = "{AUTHORITY}";
335+
oidcOptions.ClientId = "{CLIENT ID}";
336+
```
337+
338+
Example:
339+
340+
* Authority (`{AUTHORITY}`): `https://login.microsoftonline.com/aaaabbbb-0000-cccc-1111-dddd2222eeee/v2.0/` (uses Tenant ID `aaaabbbb-0000-cccc-1111-dddd2222eeee`)
341+
* Client Id (`{CLIENT ID}`): `00001111-aaaa-2222-bbbb-3333cccc4444`
342+
343+
```csharp
344+
oidcOptions.Authority = "https://login.microsoftonline.com/aaaabbbb-0000-cccc-1111-dddd2222eeee/v2.0/";
345+
oidcOptions.ClientId = "00001111-aaaa-2222-bbbb-3333cccc4444";
346+
```
347+
348+
Example for Microsoft Azure "common" authority:
349+
350+
The "common" authority should be used for multi-tenant apps. You can also use the "common" authority for single-tenant apps, but a custom <xref:Microsoft.IdentityModel.Tokens.TokenValidationParameters.IssuerValidator%2A> is required, as shown later in this section.
351+
352+
```csharp
353+
oidcOptions.Authority = "https://login.microsoftonline.com/common/v2.0/";
354+
```
355+
356+
* <xref:Microsoft.AspNetCore.Authentication.OpenIdConnect.OpenIdConnectOptions.ResponseType%2A>: Configures the OIDC handler to only perform authorization code flow. Implicit grants and hybrid flows are unnecessary in this mode. The OIDC handler automatically requests the appropriate tokens using the code returned from the authorization endpoint.
357+
358+
```csharp
359+
oidcOptions.ResponseType = OpenIdConnectResponseType.Code;
360+
```
361+
362+
> [!NOTE]
363+
> In the Entra or Azure portal's **Implicit grant and hybrid flows** app registration configuration, do **not** select either checkbox for the authorization endpoint to return **Access tokens** or **ID tokens**.
364+
365+
* <xref:Microsoft.AspNetCore.Authentication.OpenIdConnect.OpenIdConnectOptions.MapInboundClaims%2A> and configuration of <xref:Microsoft.IdentityModel.Tokens.TokenValidationParameters.NameClaimType%2A> and <xref:Microsoft.IdentityModel.Tokens.TokenValidationParameters.RoleClaimType%2A>: Many OIDC servers use "`name`" and "`role`" rather than the SOAP/WS-Fed defaults in <xref:System.Security.Claims.ClaimTypes>. When <xref:Microsoft.AspNetCore.Authentication.OpenIdConnect.OpenIdConnectOptions.MapInboundClaims%2A> is set to `false`, the handler doesn't perform claims mappings, and the claim names from the JWT are used directly by the app. The following example sets the role claim type to "`roles`," which is appropriate for [Microsoft Entra ID (ME-ID)](https://www.microsoft.com/security/business/microsoft-entra). Consult your identity provider's documentation for more information.
366+
367+
> [!NOTE]
368+
> <xref:Microsoft.AspNetCore.Authentication.OpenIdConnect.OpenIdConnectOptions.MapInboundClaims%2A> must be set to `false` for most OIDC providers, which prevents renaming claims.
369+
370+
```csharp
371+
oidcOptions.MapInboundClaims = false;
372+
oidcOptions.TokenValidationParameters.NameClaimType = "name";
373+
oidcOptions.TokenValidationParameters.RoleClaimType = "roles";
374+
```
375+
376+
* Path configuration: Paths must match the redirect URI (login callback path) and post logout redirect (signed-out callback path) paths configured when registering the application with the OIDC provider. In the Azure portal, paths are configured in the **Authentication** blade of the app's registration. Both the sign-in and sign-out paths must be registered as redirect URIs. The default values are `/signin-oidc` and `/signout-callback-oidc`.
377+
378+
* <xref:Microsoft.AspNetCore.Builder.RemoteAuthenticationOptions.CallbackPath>: The request path within the app's base path where the user-agent is returned.
379+
380+
Configure the signed-out callback path in the app's OIDC provider registration. In the following example, the `{PORT}` placeholder is the app's port:
381+
382+
> :::no-loc text="https://localhost:{PORT}/signin-oidc":::
383+
384+
> [!NOTE]
385+
> A port isn't required for `localhost` addresses when using Microsoft Entra ID. Most other OIDC providers require the correct port.
386+
387+
* <xref:Microsoft.AspNetCore.Authentication.OpenIdConnect.OpenIdConnectOptions.SignedOutCallbackPath%2A> (configuration key: "`SignedOutCallbackPath`"): The request path within the app's base path intercepted by the OIDC handler where the user agent is first returned after signing out from the identity provider. The sample app doesn't set a value for the path because the default value of "`/signout-callback-oidc`" is used. After intercepting the request, the OIDC handler redirects to the <xref:Microsoft.AspNetCore.Authentication.OpenIdConnect.OpenIdConnectOptions.SignedOutRedirectUri%2A> or <xref:Microsoft.AspNetCore.Authentication.AuthenticationProperties.RedirectUri%2A>, if specified.
388+
389+
Configure the signed-out callback path in the app's OIDC provider registration. In the following example, the `{PORT}` placeholder is the app's port:
390+
391+
> :::no-loc text="https://localhost:{PORT}/signout-callback-oidc":::
392+
393+
> [!NOTE]
394+
> When using Microsoft Entra ID, set the path in the **Web** platform configuration's **Redirect URI** entries in the Entra or Azure portal. A port isn't required for `localhost` addresses when using Entra. Most other OIDC providers require the correct port. If you don't add the signed-out callback path URI to the app's registration in Entra, Entra refuses to redirect the user back to the app and merely asks them to close their browser window.
395+
396+
* <xref:Microsoft.AspNetCore.Builder.OpenIdConnectOptions.RemoteSignOutPath%2A>: Requests received on this path cause the handler to invoke sign-out using the sign-out scheme.
397+
398+
In the following example, the `{PORT}` placeholder is the app's port:
399+
400+
> :::no-loc text="https://localhost/signout-oidc":::
401+
402+
> [!NOTE]
403+
> When using Microsoft Entra ID, set the **Front-channel logout URL** in the Entra or Azure portal. A port isn't required for `localhost` addresses when using Entra. Most other OIDC providers require the correct port.
404+
405+
```csharp
406+
oidcOptions.CallbackPath = new PathString("{PATH}");
407+
oidcOptions.SignedOutCallbackPath = new PathString("{PATH}");
408+
oidcOptions.RemoteSignOutPath = new PathString("{PATH}");
409+
```
410+
411+
Examples (default values):
412+
413+
```csharp
414+
oidcOptions.CallbackPath = new PathString("/signin-oidc");
415+
oidcOptions.SignedOutCallbackPath = new PathString("/signout-callback-oidc");
416+
oidcOptions.RemoteSignOutPath = new PathString("/signout-oidc");
417+
```
418+
419+
* (*Microsoft Azure only with the "common" endpoint*) <xref:Microsoft.IdentityModel.Tokens.TokenValidationParameters.IssuerValidator%2A?displayProperty=nameWithType>: Many OIDC providers work with the default issuer validator, but we need to account for the issuer parameterized with the Tenant ID (`{TENANT ID}`) returned by `https://login.microsoftonline.com/common/v2.0/.well-known/openid-configuration`. For more information, see [SecurityTokenInvalidIssuerException with OpenID Connect and the Azure AD "common" endpoint (`AzureAD/azure-activedirectory-identitymodel-extensions-for-dotnet` #1731)](https://github.com/AzureAD/azure-activedirectory-identitymodel-extensions-for-dotnet/issues/1731).
420+
421+
Only for apps using Microsoft Entra ID or Azure AD B2C with the "common" endpoint:
422+
423+
```csharp
424+
var microsoftIssuerValidator = AadIssuerValidator.GetAadIssuerValidator(oidcOptions.Authority);
425+
oidcOptions.TokenValidationParameters.IssuerValidator = microsoftIssuerValidator.Validate;
426+
```
427+
428+
## Sample app code
429+
430+
Inspect the sample app for the following features:
431+
432+
* Automatic non-interactive token refresh with the help of a custom cookie refresher (`CookieOidcRefresher.cs`).
433+
* The `Weather` component uses the [`[Authorize]` attribute](xref:Microsoft.AspNetCore.Authorization.AuthorizeAttribute) to prevent unauthorized access. For more information on requiring authorization across the app via an [authorization policy](xref:security/authorization/policies) and opting out of authorization at a subset of public endpoints, see the [Razor Pages OIDC guidance](xref:security/authentication/configure-oidc-web-authentication#force-authorization).
434+
435+
:::zone-end
436+
254437
:::zone pivot="with-bff-pattern"
255438

256439
This version of the article covers implementing OIDC with the [Backend for Frontend (BFF) pattern](/azure/architecture/patterns/backends-for-frontends). Change the article version selector to **OIDC without BFF pattern** if the app's specification doesn't call for adopting the BFF pattern.

aspnetcore/zone-pivot-groups.yml

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -85,9 +85,11 @@ groups:
8585
prompt: Choose the app specification
8686
pivots:
8787
- id: without-bff-pattern
88-
title: OIDC without BFF pattern
88+
title: OIDC without BFF pattern (Auto)
89+
- id: without-bff-pattern-server
90+
title: OIDC without BFF pattern (Server)
8991
- id: with-bff-pattern
90-
title: OIDC with BFF pattern
92+
title: OIDC with BFF pattern (Auto)
9193
- id: tooling
9294
title: Tooling
9395
prompt: Select your tooling

0 commit comments

Comments
 (0)