diff --git a/aspnetcore/blazor/hybrid/security/index.md b/aspnetcore/blazor/hybrid/security/index.md index 1756e7fa0c31..c40c8da90666 100644 --- a/aspnetcore/blazor/hybrid/security/index.md +++ b/aspnetcore/blazor/hybrid/security/index.md @@ -17,7 +17,7 @@ This article describes ASP.NET Core's support for the configuration and manageme ::: moniker range=">= aspnetcore-7.0" -Authentication in Blazor Hybrid apps is handled by native platform libraries, as they offer enhanced security guarantees that the browser sandbox can't offer. Authentication of native apps uses an OS-specific mechanism or via a federated protocol, such as [OpenID Connect (OIDC)](https://openid.net/connect/). Follow the guidance for the identity provider that you've selected for the app and then further integrate identity with Blazor using the guidance in this article. +Authentication in Blazor Hybrid apps is handled by native platform libraries, as they offer enhanced security guarantees that the browser sandbox can't offer. Authentication of native apps uses an OS-specific mechanism or via a federated protocol, such as [OpenID Connect (OIDC)](https://openid.net/developers/how-connect-works/). Follow the guidance for the identity provider that you've selected for the app and then further integrate identity with Blazor using the guidance in this article. Integrating authentication must achieve the following goals for Razor components and services: @@ -548,7 +548,7 @@ When implementing authentication: ::: moniker range=">= aspnetcore-6.0 < aspnetcore-7.0" -Authentication in Blazor Hybrid apps is handled by native platform libraries, as they offer enhanced security guarantees that the browser sandbox can't offer. Authentication of native apps uses an OS-specific mechanism or via a federated protocol, such as [OpenID Connect (OIDC)](https://openid.net/connect/). Follow the guidance for the identity provider that you've selected for the app and then further integrate identity with Blazor using the guidance in this article. +Authentication in Blazor Hybrid apps is handled by native platform libraries, as they offer enhanced security guarantees that the browser sandbox can't offer. Authentication of native apps uses an OS-specific mechanism or via a federated protocol, such as [OpenID Connect (OIDC)](https://openid.net/developers/how-connect-works/). Follow the guidance for the identity provider that you've selected for the app and then further integrate identity with Blazor using the guidance in this article. Integrating authentication must achieve the following goals for Razor components and services: diff --git a/aspnetcore/blazor/security/server/blazor-web-app-with-oidc.md b/aspnetcore/blazor/security/server/blazor-web-app-with-oidc.md new file mode 100644 index 000000000000..053cb0db4329 --- /dev/null +++ b/aspnetcore/blazor/security/server/blazor-web-app-with-oidc.md @@ -0,0 +1,532 @@ +--- +title: Secure an ASP.NET Core Blazor Web App with OpenID Connect (OIDC) +author: guardrex +description: Learn how to secure a Blazor WebAssembly App with OpenID Connect (OIDC). +monikerRange: '>= aspnetcore-8.0' +ms.author: riande +ms.custom: mvc +ms.date: 02/14/2024 +uid: blazor/security/server/blazor-web-app-oidc +zone_pivot_groups: blazor-web-app-oidc-specification +--- +# Secure an ASP.NET Core Blazor Web App with OpenID Connect (OIDC) + + + +This article describes how to secure a Blazor Web App with [OpenID Connect (OIDC)](https://openid.net/developers/how-connect-works/) using a sample app in the [`dotnet/blazor-samples` GitHub repository (.NET 8 or later)](https://github.com/dotnet/blazor-samples). + +:::zone pivot="without-bff-pattern" + +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. + +The following specification is covered: + +* The Blazor Web App uses [the Auto render mode with global interactivity](xref:blazor/components/render-modes). +* Custom auth state provider services are used by the server and client apps to capture the user's authentication state and flow it between the server and client. +* 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 used with Entra, Microsoft Identity Web, and hosted in Azure. +* Automatic non-interactive token refresh. + +## Sample app + +The sample app consists of two projects: + +* `BlazorWebAppOidc`: Server-side project of the Blazor Web App, containing an example [Minimal API](xref:fundamentals/minimal-apis) endpoint for weather data. +* `BlazorWebAppOidc.Client`: Client-side project of the Blazor Web App. + +Access the sample apps through the latest version folder from the repository's root with the following link. The projects are in the `BlazorWebAppOidc` folder for .NET 8 or later. + +[View or download sample code](https://github.com/dotnet/blazor-samples) ([how to download](xref:index#how-to-download-a-sample)) + +## Server-side Blazor Web App project (`BlazorWebAppOidc`) + +The `BlazorWebAppOidc` project is the server-side project of the Blazor Web App. + +The `BlazorWebAppOidc.http` file can be used for testing the weather data request. Note that the `BlazorWebAppOidc` project must be running to test the endpoint, and the endpoint is hardcoded into the file. For more information, see . + +### Configuration + +This section explains how to configure the sample app. + +> [!NOTE] +> For Microsoft Entra ID and Azure AD B2C, you can use 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]()), 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. + +The following configuration is found in the project's `Program` file on the call to : + +* : 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, is used as a fallback value. + + ```csharp + oidcOptions.SignInScheme = CookieAuthenticationDefaults.AuthenticationScheme; + ``` + +* Scopes for `openid` and `profile` () (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 and . + + ```csharp + oidcOptions.Scope.Add(OpenIdConnectScope.OpenIdProfile); + ``` + +* : Defines whether access and refresh tokens should be stored in the after a successful authorization. This property is set to `false` by default to reduce the size of the final authentication cookie. + + ```csharp + oidcOptions.SaveTokens = false; + ``` + +* Scope for offline access (): The `offline_access` scope is required for the refresh token. + + ```csharp + oidcOptions.Scope.Add(OpenIdConnectScope.OfflineAccess); + ``` + +* and : Sets the Authority and Client ID for OIDC calls. + + ```csharp + oidcOptions.Authority = "{AUTHORITY}"; + oidcOptions.ClientId = "{CLIENT ID}"; + ``` + + Example: + + * Authority (`{AUTHORITY}`): `https://login.microsoftonline.com/a3942615-d115-4eb7-bc84-9974abcf5064/v2.0/` (uses Tenant ID `a3942615-d115-4eb7-bc84-9974abcf5064`) + * Client Id (`{CLIENT ID}`): `4ba4de56-9cef-45d9-83fa-a4c18f9f5f0f` + + ```csharp + oidcOptions.Authority = "https://login.microsoftonline.com/a3942615-d115-4eb7-bc84-9974abcf5064/v2.0/"; + oidcOptions.ClientId = "4ba4de56-9cef-45d9-83fa-a4c18f9f5f0f"; + ``` + + Example for Microsoft Azure "common" authority: + + The "common" authority should be used for multi-tenant apps. You can also use the "common" authority for single-tenant apps, but a custom is required, as shown later in this section. + + ```csharp + oidcOptions.Authority = "https://login.microsoftonline.com/common/v2.0/"; + ``` + +* : The OIDC client secret. + + **The following example is only for testing and demonstration purposes. Don't store the client secret in the app's assembly or check the secret into source control.** Store the client secret in [User Secrets](xref:security/app-secrets), [Azure Key Vault](xref:security/key-vault-configuration), or an [environment variable](xref:fundamentals/configuration/index#non-prefixed-environment-variables). + + Authentication scheme configuration is automatically read from `builder.Configuration["Authentication:Schemes:{SCHEME NAME}:{PropertyName}"]`, where the `{SCHEME NAME}` placeholder is the scheme, which is `MicrosoftOidc` by default. Because configuration is preconfigured, a client secret can automatically be read via the `Authentication:Schemes:MicrosoftOidc:ClientSecret` configuration key. On the server using environment variables, name the environment variable `Authentication__Schemes__MicrosoftOidc__ClientSecret`: + + ```dotnetcli + set Authentication__Schemes__MicrosoftOidc__ClientSecret={CLIENT SECRET} + ``` + + **For demonstration and testing only**, the can be set directly. Don't set the value directly for deployed production apps. For slightly improved security, [conditionally compile](/dotnet/csharp/language-reference/preprocessor-directives#conditional-compilation) the line with the `DEBUG` symbol: + + ```csharp + #if DEBUG + oidcOptions.ClientSecret = "{CLIENT SECRET}"; + #endif + ``` + + Example: + + Client secret (`{CLIENT SECRET}`): `463471c8c4...f90d674bc9` (shortened for display) + + ```csharp + #if DEBUG + oidcOptions.ClientSecret = "463471c8c4...137f90d674bc9"; + #endif + ``` + +* : Configures the OIDC handler to only perform authorization code flow. Implicit grants and hybrid flows are unnecessary in this mode. + + 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**. The OIDC handler automatically requests the appropriate tokens using the code returned from the authorization endpoint. + + ```csharp + oidcOptions.ResponseType = OpenIdConnectResponseType.Code; + ``` + +* and configuration of and : Many OIDC servers use "`name`" and "`role`" rather than the SOAP/WS-Fed defaults in . When 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 manually maps the name and role claims: + + ```csharp + oidcOptions.MapInboundClaims = false; + oidcOptions.TokenValidationParameters.NameClaimType = JwtRegisteredClaimNames.Name; + oidcOptions.TokenValidationParameters.RoleClaimType = "role"; + ``` + +* 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`. + + * : The request path within the app's base path where the user-agent is returned. + + In the Entra or Azure portal, set the path in the **Web** platform configuration's **Redirect URI**: + + > :::no-loc text="https://localhost/signin-oidc"::: + + > [!NOTE] + > A port isn't required for `localhost` addresses. + + * : The request path within the app's base path where the user agent is returned after sign out from the identity provider. + + In the Entra or Azure portal, set the path in the **Web** platform configuration's **Redirect URI**: + + > :::no-loc text="https://localhost/signout-callback-oidc"::: + + > [!NOTE] + > A port isn't required for `localhost` addresses. + + > [!NOTE] + > If using Microsoft Identity Web, the provider currently only redirects back to the if the `microsoftonline.com` Authority (`https://login.microsoftonline.com/{TENANT ID}/v2.0/`) is used. This limitation doesn't exist if you can use the "common" Authority with Microsoft Identity Web. For more information, see [postLogoutRedirectUri not working when authority url contains a tenant ID (`AzureAD/microsoft-authentication-library-for-js` #5783)](https://github.com/AzureAD/microsoft-authentication-library-for-js/issues/5783). + + * : Requests received on this path cause the handler to invoke sign-out using the sign-out scheme. + + In the Entra or Azure portal, set the **Front-channel logout URL**: + + > :::no-loc text="https://localhost/signout-oidc"::: + + > [!NOTE] + > A port isn't required for `localhost` addresses. + + ```csharp + oidcOptions.CallbackPath = new PathString("{PATH}"); + oidcOptions.SignedOutCallbackPath = new PathString("{PATH}"); + oidcOptions.RemoteSignOutPath = new PathString("{PATH}"); + ``` + + Examples (default values): + + ```csharp + oidcOptions.CallbackPath = new PathString("/signin-oidc"); + oidcOptions.SignedOutCallbackPath = new PathString("/signout-callback-oidc"); + oidcOptions.RemoteSignOutPath = new PathString("/signout-oidc"); + ``` + +* (*Microsoft Azure only with the "common" endpoint*) : 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). + + Only for apps using Microsoft Entra ID or Azure AD B2C with the "common" endpoint: + + ```csharp + var microsoftIssuerValidator = AadIssuerValidator.GetAadIssuerValidator(oidcOptions.Authority); + oidcOptions.TokenValidationParameters.IssuerValidator = microsoftIssuerValidator.Validate; + ``` + +### Sample app code + +Inspect the sample app for the following features: + +* Automatic non-interactive token refresh with the help of a custom cookie refresher (`CookieOidcRefresher.cs`). +* The `PersistingAuthenticationStateProvider` class (`PersistingAuthenticationStateProvider.cs`) is a server-side that uses to flow the authentication state to the client, which is then fixed for the lifetime of the WebAssembly application. +* An example requests to the Blazor Web App for weather data is handled by a Minimal API endpoint (`/weatherforecast`) in the `Program` file (`Program.cs`). The endpoint requires authorization by calling . For any controllers that you add to the project, add the [`[Authorize]` attribute](xref:Microsoft.AspNetCore.Authorization.AuthorizeAttribute) to the controller or action. + +## Client-side Blazor Web App project (`BlazorWebAppOidc.Client`) + +The `BlazorWebAppOidc.Client` project is the client-side project of the Blazor Web App. + +The `PersistentAuthenticationStateProvider` class (`PersistentAuthenticationStateProvider.cs`) is a client-side that determines the user's authentication state by looking for data persisted in the page when it was rendered on the server. The authentication state is fixed for the lifetime of the WebAssembly application. + +If the user needs to log in or out, a full page reload is required. + +The sample app only provides a user name and email for display purposes. It doesn't include tokens that authenticate to the server when making subsequent requests, which works separately using a cookie that's included on requests to the server. + +:::zone-end + +:::zone pivot="with-bff-pattern" + +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. + +The following specification is covered: + +* The Blazor Web App uses [the Auto render mode with global interactivity](xref:blazor/components/render-modes). +* Custom auth state provider services are used by the server and client apps to capture the user's authentication state and flow it between the server and client. +* 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 used with Entra, Microsoft Identity Web, and hosted in Azure. +* Automatic non-interactive token refresh. +* The [Backend for Frontend (BFF) pattern](/azure/architecture/patterns/backends-for-frontends) is adopted using [.NET Aspire](/dotnet/aspire/get-started/aspire-overview) for service discovery and [YARP](https://microsoft.github.io/reverse-proxy/) for proxying requests to a weather forecast endpoint on the backend app. + * A backend web API uses JWT-bearer authentication to validate JWT tokens saved by the Blazor Web App in the sign-in cookie. + * Aspire improves the experience of building .NET cloud-native apps. It provides a consistent, opinionated set of tools and patterns for building and running distributed apps. + * YARP (Yet Another Reverse Proxy) is a library used to create a reverse proxy server. + +## Preview package warning + +> [!WARNING] +> Technologies and packages used by the `BlazorWebAppOidcBff` sample app and described in this article are in ***preview*** release at this time. The article's content, the API, and the sample app aren't supported at this time and aren't currently recommended for production use. The sample app and guidance are subject to change without notice. + +## Sample app + +The sample app consists of five projects: + +* .NET Aspire: + * `Aspire.AppHost`: Used to manage the high level orchestration concerns of the app. + * `Aspire.ServiceDefaults`: Contains default .NET Aspire app configurations that can be extended and customized as needed. +* `MinimalApiJwt`: Backend web API, containing an example [Minimal API](xref:fundamentals/minimal-apis) endpoint for weather data. +* `BlazorWebAppOidc`: Server-side project of the Blazor Web App. +* `BlazorWebAppOidc.Client`: Client-side project of the Blazor Web App. + +Access the sample apps through the latest version folder from the repository's root with the following link. The projects are in the `BlazorWebAppOidcBff` folder for .NET 8 or later. + +[View or download sample code](https://github.com/dotnet/blazor-samples) ([how to download](xref:index#how-to-download-a-sample)) + +## .NET Aspire projects + +For more information on using .NET Aspire and details on the `.AppHost` and `.ServiceDefaults` projects of the sample app, see the [.NET Aspire documentation](/dotnet/aspire/). + +Confirm that you've met the prerequisites for .NET Aspire. For more information, see the *Prerequisites* section of [Quickstart: Build your first .NET Aspire app](/dotnet/aspire/get-started/build-your-first-aspire-app?tabs=visual-studio#prerequisites). + +## Server-side Blazor Web App project (`BlazorWebAppOidc`) + +The `BlazorWebAppOidc` project is the server-side project of the Blazor Web App. The project uses [YARP](https://microsoft.github.io/reverse-proxy/) to proxy requests to a weather forecast endpoint in the backend web API project (`MinimalApiJwt`) with the `access_token` stored in the authentication cookie. + +The `BlazorWebAppOidc.http` file can be used for testing the weather data request. Note that the `BlazorWebAppOidc` project must be running to test the endpoint, and the endpoint is hardcoded into the file. For more information, see . + +### Configuration + +This section explains how to configure the sample app. + +> [!NOTE] +> For Microsoft Entra ID and Azure AD B2C, you can use 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]()), 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. + +The following configuration is found in the project's `Program` file on the call to : + +* : 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, is used as a fallback value. + + ```csharp + oidcOptions.SignInScheme = CookieAuthenticationDefaults.AuthenticationScheme; + ``` + +* Scopes for `openid` and `profile` () (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 and . + + ```csharp + oidcOptions.Scope.Add(OpenIdConnectScope.OpenIdProfile); + ``` + +* : Defines whether access and refresh tokens should be stored in the after a successful authorization. The value is set to `true` to authenticate requests for weather data from the backend web API project (`MinimalApiJwt`). + + ```csharp + oidcOptions.SaveTokens = true; + ``` + +* Scope for offline access (): The `offline_access` scope is required for the refresh token. + + ```csharp + oidcOptions.Scope.Add(OpenIdConnectScope.OfflineAccess); + ``` + +* Scopes for obtaining weather data from the web API (): The `Weather.Get` scope is configured in the Azure or Entra portal under **Expose an API**. This is necessary for backend web API project (`MinimalApiJwt`) to validate the access token with bearer JWT. + + ```csharp + oidcOptions.Scope.Add("{SCOPE}"); + ``` + + Example (`{SCOPE}`): + + ```csharp + oidcOptions.Scope.Add("https://contoso.onmicrosoft.com/4ba4de56-9cef-45d9-83fa-a4c18f9f5f0f/Weather.Get"); + ``` + + The preceding example uses: + + * Directory name: `contoso` + * Client Id: `4ba4de56-9cef-45d9-83fa-a4c18f9f5f0f` + * Scope configured for weather data from `MinimalApiJwt`: `Weather.Get` + +* and : Sets the Authority and Client ID for OIDC calls. + + ```csharp + oidcOptions.Authority = "{AUTHORITY}"; + oidcOptions.ClientId = "{CLIENT ID}"; + ``` + + Example: + + * Authority (`{AUTHORITY}`): `https://login.microsoftonline.com/a3942615-d115-4eb7-bc84-9974abcf5064/v2.0/` (uses Tenant ID `a3942615-d115-4eb7-bc84-9974abcf5064`) + * Client Id (`{CLIENT ID}`): `4ba4de56-9cef-45d9-83fa-a4c18f9f5f0f` + + ```csharp + oidcOptions.Authority = "https://login.microsoftonline.com/a3942615-d115-4eb7-bc84-9974abcf5064/v2.0/"; + oidcOptions.ClientId = "4ba4de56-9cef-45d9-83fa-a4c18f9f5f0f"; + ``` + + Example for Microsoft Azure "common" authority: + + The "common" authority should be used for multi-tenant apps. You can also use the "common" authority for single-tenant apps, but a custom is required, as shown later in this section. + + ```csharp + oidcOptions.Authority = "https://login.microsoftonline.com/common/v2.0/"; + ``` + +* : The OIDC client secret. + + **The following example is only for testing and demonstration purposes. Don't store the client secret in the app's assembly or check the secret into source control.** Store the client secret in [User Secrets](xref:security/app-secrets), [Azure Key Vault](xref:security/key-vault-configuration), or an [environment variable](xref:fundamentals/configuration/index#non-prefixed-environment-variables). + + Authentication scheme configuration is automatically read from `builder.Configuration["Authentication:Schemes:{SCHEME NAME}:{PropertyName}"]`, where the `{SCHEME NAME}` placeholder is the scheme, which is `MicrosoftOidc` by default. Because configuration is preconfigured, a client secret can automatically be read via the `Authentication:Schemes:MicrosoftOidc:ClientSecret` configuration key. On the server using environment variables, name the environment variable `Authentication__Schemes__MicrosoftOidc__ClientSecret`: + + ```dotnetcli + set Authentication__Schemes__MicrosoftOidc__ClientSecret={CLIENT SECRET} + ``` + + **For demonstration and testing only**, the can be set directly. Don't set the value directly for deployed production apps. For slightly improved security, [conditionally compile](/dotnet/csharp/language-reference/preprocessor-directives#conditional-compilation) the line with the `DEBUG` symbol: + + ```csharp + #if DEBUG + oidcOptions.ClientSecret = "{CLIENT SECRET}"; + #endif + ``` + + Example: + + Client secret (`{CLIENT SECRET}`): `463471c8c4...f90d674bc9` (shortened for display) + + ```csharp + #if DEBUG + oidcOptions.ClientSecret = "463471c8c4...137f90d674bc9"; + #endif + ``` + +* : Configures the OIDC handler to only perform authorization code flow. Implicit grants and hybrid flows are unnecessary in this mode. + + 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**. The OIDC handler automatically requests the appropriate tokens using the code returned from the authorization endpoint. + + ```csharp + oidcOptions.ResponseType = OpenIdConnectResponseType.Code; + ``` + +* and configuration of and : Many OIDC servers use "`name`" and "`role`" rather than the SOAP/WS-Fed defaults in . When 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 manually maps the name and role claims: + + ```csharp + oidcOptions.MapInboundClaims = false; + oidcOptions.TokenValidationParameters.NameClaimType = JwtRegisteredClaimNames.Name; + oidcOptions.TokenValidationParameters.RoleClaimType = "role"; + ``` + +* 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`. + + * : The request path within the app's base path where the user-agent is returned. + + In the Entra or Azure portal, set the path in the **Web** platform configuration's **Redirect URI**: + + > :::no-loc text="https://localhost/signin-oidc"::: + + > [!NOTE] + > A port isn't required for `localhost` addresses. + + * : The request path within the app's base path where the user agent is returned after sign out from the identity provider. + + In the Entra or Azure portal, set the path in the **Web** platform configuration's **Redirect URI**: + + > :::no-loc text="https://localhost/signout-callback-oidc"::: + + > [!NOTE] + > A port isn't required for `localhost` addresses. + + > [!NOTE] + > If using Microsoft Identity Web, the provider currently only redirects back to the if the `microsoftonline.com` Authority (`https://login.microsoftonline.com/{TENANT ID}/v2.0/`) is used. This limitation doesn't exist if you can use the "common" Authority with Microsoft Identity Web. For more information, see [postLogoutRedirectUri not working when authority url contains a tenant ID (`AzureAD/microsoft-authentication-library-for-js` #5783)](https://github.com/AzureAD/microsoft-authentication-library-for-js/issues/5783). + + * : Requests received on this path cause the handler to invoke sign-out using the sign-out scheme. + + In the Entra or Azure portal, set the **Front-channel logout URL**: + + > :::no-loc text="https://localhost/signout-oidc"::: + + > [!NOTE] + > A port isn't required for `localhost` addresses. + + ```csharp + oidcOptions.CallbackPath = new PathString("{PATH}"); + oidcOptions.SignedOutCallbackPath = new PathString("{PATH}"); + oidcOptions.RemoteSignOutPath = new PathString("{PATH}"); + ``` + + Examples (default values): + + ```csharp + oidcOptions.CallbackPath = new PathString("/signin-oidc"); + oidcOptions.SignedOutCallbackPath = new PathString("/signout-callback-oidc"); + oidcOptions.RemoteSignOutPath = new PathString("/signout-oidc"); + ``` + +* (*Microsoft Azure only with the "common" endpoint*) : 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). + + Only for apps using Microsoft Entra ID or Azure AD B2C with the "common" endpoint: + + ```csharp + var microsoftIssuerValidator = AadIssuerValidator.GetAadIssuerValidator(oidcOptions.Authority); + oidcOptions.TokenValidationParameters.IssuerValidator = microsoftIssuerValidator.Validate; + ``` + +### Sample app code + +Inspect the sample app for the following features: + +* Automatic non-interactive token refresh with the help of a custom cookie refresher (`CookieOidcRefresher.cs`). +* The `PersistingAuthenticationStateProvider` class (`PersistingAuthenticationStateProvider.cs`) is a server-side that uses to flow the authentication state to the client, which is then fixed for the lifetime of the WebAssembly application. +* Requests to the Blazor Web App are proxied to the backend web API project (`MinimalApiJwt`). `MapForwarder` in the `Program` file adds direct forwarding of HTTP requests that match the specified pattern to a specific destination using default configuration for the outgoing request, customized transforms, and default HTTP client. + +## Client-side Blazor Web App project (`BlazorWebAppOidc.Client`) + +The `BlazorWebAppOidc.Client` project is the client-side project of the Blazor Web App. + +The `PersistentAuthenticationStateProvider` class (`PersistentAuthenticationStateProvider.cs`) is a client-side that determines the user's authentication state by looking for data persisted in the page when it was rendered on the server. The authentication state is fixed for the lifetime of the WebAssembly application. + +If the user needs to log in or out, a full page reload is required. + +The sample app only provides a user name and email for display purposes. It doesn't include tokens that authenticate to the server when making subsequent requests, which works separately using a cookie that's included on requests to the server. + +## Backend web API project (`MinimalApiJwt`) + +The `MinimalApiJwt` project is a backend web API for multiple frontend projects. The project configures a [Minimal API](xref:fundamentals/minimal-apis) endpoint for weather data. Requests from the Blazor Web App server-side project (`BlazorWebAppOidc`) are proxied to the `MinimalApiJwt` project. + +### Configuration + +Configure the project in the of the call in the project's `Program` file: + +* : Sets the Audience for any received OpenID Connect token. + + In the Azure or Entra portal: Match the value to just the path of the **Application ID URI** configured when adding the `Weather.Get` scope under **Expose an API**: + + ```csharp + jwtOptions.Audience = "{CLIENT ID}"; + ``` + + Example: + + Client Id (`{CLIENT ID}`): `4ba4de56-9cef-45d9-83fa-a4c18f9f5f0f` + + ```csharp + jwtOptions.Audience = "4ba4de56-9cef-45d9-83fa-a4c18f9f5f0f"; + ``` + +* : Sets the Authority for making OpenID Connect calls. Match the value to the Authority configured for the OIDC handler in `BlazorWebAppOidc/Program.cs`: + + ```csharp + jwtOptions.Authority = "{AUTHORITY}"; + ``` + + Example: + + Authority (`{AUTHORITY}`): `https://login.microsoftonline.com/a3942615-d115-4eb7-bc84-9974abcf5064/v2.0/` (uses Tenant ID `a3942615-d115-4eb7-bc84-9974abcf5064`) + + ```csharp + jwtOptions.Authority = "https://login.microsoftonline.com/a3942615-d115-4eb7-bc84-9974abcf5064/v2.0/"; + ``` + +### Minimal API for weather data + +Secure weather forecast data endpoint in the project's `Program` file: + +```csharp +app.MapGet("/weatherforecast", () => +{ + var forecast = Enumerable.Range(1, 5).Select(index => + new WeatherForecast + ( + DateOnly.FromDateTime(DateTime.Now.AddDays(index)), + Random.Shared.Next(-20, 55), + summaries[Random.Shared.Next(summaries.Length)] + )) + .ToArray(); + return forecast; +}).RequireAuthorization(); +``` + +The extension method requires authorization for the route definition. For any controllers that you add to the project, add the [`[Authorize]` attribute](xref:Microsoft.AspNetCore.Authorization.AuthorizeAttribute) to the controller or action. + +:::zone-end + +## Additional resources + +* [`AzureAD/microsoft-identity-web` GitHub repository](https://github.com/AzureAD/microsoft-identity-web/wiki): Helpful guidance on implementing Microsoft Identity Web for Microsoft Entra ID and Azure Active Directory B2C for ASP.NET Core apps, including links to sample apps and related Azure documentation. Currently, Blazor Web Apps aren't explicitly addressed by the Azure documentation, but the setup and configuration of a Blazor Web App for ME-ID and Azure hosting is the same as it is for any ASP.NET Core web app. +* [`AuthenticationStateProvider` service](xref:blazor/security/index#authenticationstateprovider-service) +* [Manage authentication state in Blazor Web Apps](xref:blazor/security/server/index#manage-authentication-state-in-blazor-web-apps) diff --git a/aspnetcore/blazor/security/webassembly/index.md b/aspnetcore/blazor/security/webassembly/index.md index 26b13a71769d..138021decfca 100644 --- a/aspnetcore/blazor/security/webassembly/index.md +++ b/aspnetcore/blazor/security/webassembly/index.md @@ -12,7 +12,7 @@ uid: blazor/security/webassembly/index [!INCLUDE[](~/includes/not-latest-version.md)] -Blazor WebAssembly apps are secured in the same manner as single-page applications (SPAs). There are several approaches for authenticating users to SPAs, but the most common and comprehensive approach is to use an implementation based on the [OAuth 2.0 protocol](https://oauth.net/), such as [OpenID Connect (OIDC)](https://openid.net/connect/). +Blazor WebAssembly apps are secured in the same manner as single-page applications (SPAs). There are several approaches for authenticating users to SPAs, but the most common and comprehensive approach is to use an implementation based on the [OAuth 2.0 protocol](https://oauth.net/), such as [OpenID Connect (OIDC)](https://openid.net/developers/how-connect-works/). The Blazor WebAssembly security documentation primarily focuses on how to accomplish user authentication and authorization tasks. For OAuth 2.0/OIDC general concept coverage, see the resources in the [main overview article's *Additional resources* section](xref:blazor/security/index#additional-resources). diff --git a/aspnetcore/blazor/security/webassembly/standalone-with-authentication-library.md b/aspnetcore/blazor/security/webassembly/standalone-with-authentication-library.md index f0052cdabc64..1fb395e23c10 100644 --- a/aspnetcore/blazor/security/webassembly/standalone-with-authentication-library.md +++ b/aspnetcore/blazor/security/webassembly/standalone-with-authentication-library.md @@ -30,7 +30,7 @@ The subsections of the walkthrough explain how to: ### Register an app -Register an app with an [OpenID Connect (OIDC)](https://openid.net/connect/) Identity Provider (IP) following the guidance provided by the maintainer of the IP. +Register an app with an [OpenID Connect (OIDC)](https://openid.net/developers/how-connect-works/) Identity Provider (IP) following the guidance provided by the maintainer of the IP. Record the following information: diff --git a/aspnetcore/blazor/security/webassembly/standalone-with-identity.md b/aspnetcore/blazor/security/webassembly/standalone-with-identity.md index b0eb96eb1a61..a8da300b9670 100644 --- a/aspnetcore/blazor/security/webassembly/standalone-with-identity.md +++ b/aspnetcore/blazor/security/webassembly/standalone-with-identity.md @@ -100,7 +100,7 @@ Access the sample apps through the latest version folder from the repository's r [View or download sample code](https://github.com/dotnet/blazor-samples) ([how to download](xref:index#how-to-download-a-sample)) -## Backend web API app +## Backend web API app packages and code The backend web API app maintains a user identity store for ASP.NET Core Identity. @@ -149,7 +149,7 @@ To secure an endpoint, add the instance, see [DbContext Lifetime, Configuration, and Initialization](/ef/core/dbcontext-configuration/) in the EF Core documentation. -## Frontend standalone Blazor WebAssembly app +## Frontend standalone Blazor WebAssembly app packages and code A standalone Blazor WebAssembly frontend app demonstrates user authentication and authorization to access a private webpage. diff --git a/aspnetcore/blazor/state-management.md b/aspnetcore/blazor/state-management.md index 7a2165c61752..66edb2a6a0c8 100644 --- a/aspnetcore/blazor/state-management.md +++ b/aspnetcore/blazor/state-management.md @@ -612,7 +612,7 @@ For permanent data persistence that spans multiple users and devices, the app ca After data is saved, the user's state is retained and available in any new browser session. -Because Blazor WebAssembly apps run entirely in the user's browser, they require additional measures to access secure external systems, such as storage services and databases. Blazor WebAssembly apps are secured in the same manner as single-page applications (SPAs). Typically, an app authenticates a user via [OAuth](https://oauth.net)/[OpenID Connect (OIDC)](https://openid.net/connect/) and then interacts with storage services and databases through web API calls to a server-side app. The server-side app mediates the transfer of data between the Blazor WebAssembly app and the storage service or database. The Blazor WebAssembly app maintains an ephemeral connection to the server-side app, while the server-side app has a persistent connection to storage. +Because Blazor WebAssembly apps run entirely in the user's browser, they require additional measures to access secure external systems, such as storage services and databases. Blazor WebAssembly apps are secured in the same manner as single-page applications (SPAs). Typically, an app authenticates a user via [OAuth](https://oauth.net)/[OpenID Connect (OIDC)](https://openid.net/developers/how-connect-works/) and then interacts with storage services and databases through web API calls to a server-side app. The server-side app mediates the transfer of data between the Blazor WebAssembly app and the storage service or database. The Blazor WebAssembly app maintains an ephemeral connection to the server-side app, while the server-side app has a persistent connection to storage. For more information, see the following resources: diff --git a/aspnetcore/fundamentals/minimal-apis/security.md b/aspnetcore/fundamentals/minimal-apis/security.md index 4dc6b97c2499..481cdad780b3 100644 --- a/aspnetcore/fundamentals/minimal-apis/security.md +++ b/aspnetcore/fundamentals/minimal-apis/security.md @@ -49,7 +49,7 @@ In some cases, such as controlling middleware order, it's necessary to explicitl Authentication strategies typically support a variety of configurations that are loaded via options. Minimal apps support loading options from configuration for the following authentication strategies: - [JWT bearer-based](https://jwt.io/introduction) -- [OpenID Connection-based](https://openid.net/connect/) +- [OpenID Connection-based](https://openid.net/developers/how-connect-works/) The ASP.NET Core framework expects to find these options under the `Authentication:Schemes:{SchemeName}` section in [configuration](/aspnet/core/fundamentals/configuration). In the following sample, two different schemes, `Bearer` and `LocalAuthIssuer`, are defined with their respective options. The `Authentication:DefaultScheme` option can be used to configure the default authentication strategy that's used. diff --git a/aspnetcore/security/authentication/claims.md b/aspnetcore/security/authentication/claims.md index 202d86db2d1d..23e8d3caacb6 100644 --- a/aspnetcore/security/authentication/claims.md +++ b/aspnetcore/security/authentication/claims.md @@ -17,7 +17,7 @@ By [Damien Bowden](https://github.com/damienbod) Claims can be created from any user or identity data which can be issued using a trusted identity provider or ASP.NET Core identity. A claim is a name value pair that represents what the subject is, not what the subject can do. This article covers the following areas: -* How to configure and map claims using an [OpenID Connect](https://openid.net/connect/) client +* How to configure and map claims using an [OpenID Connect](https://openid.net/developers/how-connect-works/) client * Set the name and role claim * Reset the claims namespaces * Customize, extend the claims using @@ -100,7 +100,7 @@ Refer to the following document: Claims can be created from any user or identity data which can be issued using a trusted identity provider or ASP.NET Core identity. A claim is a name value pair that represents what the subject is, not what the subject can do. This article covers the following areas: -* How to configure and map claims using an [OpenID Connect](https://openid.net/connect/) client +* How to configure and map claims using an [OpenID Connect](https://openid.net/developers/how-connect-works/) client * Set the name and role claim * Reset the claims namespaces * Customize, extend the claims using diff --git a/aspnetcore/security/authentication/index.md b/aspnetcore/security/authentication/index.md index ed640dd3b3c4..98d34e68b4c4 100644 --- a/aspnetcore/security/authentication/index.md +++ b/aspnetcore/security/authentication/index.md @@ -96,7 +96,7 @@ Based on the authentication scheme's configuration and the incoming request cont ### `RemoteAuthenticationHandler` vs `AuthenticationHandler` - is the class for authentication that requires a remote authentication step. When the remote authentication step is finished, the handler calls back to the `CallbackPath` set by the handler. The handler finishes the authentication step using the information passed to the callback path. [OAuth 2.0](https://oauth.net/2/) and [OIDC](https://openid.net/connect/) both use this pattern. JWT and cookies don't since they can directly use the bearer header and cookie to authenticate. The remotely hosted provider in this case: + is the class for authentication that requires a remote authentication step. When the remote authentication step is finished, the handler calls back to the `CallbackPath` set by the handler. The handler finishes the authentication step using the information passed to the callback path. [OAuth 2.0](https://oauth.net/2/) and [OIDC](https://openid.net/developers/how-connect-works/) both use this pattern. JWT and cookies don't since they can directly use the bearer header and cookie to authenticate. The remotely hosted provider in this case: * Is the authentication provider. * Examples include [Facebook](xref:security/authentication/facebook-logins), [Twitter](xref:security/authentication/twitter-logins), [Google](xref:security/authentication/google-logins), [Microsoft](xref:security/authentication/microsoft-logins), and any other OIDC provider that handles authenticating users using the handlers mechanism. @@ -245,7 +245,7 @@ Based on the authentication scheme's configuration and the incoming request cont ### `RemoteAuthenticationHandler` vs `AuthenticationHandler` - is the class for authentication that requires a remote authentication step. When the remote authentication step is finished, the handler calls back to the `CallbackPath` set by the handler. The handler finishes the authentication step using the information passed to the callback path. [OAuth 2.0](https://oauth.net/2/) and [OIDC](https://openid.net/connect/) both use this pattern. JWT and cookies don't since they can directly use the bearer header and cookie to authenticate. The remotely hosted provider in this case: + is the class for authentication that requires a remote authentication step. When the remote authentication step is finished, the handler calls back to the `CallbackPath` set by the handler. The handler finishes the authentication step using the information passed to the callback path. [OAuth 2.0](https://oauth.net/2/) and [OIDC](https://openid.net/developers/how-connect-works/) both use this pattern. JWT and cookies don't since they can directly use the bearer header and cookie to authenticate. The remotely hosted provider in this case: * Is the authentication provider. * Examples include [Facebook](xref:security/authentication/facebook-logins), [Twitter](xref:security/authentication/twitter-logins), [Google](xref:security/authentication/google-logins), [Microsoft](xref:security/authentication/microsoft-logins), and any other OIDC provider that handles authenticating users using the handlers mechanism. @@ -388,7 +388,7 @@ Based on the authentication scheme's configuration and the incoming request cont ### `RemoteAuthenticationHandler` vs `AuthenticationHandler` - is the class for authentication that requires a remote authentication step. When the remote authentication step is finished, the handler calls back to the `CallbackPath` set by the handler. The handler finishes the authentication step using the information passed to the callback path. [OAuth 2.0](https://oauth.net/2/) and [OIDC](https://openid.net/connect/) both use this pattern. JWT and cookies don't since they can directly use the bearer header and cookie to authenticate. The remotely hosted provider in this case: + is the class for authentication that requires a remote authentication step. When the remote authentication step is finished, the handler calls back to the `CallbackPath` set by the handler. The handler finishes the authentication step using the information passed to the callback path. [OAuth 2.0](https://oauth.net/2/) and [OIDC](https://openid.net/developers/how-connect-works/) both use this pattern. JWT and cookies don't since they can directly use the bearer header and cookie to authenticate. The remotely hosted provider in this case: * Is the authentication provider. * Examples include [Facebook](xref:security/authentication/facebook-logins), [Twitter](xref:security/authentication/twitter-logins), [Google](xref:security/authentication/google-logins), [Microsoft](xref:security/authentication/microsoft-logins), and any other OIDC provider that handles authenticating users using the handlers mechanism. diff --git a/aspnetcore/security/samesite.md b/aspnetcore/security/samesite.md index c1f3f6a68e49..ebc45ce873e7 100644 --- a/aspnetcore/security/samesite.md +++ b/aspnetcore/security/samesite.md @@ -22,7 +22,7 @@ SameSite is an [IETF](https://ietf.org/about/) draft standard designed to provid * Applications that use [`