Compartir vía


Protección de una Blazor Web App de ASP.NET Core con OpenID Connect (OIDC)

En este artículo se describe cómo proteger un objeto Blazor Web App con OpenID Connect (OIDC) mediante una aplicación de ejemplo en el repositorio de GitHub dotnet/blazor-samples (.NET 8 o posterior) (cómo descargar).

En esta versión del artículo se describe la implementación de OIDC sin adoptar el patrón BFF (Backend for Frontend). El patrón BFF es útil para realizar solicitudes autenticadas a servicios externos. Cambia el selector de versión del artículo a OIDC con el patrón BFF si la especificación de la aplicación llama a adoptar el patrón BFF.

Se trata la especificación siguiente:

  • La Blazor Web App usa el modo de representación automática con interactividad global.
  • Las aplicaciones cliente y el servidor usan los servicios de proveedor de estado de autenticación personalizados para capturar el estado de autenticación del usuario y hacer que fluya entre el servidor y el cliente.
  • Esta aplicación es un punto de partida para cualquier flujo de autenticación de OIDC. OIDC se configura manualmente en la aplicación y no se basa en los paquetes Microsoft Entra ID ni Microsoft Identity Web, y la aplicación de ejemplo tampoco requiere el hospedaje de Microsoft Azure. Sin embargo, la aplicación de ejemplo puede usarse con Entra y Microsoft Identity Web, y hospedarse en Azure.
  • Actualización automática de token no interactivo.
  • Llama de forma segura a una API (web) en el proyecto de servidor para obtener datos.

Aplicación de ejemplo

El ejemplo consta de dos proyectos:

  • BlazorWebAppOidc: proyecto del lado servidor de la Blazor Web App, que contiene un punto de conexión de API mínima de ejemplo para datos meteorológicos.
  • BlazorWebAppOidc.Client: proyecto del lado cliente de la Blazor Web App.

Accede a las aplicaciones de ejemplo a través de la carpeta de versión más reciente desde la raíz del repositorio con el vínculo siguiente. Los proyectos se encuentran en la carpeta BlazorWebAppOidc de .NET 8 o posterior.

Vea o descargue el código de ejemplo (cómo descargarlo)

Proyecto de Blazor Web App del lado servidor (BlazorWebAppOidc)

El proyecto BlazorWebAppOidc es el proyecto del lado servidor de la Blazor Web App.

El archivo BlazorWebAppOidc.http se puede usar para probar la solicitud de datos meteorológicos. Ten en cuenta que el proyecto BlazorWebAppOidc debe ejecutarse para probar el punto de conexión y el punto de conexión está codificado de forma dura en el archivo. Para obtener más información, consulta Usar archivos .http en Visual Studio 2022.

Nota:

El proyecto de servidor usa IHttpContextAccessor/HttpContext, pero nunca para componentes representados de forma interactiva. Para obtener más información, consulta Guía de mitigación de amenazas para Blazor la representación interactiva en el lado del servidor de ASP.NET Core.

Configuración

En esta sección se explica cómo configurar la aplicación de ejemplo.

Nota:

En el caso de Microsoft Entra ID o Azure AD B2C, puede usar AddMicrosoftIdentityWebApp desde Microsoft Identity Web (Microsoft.Identity.Web paquete NuGet, documentación de API), que agrega los controladores de autenticación y Cookie OIDC con los valores predeterminados adecuados. La aplicación de ejemplo y las instrucciones de esta sección no usan Microsoft Identity Web. En la guía se muestra cómo configurar el controlador OIDC manualmente para cualquier proveedor de OIDC. Para obtener más información sobre la implementación de Microsoft Identity Web, consulta los recursos vinculados.

Establecimiento del secreto de cliente

Advertencia

No almacene secretos de aplicación, cadena de conexión s, credenciales, contraseñas, números de identificación personal (PIN), código C#/.NET privado o claves o tokens privados en el código del lado cliente, lo que siempre es inseguro. En entornos de prueba/ensayo y producción, el código del lado Blazor servidor y las API web deben usar flujos de autenticación seguros que eviten mantener las credenciales dentro del código del proyecto o los archivos de configuración. Fuera de las pruebas de desarrollo local, se recomienda evitar el uso de variables de entorno para almacenar datos confidenciales, ya que las variables de entorno no son el enfoque más seguro. Para las pruebas de desarrollo local, se recomienda la herramienta Secret Manager para proteger los datos confidenciales. Para obtener más información, consulte Mantener de forma segura los datos confidenciales y las credenciales.

Para las pruebas de desarrollo local, use la herramienta Administrador de secretos para almacenar el secreto de cliente de la aplicación de servidor en la clave Authentication:Schemes:MicrosoftOidc:ClientSecretde configuración .

Nota:

Si la aplicación usa Microsoft Entra ID o Azure AD B2C, cree un secreto de cliente en el registro de la aplicación en Entra o Azure Portal (Administrar>certificados y secretos>Nuevo secreto de cliente). Use el valor del nuevo secreto en las instrucciones siguientes.

La aplicación de ejemplo no se ha inicializado para la herramienta Administrador de secretos. Use un shell de comandos, como el shell de comandos de PowerShell para desarrolladores en Visual Studio, para ejecutar el comando siguiente. Antes de ejecutar el comando, cambie el directorio con el cd comando al directorio del proyecto de servidor. El comando establece un identificador de secretos de usuario (<UserSecretsId> en el archivo de proyecto de la aplicación de servidor):

dotnet user-secrets init

Ejecute el siguiente comando para establecer el secreto de cliente. El {SECRET} marcador de posición es el secreto de cliente obtenido del registro de la aplicación:

dotnet user-secrets set "Authentication:Schemes:MicrosoftOidc:ClientSecret" "{SECRET}"

Si usa Visual Studio, puede confirmar que el secreto está establecido haciendo clic con el botón derecho en el proyecto de servidor en Explorador de soluciones y seleccionando Administrar secretos de usuario.

Configuración de la aplicación

La siguiente configuración OpenIdConnectOptions se encuentra en el archivo del proyecto Program en la llamada a AddOpenIdConnect:

  • SignInScheme: establece el esquema de autenticación correspondiente al middleware responsable de conservar identity del usuario después de una autenticación correcta. El controlador OIDC debe usar un esquema de inicio de sesión que sea capaz de conservar las credenciales de usuario entre las solicitudes. La siguiente línea se presenta simplemente con fines de demostración. Si se omite, DefaultSignInScheme se usa como valor de reserva.

    oidcOptions.SignInScheme = CookieAuthenticationDefaults.AuthenticationScheme;
    
  • Ámbitos para openid y profile (Scope) (opcional): los ámbitos openid y profile también se configuran de forma predeterminada porque son necesarios para que el controlador OIDC funcione, pero es posible que deban volver a agregarse si los ámbitos se incluyen en la configuración de Authentication:Schemes:MicrosoftOidc:Scope. Para obtener instrucciones generales de configuración, consulta Configuración en ASP.NET Core y Configuración de Blazor de ASP.NET Core.

    oidcOptions.Scope.Add(OpenIdConnectScope.OpenIdProfile);
    
  • SaveTokens: define si los tokens de acceso y actualización deben almacenarse en AuthenticationProperties después de una autorización correcta. Esta propiedad se establece en false para reducir el tamaño de cookie de autenticación final.

    oidcOptions.SaveTokens = false;
    
  • Ámbito para el acceso sin conexión (Scope): el ámbito offline_access es necesario para el token de actualización.

    oidcOptions.Scope.Add(OpenIdConnectScope.OfflineAccess);
    
  • Authority y ClientId: establece la autoridad y el id. de cliente para las llamadas de OIDC.

    oidcOptions.Authority = "{AUTHORITY}";
    oidcOptions.ClientId = "{CLIENT ID}";
    

    Ejemplo:

    • Autoridad ({AUTHORITY}): https://login.microsoftonline.com/aaaabbbb-0000-cccc-1111-dddd2222eeee/v2.0/ (usa el id. de inquilino aaaabbbb-0000-cccc-1111-dddd2222eeee)
    • Id. de cliente ({CLIENT ID}): 00001111-aaaa-2222-bbbb-3333cccc4444
    oidcOptions.Authority = "https://login.microsoftonline.com/aaaabbbb-0000-cccc-1111-dddd2222eeee/v2.0/";
    oidcOptions.ClientId = "00001111-aaaa-2222-bbbb-3333cccc4444";
    

    Ejemplo de autoridad "común" de Microsoft Azure:

    La entidad "común" debe usarse para aplicaciones multiinquilino. También puedes usar la entidad "común" para las aplicaciones de un solo inquilino, pero se requiere un IssuerValidator personalizado, como se muestra más adelante en esta sección.

    oidcOptions.Authority = "https://login.microsoftonline.com/common/v2.0/";
    
  • ResponseType: configura el controlador OIDC para que solo realice el flujo de código de autorización. Las concesiones implícitas y los flujos híbridos no son necesarios en este modo.

    En la configuración de registro de aplicaciones de flujos híbridos y concesión implícita de Entra o Azure Portal, no actives ninguna casilla de selección para que el punto de conexión de autorización devuelva Tokens de acceso o Tokens de id.. El controlador OIDC solicita automáticamente los tokens adecuados mediante el código que devuelve el punto de conexión de autorización.

    oidcOptions.ResponseType = OpenIdConnectResponseType.Code;
    
  • MapInboundClaims y configuración de NameClaimType y RoleClaimType: muchos servidores OIDC usan "name" y "role" en lugar de los valores predeterminados de SOAP/WS-Fed en ClaimTypes. Cuando MapInboundClaims se establece en false, el controlador no realiza asignaciones de notificaciones y la aplicación usa directamente los nombres de notificación del JWT. En el ejemplo siguiente, se establece el tipo de notificación de rol en "roles", que es adecuado para Microsoft Entra ID (ME-ID). Para más información, consulta la documentación de tu proveedor de identity.

    Nota:

    MapInboundClaims debe establecerse en false para la mayoría de los proveedores de OIDC, lo que impide cambiar el nombre de las notificaciones.

    oidcOptions.MapInboundClaims = false;
    oidcOptions.TokenValidationParameters.NameClaimType = "name";
    oidcOptions.TokenValidationParameters.RoleClaimType = "roles";
    
  • Configuración de rutas: las rutas deben coincidir con las rutas del URI de redirección (ruta de devolución de llamada de inicio de sesión) y la redirección posterior al cierre de sesión (ruta de devolución de llamada de cierre de sesión) configuradas al registrar la aplicación con el proveedor OIDC. En Azure Portal, las rutas se configuran en la hoja Autenticación del registro de la aplicación. Las rutas de inicio de sesión y cierre de sesión deben registrarse como URI de redirección. Los valores predeterminados son /signin-oidc y /signout-callback-oidc.

    • CallbackPath: ruta de solicitud en la ruta base de la aplicación en la que se devolverá el agente de usuario.

      En Entra o Azure Portal, establece la ruta en el URI de redirección de la configuración de la plataforma web:

      https://localhost/signin-oidc

      Nota:

      No se requiere un puerto para las direcciones localhost cuando se usa Microsoft Entra ID. La mayoría de los otros proveedores de OIDC requieren un puerto correcto.

    • SignedOutCallbackPath: ruta de solicitud en la ruta base de la aplicación en la que se devolverá el agente de usuario después de cerrar la sesión del proveedor de identity.

      En Entra o Azure Portal, establece la ruta en el URI de redirección de la configuración de la plataforma web:

      https://localhost/signout-callback-oidc

      Nota:

      No se requiere un puerto para las direcciones localhost cuando se usa Microsoft Entra ID. La mayoría de los otros proveedores de OIDC requieren un puerto correcto.

      Nota:

      Si usas Microsoft Identity Web, actualmente el proveedor solo redirige de nuevo a SignedOutCallbackPath si se usa la autoridad de microsoftonline.com (https://login.microsoftonline.com/{TENANT ID}/v2.0/). Esta limitación no existe si puedes usar la autoridad "común" con Microsoft Identity Web. Para obtener más información, consulta postLogoutRedirectUri no funciona cuando la dirección URL de autoridad contiene un id. de inquilino (AzureAD/microsoft-authentication-library-for-js #5783).

    • RemoteSignOutPath: las solicitudes recibidas en esta ruta hacen que el controlador invoque el cierre de sesión mediante el esquema de cierre de sesión.

      En Entra o Azure Portal, establece la URL de cierre de sesión de Front-Channel:

      https://localhost/signout-oidc

      Nota:

      No se requiere un puerto para las direcciones localhost cuando se usa Microsoft Entra ID. La mayoría de los otros proveedores de OIDC requieren un puerto correcto.

      oidcOptions.CallbackPath = new PathString("{PATH}");
      oidcOptions.SignedOutCallbackPath = new PathString("{PATH}");
      oidcOptions.RemoteSignOutPath = new PathString("{PATH}");
      

      Ejemplos (valores predeterminados):

      oidcOptions.CallbackPath = new PathString("/signin-oidc");
      oidcOptions.SignedOutCallbackPath = new PathString("/signout-callback-oidc");
      oidcOptions.RemoteSignOutPath = new PathString("/signout-oidc");
      
  • (Microsoft Azure solo con el punto de conexión "común") TokenValidationParameters.IssuerValidator: muchos proveedores de OIDC funcionan con el validador de emisor predeterminado, pero es necesario tener en cuenta el emisor parametrizado con el id. de inquilino ({TENANT ID}) devuelto por https://login.microsoftonline.com/common/v2.0/.well-known/openid-configuration. Para obtener más información, consulta SecurityTokenInvalidIssuerException con OpenID Connect y el punto de conexión "común" de Azure AD (AzureAD/azure-activedirectory-identitymodel-extensions-for-dotnet #1731).

    Solo para las aplicaciones que usan Microsoft Entra ID o Azure AD B2C con el punto de conexión "común":

    var microsoftIssuerValidator = AadIssuerValidator.GetAadIssuerValidator(oidcOptions.Authority);
    oidcOptions.TokenValidationParameters.IssuerValidator = microsoftIssuerValidator.Validate;
    

Código de aplicación de ejemplo

Inspecciona la aplicación de ejemplo para ver las siguientes características:

  • Actualización automática de tokens no interactivos con la ayuda de un actualizador personalizado cookie (CookieOidcRefresher.cs).
  • El proyecto de servidor llama AddAuthenticationStateSerialization a para agregar un proveedor de estado de autenticación del lado servidor que usa PersistentComponentState para fluir el estado de autenticación al cliente. El cliente llama AddAuthenticationStateDeserialization a para deserializar y usar el estado de autenticación pasado por el servidor. El estado de autenticación es fijo durante la vigencia de la aplicación WebAssembly.
  • Un ejemplo de solicitudes a la Blazor Web App para datos meteorológicos se controla mediante un punto de conexión de API mínima (/weather-forecast) en el archivo Program (Program.cs). El punto de conexión requiere autorización llamando a RequireAuthorization. Para los controladores que agregues al proyecto, agrega el atributo [Authorize] al controlador o la acción.
  • La aplicación llama de forma segura a una API (web) en el proyecto de servidor para obtener los datos meteorológicos:
    • Al representar el componente Weather en el servidor, el componente usa el ServerWeatherForecaster en el servidor para obtener los datos meteorológicos directamente (no a través de una llamada API web).
    • Cuando el componente se representa en el cliente, usa la implementación del servicio ClientWeatherForecaster, que usa un HttpClient preconfigurado (en el archivo Program del proyecto de cliente) para realizar una llamada API web al proyecto de servidor. Un punto de conexión de API mínimo (/weather-forecast) definido en el archivo Program del proyecto de servidor obtiene los datos meteorológicos de la ServerWeatherForecaster y devuelve los datos al cliente.
  • Actualización automática de tokens no interactivos con la ayuda de un actualizador personalizado cookie (CookieOidcRefresher.cs).
  • La clase PersistingAuthenticationStateProvider (PersistingAuthenticationStateProvider.cs) es un AuthenticationStateProvider del lado servidor que usa PersistentComponentState para hacer fluir el estado de autenticación, que posteriormente es fijo durante la vigencia de la aplicación WebAssembly, al cliente.
  • Un ejemplo de solicitudes a la Blazor Web App para datos meteorológicos se controla mediante un punto de conexión de API mínima (/weather-forecast) en el archivo Program (Program.cs). El punto de conexión requiere autorización llamando a RequireAuthorization. Para los controladores que agregues al proyecto, agrega el atributo [Authorize] al controlador o la acción.
  • La aplicación llama de forma segura a una API (web) en el proyecto de servidor para obtener los datos meteorológicos:
    • Al representar el componente Weather en el servidor, el componente usa el ServerWeatherForecaster en el servidor para obtener los datos meteorológicos directamente (no a través de una llamada API web).
    • Cuando el componente se representa en el cliente, usa la implementación del servicio ClientWeatherForecaster, que usa un HttpClient preconfigurado (en el archivo Program del proyecto de cliente) para realizar una llamada API web al proyecto de servidor. Un punto de conexión de API mínimo (/weather-forecast) definido en el archivo Program del proyecto de servidor obtiene los datos meteorológicos de la ServerWeatherForecaster y devuelve los datos al cliente.

Para obtener más información sobre las llamadas API (web) mediante abstracciones de servicio en Blazor Web App, consulta Llamada a una API web desde una aplicación Blazor de ASP.NET Core.

Proyecto de Blazor Web App del lado cliente (BlazorWebAppOidc.Client)

El proyecto BlazorWebAppOidc.Client es el proyecto del lado cliente de Blazor Web App.

El cliente llama AddAuthenticationStateDeserialization a para deserializar y usar el estado de autenticación pasado por el servidor. El estado de autenticación es fijo durante la vigencia de la aplicación WebAssembly.

La clase PersistentAuthenticationStateProvider (PersistentAuthenticationStateProvider.cs) es un AuthenticationStateProvider del lado cliente que determina el estado de autenticación del usuario buscando datos guardados en la página cuando se representa en el servidor. El estado de autenticación es fijo durante la vigencia de la aplicación WebAssembly.

Si el usuario necesita iniciar sesión o cerrar sesión, se requiere una recarga de página completa.

La aplicación de ejemplo solo proporciona un nombre de usuario y un correo electrónico con fines de demostración. No incluye tokens que se autentican en el servidor al realizar solicitudes posteriores, que funcionan por separado mediante un cookie que se incluye en solicitudes HttpClient al servidor.

En esta versión del artículo se describe la implementación de OIDC con el patrón BFF (Backend for Frontend). Cambia el selector de versión del artículo a OIDC sin el patrón BFF si la especificación de la aplicación no llama a adoptar el patrón BFF.

Se trata la especificación siguiente:

  • La Blazor Web App usa el modo de representación automática con interactividad global.
  • Las aplicaciones cliente y el servidor usan los servicios de proveedor de estado de autenticación personalizados para capturar el estado de autenticación del usuario y hacer que fluya entre el servidor y el cliente.
  • Esta aplicación es un punto de partida para cualquier flujo de autenticación de OIDC. OIDC se configura manualmente en la aplicación y no se basa en los paquetes Microsoft Entra ID ni Microsoft Identity Web, y la aplicación de ejemplo tampoco requiere el hospedaje de Microsoft Azure. Sin embargo, la aplicación de ejemplo puede usarse con Entra y Microsoft Identity Web, y hospedarse en Azure.
  • Actualización automática de token no interactivo.
  • El patrón Backend for Frontend (BFF) se adopta mediante .NET Aspire para la detección de servicios y YARP para redirigir mediante proxy solicitudes a un punto de conexión de previsión meteorológica en la aplicación back-end.
    • Una API web de back-end usa la autenticación de portador JWT para validar los tokens JWT guardados por la Blazor Web App en la cookie de inicio de sesión.
    • Aspire mejora la experiencia de creación de aplicaciones nativas de nube .NET. Proporciona un conjunto coherente y fundamentado de herramientas y patrones para compilar y ejecutar aplicaciones distribuidas.
    • YARP es una biblioteca que se usa para crear un servidor proxy inverso.

Para obtener más información sobre .NET Aspire, consulta Disponibilidad general de .NET Aspire: simplificación del desarrollo nativo de nube de .NET (mayo de 2024).

Requisito previo

.NET Aspire requiere Visual Studio versión 17.10 o versión posterior.

Aplicación de ejemplo

El ejemplo consta de cinco proyectos:

  • .NET Aspire:
    • Aspire.AppHost: se usa para administrar los problemas de orquestación de nivel general de la aplicación.
    • Aspire.ServiceDefaults: contiene configuraciones de aplicaciones .NET Aspire predeterminadas que se pueden ampliar y personalizar según sea necesario.
  • MinimalApiJwt: API web de back-end, que contiene un punto de conexión de API mínima de ejemplo para los datos meteorológicos.
  • BlazorWebAppOidc: proyecto del lado servidor de la Blazor Web App.
  • BlazorWebAppOidc.Client: proyecto del lado cliente de la Blazor Web App.

Accede a las aplicaciones de ejemplo a través de la carpeta de versión más reciente desde la raíz del repositorio con el vínculo siguiente. Los proyectos se encuentran en la carpeta BlazorWebAppOidcBff de .NET 8 o posterior.

Consulta o descarga el código de ejemplo (cómo descargarlo)

Proyectos .NET Aspire

Para obtener más información sobre el uso de .NET Aspire y detalles sobre los proyectos .AppHost y .ServiceDefaults de la aplicación de ejemplo, consulta la documentación de .NET Aspire.

Confirma que cumples los requisitos previos para .NET Aspire. Para obtener más información, consulta la sección Requisitos previos de Inicio rápido: Compilación de la primera aplicación .NET Aspire.

La aplicación de muestra solo configura un perfil de inicio HTTP no seguro (http) para usarlo durante las pruebas de desarrollo. Para obtener más información, incluido un ejemplo de perfiles de configuración de inicio seguro y no seguro, consulta Permitir el transporte no seguro en .NET Aspire (documentación de .NET Aspire).

Proyecto de Blazor Web App del lado servidor (BlazorWebAppOidc)

El proyecto BlazorWebAppOidc es el proyecto del lado servidor de la Blazor Web App. El proyecto usa YARP para redirigir mediante proxy las solicitudes a un punto de conexión de previsión meteorológica en el proyecto de API web de back-end (MinimalApiJwt) con access_token almacenado en la cookie de autenticación.

El archivo BlazorWebAppOidc.http se puede usar para probar la solicitud de datos meteorológicos. Ten en cuenta que el proyecto BlazorWebAppOidc debe ejecutarse para probar el punto de conexión y el punto de conexión está codificado de forma dura en el archivo. Para obtener más información, consulta Usar archivos .http en Visual Studio 2022.

Nota:

El proyecto de servidor usa IHttpContextAccessor/HttpContext, pero nunca para componentes representados de forma interactiva. Para obtener más información, consulta Guía de mitigación de amenazas para Blazor la representación interactiva en el lado del servidor de ASP.NET Core.

Configuración

En esta sección se explica cómo configurar la aplicación de ejemplo.

Nota:

En el caso de Microsoft Entra ID o Azure AD B2C, puede usar AddMicrosoftIdentityWebApp desde Microsoft Identity Web (Microsoft.Identity.Web paquete NuGet, documentación de API), que agrega los controladores de autenticación y Cookie OIDC con los valores predeterminados adecuados. La aplicación de ejemplo y las instrucciones de esta sección no usan Microsoft Identity Web. En la guía se muestra cómo configurar el controlador OIDC manualmente para cualquier proveedor de OIDC. Para obtener más información sobre la implementación de Microsoft Identity Web, consulta los recursos vinculados.

Establecimiento del secreto de cliente

Advertencia

No almacene secretos de aplicación, cadena de conexión s, credenciales, contraseñas, números de identificación personal (PIN), código C#/.NET privado o claves o tokens privados en el código del lado cliente, lo que siempre es inseguro. En entornos de prueba/ensayo y producción, el código del lado Blazor servidor y las API web deben usar flujos de autenticación seguros que eviten mantener las credenciales dentro del código del proyecto o los archivos de configuración. Fuera de las pruebas de desarrollo local, se recomienda evitar el uso de variables de entorno para almacenar datos confidenciales, ya que las variables de entorno no son el enfoque más seguro. Para las pruebas de desarrollo local, se recomienda la herramienta Secret Manager para proteger los datos confidenciales. Para obtener más información, consulte Mantener de forma segura los datos confidenciales y las credenciales.

Para las pruebas de desarrollo local, use la herramienta Administrador de secretos para almacenar el secreto de cliente de la aplicación de servidor en la clave Authentication:Schemes:MicrosoftOidc:ClientSecretde configuración .

Nota:

Si la aplicación usa Microsoft Entra ID o Azure AD B2C, cree un secreto de cliente en el registro de la aplicación en Entra o Azure Portal (Administrar>certificados y secretos>Nuevo secreto de cliente). Use el valor del nuevo secreto en las instrucciones siguientes.

La aplicación de ejemplo no se ha inicializado para la herramienta Administrador de secretos. Use un shell de comandos, como el shell de comandos de PowerShell para desarrolladores en Visual Studio, para ejecutar el comando siguiente. Antes de ejecutar el comando, cambie el directorio con el cd comando al directorio del proyecto de servidor. El comando establece un identificador de secretos de usuario (<UserSecretsId> en el archivo de proyecto de la aplicación de servidor):

dotnet user-secrets init

Ejecute el siguiente comando para establecer el secreto de cliente. El {SECRET} marcador de posición es el secreto de cliente obtenido del registro de la aplicación:

dotnet user-secrets set "Authentication:Schemes:MicrosoftOidc:ClientSecret" "{SECRET}"

Si usa Visual Studio, puede confirmar que el secreto está establecido haciendo clic con el botón derecho en el proyecto de servidor en Explorador de soluciones y seleccionando Administrar secretos de usuario.

Configuración de la aplicación

La siguiente configuración OpenIdConnectOptions se encuentra en el archivo del proyecto Program en la llamada a AddOpenIdConnect:

  • SignInScheme: establece el esquema de autenticación correspondiente al middleware responsable de conservar identity del usuario después de una autenticación correcta. El controlador OIDC debe usar un esquema de inicio de sesión que sea capaz de conservar las credenciales de usuario entre las solicitudes. La siguiente línea se presenta simplemente con fines de demostración. Si se omite, DefaultSignInScheme se usa como valor de reserva.

    oidcOptions.SignInScheme = CookieAuthenticationDefaults.AuthenticationScheme;
    
  • Ámbitos para openid y profile (Scope) (opcional): los ámbitos openid y profile también se configuran de forma predeterminada porque son necesarios para que el controlador OIDC funcione, pero es posible que deban volver a agregarse si los ámbitos se incluyen en la configuración de Authentication:Schemes:MicrosoftOidc:Scope. Para obtener instrucciones generales de configuración, consulta Configuración en ASP.NET Core y Configuración de Blazor de ASP.NET Core.

    oidcOptions.Scope.Add(OpenIdConnectScope.OpenIdProfile);
    
  • SaveTokens: define si los tokens de acceso y actualización deben almacenarse en AuthenticationProperties después de una autorización correcta. El valor se establece en true para autenticar las solicitudes de datos meteorológicos del proyecto de API web de back-end (MinimalApiJwt).

    oidcOptions.SaveTokens = true;
    
  • Ámbito para el acceso sin conexión (Scope): el ámbito offline_access es necesario para el token de actualización.

    oidcOptions.Scope.Add(OpenIdConnectScope.OfflineAccess);
    
  • Ámbitos para obtener datos meteorológicos de la API web (Scope): el ámbito Weather.Get se configura en Entra o Azure Portal en Exponer una API. Esto es necesario para el proyecto de API web de back-end (MinimalApiJwt) de cara a validar el token de acceso con JWT de portador.

    oidcOptions.Scope.Add("{APP ID URI}/{API NAME}");
    

    Ejemplo:

    • URI de identificador de aplicación ({APP ID URI}): https://{DIRECTORY NAME}.onmicrosoft.com/{CLIENT ID}
      • Nombre del directorio ({DIRECTORY NAME}): contoso
      • Identificador de aplicación (cliente) ({CLIENT ID}): 00001111-aaaa-2222-bbbb-3333cccc4444
    • Ámbito configurado para los datos meteorológicos de MinimalApiJwt ({API NAME}): Weather.Get
    oidcOptions.Scope.Add("https://contoso.onmicrosoft.com/00001111-aaaa-2222-bbbb-3333cccc4444/Weather.Get");
    

    El ejemplo anterior pertenece a una aplicación registrada en un inquilino con un tipo de inquilino de AAD B2C. Si la aplicación está registrada en un inquilino ME-ID, el URI de identificador de aplicación es diferente, por lo que el ámbito es diferente.

    Ejemplo:

    • URI de identificador de aplicación ({APP ID URI}): api://{CLIENT ID} con identificador de aplicación (cliente) ({CLIENT ID}): 00001111-aaaa-2222-bbbb-3333cccc4444
    • Ámbito configurado para los datos meteorológicos de MinimalApiJwt ({API NAME}): Weather.Get
    oidcOptions.Scope.Add("api://00001111-aaaa-2222-bbbb-3333cccc4444/Weather.Get");
    
  • Authority y ClientId: establece la autoridad y el id. de cliente para las llamadas de OIDC.

    oidcOptions.Authority = "{AUTHORITY}";
    oidcOptions.ClientId = "{CLIENT ID}";
    

    Ejemplo:

    • Autoridad ({AUTHORITY}): https://login.microsoftonline.com/aaaabbbb-0000-cccc-1111-dddd2222eeee/v2.0/ (usa el id. de inquilino aaaabbbb-0000-cccc-1111-dddd2222eeee)
    • Id. de cliente ({CLIENT ID}): 00001111-aaaa-2222-bbbb-3333cccc4444
    oidcOptions.Authority = "https://login.microsoftonline.com/aaaabbbb-0000-cccc-1111-dddd2222eeee/v2.0/";
    oidcOptions.ClientId = "00001111-aaaa-2222-bbbb-3333cccc4444";
    

    Ejemplo de autoridad "común" de Microsoft Azure:

    La entidad "común" debe usarse para aplicaciones multiinquilino. También puedes usar la entidad "común" para las aplicaciones de un solo inquilino, pero se requiere un IssuerValidator personalizado, como se muestra más adelante en esta sección.

    oidcOptions.Authority = "https://login.microsoftonline.com/common/v2.0/";
    
  • ResponseType: configura el controlador OIDC para que solo realice el flujo de código de autorización. Las concesiones implícitas y los flujos híbridos no son necesarios en este modo.

    En la configuración de registro de aplicaciones de flujos híbridos y concesión implícita de Entra o Azure Portal, no actives ninguna casilla de selección para que el punto de conexión de autorización devuelva Tokens de acceso o Tokens de id.. El controlador OIDC solicita automáticamente los tokens adecuados mediante el código que devuelve el punto de conexión de autorización.

    oidcOptions.ResponseType = OpenIdConnectResponseType.Code;
    
  • MapInboundClaims y configuración de NameClaimType y RoleClaimType: muchos servidores OIDC usan "name" y "role" en lugar de los valores predeterminados de SOAP/WS-Fed en ClaimTypes. Cuando MapInboundClaims se establece en false, el controlador no realiza asignaciones de notificaciones y la aplicación usa directamente los nombres de notificación del JWT. En el ejemplo siguiente, se establece el tipo de notificación de rol en "roles", que es adecuado para Microsoft Entra ID (ME-ID). Para más información, consulta la documentación de tu proveedor de identity.

    Nota:

    MapInboundClaims debe establecerse en false para la mayoría de los proveedores de OIDC, lo que impide cambiar el nombre de las notificaciones.

    oidcOptions.MapInboundClaims = false;
    oidcOptions.TokenValidationParameters.NameClaimType = "name";
    oidcOptions.TokenValidationParameters.RoleClaimType = "roles";
    
  • Configuración de rutas: las rutas deben coincidir con las rutas del URI de redirección (ruta de devolución de llamada de inicio de sesión) y la redirección posterior al cierre de sesión (ruta de devolución de llamada de cierre de sesión) configuradas al registrar la aplicación con el proveedor OIDC. En Azure Portal, las rutas se configuran en la hoja Autenticación del registro de la aplicación. Las rutas de inicio de sesión y cierre de sesión deben registrarse como URI de redirección. Los valores predeterminados son /signin-oidc y /signout-callback-oidc.

    • CallbackPath: ruta de solicitud en la ruta base de la aplicación en la que se devolverá el agente de usuario.

      En Entra o Azure Portal, establece la ruta en el URI de redirección de la configuración de la plataforma web:

      https://localhost/signin-oidc

      Nota:

      No se requiere un puerto para las direcciones localhost.

    • SignedOutCallbackPath: ruta de solicitud en la ruta base de la aplicación en la que se devolverá el agente de usuario después de cerrar la sesión del proveedor de identity.

      En Entra o Azure Portal, establece la ruta en el URI de redirección de la configuración de la plataforma web:

      https://localhost/signout-callback-oidc

      Nota:

      No se requiere un puerto para las direcciones localhost.

      Nota:

      Si usas Microsoft Identity Web, actualmente el proveedor solo redirige de nuevo a SignedOutCallbackPath si se usa la autoridad de microsoftonline.com (https://login.microsoftonline.com/{TENANT ID}/v2.0/). Esta limitación no existe si puedes usar la autoridad "común" con Microsoft Identity Web. Para obtener más información, consulta postLogoutRedirectUri no funciona cuando la dirección URL de autoridad contiene un id. de inquilino (AzureAD/microsoft-authentication-library-for-js #5783).

    • RemoteSignOutPath: las solicitudes recibidas en esta ruta hacen que el controlador invoque el cierre de sesión mediante el esquema de cierre de sesión.

      En Entra o Azure Portal, establece la URL de cierre de sesión de Front-Channel:

      https://localhost/signout-oidc

      Nota:

      No se requiere un puerto para las direcciones localhost.

      oidcOptions.CallbackPath = new PathString("{PATH}");
      oidcOptions.SignedOutCallbackPath = new PathString("{PATH}");
      oidcOptions.RemoteSignOutPath = new PathString("{PATH}");
      

      Ejemplos (valores predeterminados):

      oidcOptions.CallbackPath = new PathString("/signin-oidc");
      oidcOptions.SignedOutCallbackPath = new PathString("/signout-callback-oidc");
      oidcOptions.RemoteSignOutPath = new PathString("/signout-oidc");
      
  • (Microsoft Azure solo con el punto de conexión "común") TokenValidationParameters.IssuerValidator: muchos proveedores de OIDC funcionan con el validador de emisor predeterminado, pero es necesario tener en cuenta el emisor parametrizado con el id. de inquilino ({TENANT ID}) devuelto por https://login.microsoftonline.com/common/v2.0/.well-known/openid-configuration. Para obtener más información, consulta SecurityTokenInvalidIssuerException con OpenID Connect y el punto de conexión "común" de Azure AD (AzureAD/azure-activedirectory-identitymodel-extensions-for-dotnet #1731).

    Solo para las aplicaciones que usan Microsoft Entra ID o Azure AD B2C con el punto de conexión "común":

    var microsoftIssuerValidator = AadIssuerValidator.GetAadIssuerValidator(oidcOptions.Authority);
    oidcOptions.TokenValidationParameters.IssuerValidator = microsoftIssuerValidator.Validate;
    

Código de aplicación de ejemplo

Inspecciona la aplicación de ejemplo para ver las siguientes características:

  • Actualización automática de tokens no interactivos con la ayuda de un actualizador personalizado cookie (CookieOidcRefresher.cs).
  • El proyecto de servidor llama AddAuthenticationStateSerialization a para agregar un proveedor de estado de autenticación del lado servidor que usa PersistentComponentState para fluir el estado de autenticación al cliente. El cliente llama AddAuthenticationStateDeserialization a para deserializar y usar el estado de autenticación pasado por el servidor. El estado de autenticación es fijo durante la vigencia de la aplicación WebAssembly.
  • Las solicitudes a la Blazor Web App se redirigen mediante proxy al proyecto de API web de back-end (MinimalApiJwt). El MapForwarder en el archivo Program agrega reenvío directo de solicitudes HTTP que coinciden con el patrón especificado a un destino específico mediante la configuración predeterminada para la solicitud saliente, las transformaciones personalizadas y el cliente HTTP predeterminado:
    • Al representar el componente Weather en el servidor, el componente usa el ServerWeatherForecaster para redirigir mediante proxy la solicitud de datos meteorológicos con el token de acceso del usuario.
    • Cuando el componente se representa en el cliente, usa la implementación del servicio ClientWeatherForecaster, que usa un HttpClient preconfigurado (en el archivo Program del proyecto de cliente) para realizar una llamada API web al proyecto de servidor. Un punto de conexión de API mínimo (/weather-forecast) definido en el archivo Program del proyecto de servidor transforma la solicitud con el token de acceso del usuario para obtener los datos meteorológicos.
  • Actualización automática de tokens no interactivos con la ayuda de un actualizador personalizado cookie (CookieOidcRefresher.cs).
  • La clase PersistingAuthenticationStateProvider (PersistingAuthenticationStateProvider.cs) es un AuthenticationStateProvider del lado servidor que usa PersistentComponentState para hacer fluir el estado de autenticación, que posteriormente es fijo durante la vigencia de la aplicación WebAssembly, al cliente.
  • Las solicitudes a la Blazor Web App se redirigen mediante proxy al proyecto de API web de back-end (MinimalApiJwt). El MapForwarder en el archivo Program agrega reenvío directo de solicitudes HTTP que coinciden con el patrón especificado a un destino específico mediante la configuración predeterminada para la solicitud saliente, las transformaciones personalizadas y el cliente HTTP predeterminado:
    • Al representar el componente Weather en el servidor, el componente usa el ServerWeatherForecaster para redirigir mediante proxy la solicitud de datos meteorológicos con el token de acceso del usuario.
    • Cuando el componente se representa en el cliente, usa la implementación del servicio ClientWeatherForecaster, que usa un HttpClient preconfigurado (en el archivo Program del proyecto de cliente) para realizar una llamada API web al proyecto de servidor. Un punto de conexión de API mínimo (/weather-forecast) definido en el archivo Program del proyecto de servidor transforma la solicitud con el token de acceso del usuario para obtener los datos meteorológicos.

Para obtener más información sobre las llamadas API (web) mediante abstracciones de servicio en Blazor Web App, consulta Llamada a una API web desde una aplicación Blazor de ASP.NET Core.

Proyecto de Blazor Web App del lado cliente (BlazorWebAppOidc.Client)

El proyecto BlazorWebAppOidc.Client es el proyecto del lado cliente de Blazor Web App.

El cliente llama AddAuthenticationStateDeserialization a para deserializar y usar el estado de autenticación pasado por el servidor. El estado de autenticación es fijo durante la vigencia de la aplicación WebAssembly.

La clase PersistentAuthenticationStateProvider (PersistentAuthenticationStateProvider.cs) es un AuthenticationStateProvider del lado cliente que determina el estado de autenticación del usuario buscando datos guardados en la página cuando se representa en el servidor. El estado de autenticación es fijo durante la vigencia de la aplicación WebAssembly.

Si el usuario necesita iniciar sesión o cerrar sesión, se requiere una recarga de página completa.

La aplicación de ejemplo solo proporciona un nombre de usuario y un correo electrónico con fines de demostración. No incluye tokens que se autentican en el servidor al realizar solicitudes posteriores, que funcionan por separado mediante un cookie que se incluye en solicitudes HttpClient al servidor.

Proyecto de API web de back-end (MinimalApiJwt)

El proyecto MinimalApiJwt es una API web de back-end para varios proyectos de front-end. El proyecto configura un punto de conexión de API mínima para datos meteorológicos. Las solicitudes del proyecto del lado servidor de la Blazor Web App (BlazorWebAppOidc) se redirigen mediante proxy al proyecto MinimalApiJwt.

Configuración

Configura el proyecto en JwtBearerOptions de la llamada AddJwtBearer en el archivo del proyecto Program:

  • Audience: establece la audiencia de cualquier token de OpenID Connect recibido.

    En Entra o Azure Portal: haga coincidir el valor con solo la ruta de acceso del URI del id. de aplicación configurado al agregar el ámbito Weather.Get en Exponer una API:

    jwtOptions.Audience = "{APP ID URI}";
    

    Ejemplo:

    URI de identificador de aplicación ({APP ID URI}): https://{DIRECTORY NAME}.onmicrosoft.com/{CLIENT ID}:

    • Nombre del directorio ({DIRECTORY NAME}): contoso
    • Identificador de aplicación (cliente) ({CLIENT ID}): 00001111-aaaa-2222-bbbb-3333cccc4444
    jwtOptions.Audience = "https://contoso.onmicrosoft.com/00001111-aaaa-2222-bbbb-3333cccc4444";
    

    El ejemplo anterior pertenece a una aplicación registrada en un inquilino con un tipo de inquilino de AAD B2C. Si la aplicación está registrada en un inquilino ME-ID, el URI de identificador de aplicación es diferente, por lo que el público es diferente.

    Ejemplo:

    URI de identificador de aplicación ({APP ID URI}): api://{CLIENT ID} con identificador de aplicación (cliente) ({CLIENT ID}): 00001111-aaaa-2222-bbbb-3333cccc4444

    jwtOptions.Audience = "api://00001111-aaaa-2222-bbbb-3333cccc4444";
    
  • Authority: establece la autoridad para realizar llamadas de OpenID Connect. Haz coincidir el valor con la autoridad configurada para el controlador OIDC en BlazorWebAppOidc/Program.cs:

    jwtOptions.Authority = "{AUTHORITY}";
    

    Ejemplo:

    Autoridad ({AUTHORITY}): https://login.microsoftonline.com/aaaabbbb-0000-cccc-1111-dddd2222eeee/v2.0/ (usa el id. de inquilino aaaabbbb-0000-cccc-1111-dddd2222eeee)

    jwtOptions.Authority = "https://login.microsoftonline.com/aaaabbbb-0000-cccc-1111-dddd2222eeee/v2.0/";
    

    El ejemplo anterior pertenece a una aplicación registrada en un inquilino con un tipo de inquilino de AAD B2C. Si la aplicación está registrada en un inquilino ME-ID, la autoridad debe coincidir con el emisor (iss) del JWT devuelto por el proveedor de identity:

    jwtOptions.Authority = "https://sts.windows.net/aaaabbbb-0000-cccc-1111-dddd2222eeee/";
    

API mínima para datos meteorológicos

Punto de conexión de datos de previsión meteorológica segura en el archivo Program del proyecto:

app.MapGet("/weather-forecast", () =>
{
    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();

El método de extensión RequireAuthorization requiere autorización para la definición de ruta. Para los controladores que agregues al proyecto, agrega el atributo [Authorize] al controlador o la acción.

Redireccionamiento a la página home al cerrar sesión

Cuando un usuario navega por la aplicación, el componente LogInOrOut (Layout/LogInOrOut.razor) establece un campo oculto para la dirección URL de retorno (ReturnUrl) en el valor de la dirección URL actual (currentURL). Cuando el usuario cierra la sesión de la aplicación, el proveedor de identity lo devuelve a la página desde la que cerró la sesión.

Si el usuario cierra sesión desde una página segura, se le devuelve a la misma página segura después de cerrar la sesión, solo para el envío de vuelta a través del proceso de autenticación. Este comportamiento es adecuado cuando los usuarios necesitan cambiar de cuenta con frecuencia. Sin embargo, una especificación de aplicación alternativa puede hacer que se devuelva al usuario a la página home de la aplicación o a otra página después del cierre de sesión. En el ejemplo siguiente se muestra cómo establecer la página home de la aplicación como la dirección URL de retorno para las operaciones de cierre de sesión.

Los cambios importantes en el componente LogInOrOut se muestran en el ejemplo siguiente. No es necesario proporcionar un campo oculto para el ReturnUrl conjunto en la home página en / porque es la ruta de acceso predeterminada. IDisposable ya no se implementa. NavigationManager ya no se inserta. Se elimina todo el bloque @code.

Layout/LogInOrOut.razor:

@using Microsoft.AspNetCore.Authorization

<div class="nav-item px-3">
    <AuthorizeView>
        <Authorized>
            <form action="authentication/logout" method="post">
                <AntiforgeryToken />
                <button type="submit" class="nav-link">
                    <span class="bi bi-arrow-bar-left-nav-menu" aria-hidden="true">
                    </span> Logout @context.User.Identity?.Name
                </button>
            </form>
        </Authorized>
        <NotAuthorized>
            <a class="nav-link" href="authentication/login">
                <span class="bi bi-person-badge-nav-menu" aria-hidden="true"></span> 
                Login
            </a>
        </NotAuthorized>
    </AuthorizeView>
</div>

Valor nonce criptográfico

Un valor nonce es un valor de cadena que asocia la sesión de un cliente con un token de identificador para mitigar los ataques de reproducción.

Si recibes un error del valor nonce durante el desarrollo y las pruebas de autenticación, usa una nueva sesión del explorador InPrivate o incógnito para cada ejecución de prueba, independientemente de lo pequeño que sea el cambio realizado en la aplicación o el usuario de prueba porque los datos de las cookie obsoletos pueden provocar un error del valor nonce. Para obtener más información, consulta la sección Cookies y datos del sitio.

No se requiere ni se usa un valor nonce cuando se intercambia un token de actualización para un nuevo token de acceso. En la aplicación de ejemplo, CookieOidcRefresher (CookieOidcRefresher.cs) establece deliberadamente OpenIdConnectProtocolValidator.RequireNonce en false.

Roles de aplicación para aplicaciones no registradas con Microsoft Entra (ME-ID)

Esta sección pertenece a las aplicaciones que no usan Microsoft Entra ID (ME-ID) como proveedor de identity. Para las aplicaciones registradas con ME-ID, consulta la sección Roles de aplicación para aplicaciones registradas con Microsoft Entra (ME-ID).

Configura el tipo de notificación de rol (TokenValidationParameters.RoleClaimType) en OpenIdConnectOptions de Program.cs:

oidcOptions.TokenValidationParameters.RoleClaimType = "{ROLE CLAIM TYPE}";

Para muchos proveedores de identity de OIDC, el tipo de notificación de rol es role. Comprueba la documentación del proveedor de identity para obtener el valor correcto.

Reemplaza la clase UserInfo del proyecto BlazorWebAppOidc.Client por la siguiente:

UserInfo.cs:

using Microsoft.AspNetCore.Components.WebAssembly.Authentication;
using System.Security.Claims;

namespace BlazorWebAppOidc.Client;

// Add properties to this class and update the server and client 
// AuthenticationStateProviders to expose more information about 
// the authenticated user to the client.
public sealed class UserInfo
{
    public required string UserId { get; init; }
    public required string Name { get; init; }
    public required string[] Roles { get; init; }

    public const string UserIdClaimType = "sub";
    public const string NameClaimType = "name";
    private const string RoleClaimType = "role";

    public static UserInfo FromClaimsPrincipal(ClaimsPrincipal principal) =>
        new()
        {
            UserId = GetRequiredClaim(principal, UserIdClaimType),
            Name = GetRequiredClaim(principal, NameClaimType),
            Roles = principal.FindAll(RoleClaimType).Select(c => c.Value)
                .ToArray(),
        };

    public ClaimsPrincipal ToClaimsPrincipal() =>
        new(new ClaimsIdentity(
            Roles.Select(role => new Claim(RoleClaimType, role))
                .Concat([
                    new Claim(UserIdClaimType, UserId),
                    new Claim(NameClaimType, Name),
                ]),
            authenticationType: nameof(UserInfo),
            nameType: NameClaimType,
            roleType: RoleClaimType));

    private static string GetRequiredClaim(ClaimsPrincipal principal,
        string claimType) =>
            principal.FindFirst(claimType)?.Value ??
            throw new InvalidOperationException(
                $"Could not find required '{claimType}' claim.");
}

En este momento, los componentes Razor pueden adoptar la autorización basada en roles y directivas. Los roles de aplicación aparecen en notificaciones role, una notificación por rol.

Roles de aplicación para aplicaciones registradas con Microsoft Entra (ME-ID)

Usa la guía de esta sección para implementar roles de aplicación, grupos de seguridad de ME-ID y roles de cuenta predefinida de administrador de ME-ID para aplicaciones que usan Microsoft Entra ID (ME-ID).

El enfoque descrito en esta sección configura ME-ID para enviar grupos y roles en el encabezado cookie de autenticación. Cuando los usuarios solo son miembros de unos pocos grupos de seguridad y roles, el siguiente enfoque debe funcionar para la mayoría de las plataformas de hospedaje sin tener un problema en el que los encabezados son demasiado largos, por ejemplo, con el hospedaje de IIS que tiene un límite de longitud de encabezado predeterminado de 16 KB (MaxRequestBytes). Si la longitud del encabezado es un problema debido a la pertenencia a grupos o roles elevados, se recomienda no seguir las instrucciones de esta sección en favor de implementar Microsoft Graph para obtener los grupos y roles de un usuario de ME-ID por separado, un enfoque que no infla el tamaño de la autenticación cookie. Para obtener más información, consulta Solicitud incorrecta- Solicitud demasiado larga - Servidor IIS (dotnet/aspnetcore #57545).

Configura el tipo de notificación de rol (TokenValidationParameters.RoleClaimType) en OpenIdConnectOptions de Program.cs. Establece el valor en roles:

oidcOptions.TokenValidationParameters.RoleClaimType = "roles";

Aunque no se pueden asignar roles a grupos sin una cuenta de Microsoft Entra ID Premium, sí se pueden asignar roles a usuarios y recibir una notificación para usuarios con una cuenta de Azure estándar. Las instrucciones de esta sección no requieren una cuenta ME-ID Premium.

Al trabajar con el directorio predeterminado, sigue las instrucciones de Incorporación de roles de aplicación a una aplicación y recibirlos en el token (documentación de ME-ID) para configurar y asignar los roles. Si no estás trabajando con el directorio predeterminado, edita el manifiesto de la aplicación en Azure Portal para establecer manualmente los roles de la aplicación en la entrada appRoles del archivo de manifiesto. Para obtener más información, consulta Configuración de notificación de rol (documentación de ME-ID).

Los grupos de seguridad de Azure de un usuario llegan a notificaciones groups y las asignaciones de rol Administrador de ME-ID integradas de un usuario llegan a notificaciones de id. conocidos (wids). Los valores de ambos tipos de notificación son GUID. Cuando la aplicación las recibe, estas notificaciones se pueden usar para establecer la autorización de roles y directivas en los componentes Razor.

En el manifiesto de la aplicación de Azure Portal, establece el atributo groupMembershipClaims en All. Un valor de All da como resultado que ME-ID envíe todos los grupos de seguridad o de distribución (notificaciones groups) y los roles (notificaciones wids) del usuario que ha iniciado sesión. Para establecer el atributo groupMembershipClaims:

  1. Abre el registro de la aplicación en Azure Portal.
  2. Selecciona Administrar>Manifiesto en la barra lateral.
  3. Busca el atributo groupMembershipClaims.
  4. Establece el valor en All ("groupMembershipClaims": "All").
  5. Selecciona el botón Guardar.

Reemplaza la clase UserInfo del proyecto BlazorWebAppOidc.Client por la siguiente:

UserInfo.cs:

using Microsoft.AspNetCore.Components.WebAssembly.Authentication;
using System.Security.Claims;

namespace BlazorWebAppOidc.Client;

// Add properties to this class and update the server and client 
// AuthenticationStateProviders to expose more information about 
// the authenticated user to the client.
public sealed class UserInfo
{
    public required string UserId { get; init; }
    public required string Name { get; init; }
    public required string[] Roles { get; init; }
    public required string[] Groups { get; init; }
    public required string[] Wids { get; init; }

    public const string UserIdClaimType = "sub";
    public const string NameClaimType = "name";
    private const string RoleClaimType = "roles";
    private const string GroupsClaimType = "groups";
    private const string WidsClaimType = "wids";

    public static UserInfo FromClaimsPrincipal(ClaimsPrincipal principal) =>
        new()
        {
            UserId = GetRequiredClaim(principal, UserIdClaimType),
            Name = GetRequiredClaim(principal, NameClaimType),
            Roles = principal.FindAll(RoleClaimType).Select(c => c.Value)
                .ToArray(),
            Groups = principal.FindAll(GroupsClaimType).Select(c => c.Value)
                .ToArray(),
            Wids = principal.FindAll(WidsClaimType).Select(c => c.Value)
                .ToArray(),
        };

    public ClaimsPrincipal ToClaimsPrincipal() =>
        new(new ClaimsIdentity(
            Roles.Select(role => new Claim(RoleClaimType, role))
                .Concat(Groups.Select(role => new Claim(GroupsClaimType, role)))
                .Concat(Wids.Select(role => new Claim(WidsClaimType, role)))
                .Concat([
                    new Claim(UserIdClaimType, UserId),
                    new Claim(NameClaimType, Name),
                ]),
            authenticationType: nameof(UserInfo),
            nameType: NameClaimType,
            roleType: RoleClaimType));

    private static string GetRequiredClaim(ClaimsPrincipal principal,
        string claimType) =>
            principal.FindFirst(claimType)?.Value ??
            throw new InvalidOperationException(
                $"Could not find required '{claimType}' claim.");
}

En este momento, los componentes Razor pueden adoptar la autorización basada en roles y directivas:

  • Los roles de aplicación aparecen en notificaciones roles, una notificación por rol.
  • Los grupos de seguridad aparecen en notificaciones groups, una notificación por grupo. Los GUID del grupo de seguridad aparecen en Azure Portal al crear un grupo de seguridad y se muestran al seleccionar Identity>Información general>Grupos>Vista.
  • Los roles de administrador de ME-ID integrados aparecen en notificaciones wids, una notificación por rol. La notificación wids con un valor de b79fbf4d-3ef9-4689-8143-76b194e85509 siempre se envía mediante ME-ID para las cuentas del inquilino que no son de invitado y no hace referencia a un rol Administrador. Los GUID de rol Administrador (id. de plantilla de rol) aparecen en Azure Portal al seleccionar Roles y administradores, seguidos de los puntos suspensivos () >Descripción para el rol indicado. Los id. de plantilla de rol también aparecen en roles integrados de Microsoft Entra (documentación de Entra).

Solución de problemas

Registro

La aplicación de servidor es una aplicación estándar ASP.NET Core. Consulta la guía de registro de ASP.NET Core para habilitar un nivel de registro inferior en la aplicación de servidor.

Para habilitar el registro de depuración o rastreo para la autenticación de Blazor WebAssembly, consulta la sección Registro de autenticación del lado del cliente del Registro de Blazor de ASP.NET Core con el selector de versión del artículo establecido en ASP.NET Core 7.0 o posterior.

Errores comunes

  • Error de configuración de la aplicación o del proveedor de Identity (IP)

    Los errores más comunes se deben a una configuración incorrecta. Estos son algunos ejemplos:

    • En función de los requisitos del escenario, la disponibilidad o no de una autoridad, una instancia, un identificador o dominio de inquilino, un identificador de cliente o un URI de redireccionamiento, o bien que estos no elementos no sean correctos, impide a una aplicación autenticar clientes.
    • Los ámbitos de solicitud incorrectos impiden a los clientes acceder a los puntos de conexión de la API web del servidor.
    • Faltan permisos de la API de servidor o estos son incorrectos, lo cual impide a los clientes acceder a los puntos de conexión de API web.
    • Ejecutar la aplicación en un puerto diferente al configurado en el URI de redirección del registro de aplicación de la IP. Ten en cuenta que no se requiere ningún puerto para Microsoft Entra ID y una aplicación que se ejecute en una dirección de pruebas de desarrollo localhost, pero la configuración del puerto de la aplicación y el puerto en el que se ejecuta la aplicación deben coincidir para las direcciones que no sean localhost.

    La cobertura de configuración de este artículo muestra ejemplos de la configuración correcta. Comprueba cuidadosamente la configuración que busca la aplicación y la configuración incorrecta de IP.

    Si la configuración parece correcta:

    • Analiza los registros de la aplicación.

    • Examina el tráfico de red entre la aplicación cliente y la de servidor, o la dirección IP con las herramientas de desarrollo del explorador. A menudo, la aplicación de servidor o la dirección IP devuelve al cliente un mensaje de error exacto o un mensaje con una pista sobre la causa del problema. En los siguientes artículos encontrarás instrucciones sobre las herramientas de desarrollo:

    El equipo de documentación responde a los comentarios y los errores en los artículos (abre una incidencia en la sección de comentarios de esta página), pero no puede proporcionar soporte técnico para el producto. Existen varios foros de soporte técnico públicos que ayudan a solucionar los problemas de una aplicación. Se recomienda lo siguiente:

    Microsoft no posee ni controla ninguno de los foros anteriores.

    Respecto a los informes de errores del marco que no son de seguridad ni confidenciales, o que no se pueden reproducir, abre una incidencia con la unidad de producto ASP.NET Core. No abras una incidencia con la unidad de producto hasta que hayas investigado exhaustivamente su causa y no puedas resolverlo por tu cuenta o con la ayuda de la comunidad en un foro de soporte técnico público. La unidad de producto no puede solucionar problemas de aplicaciones individuales cuyo funcionamiento se haya interrumpido debido a errores de configuración o casos de uso sencillos que involucren servicios de terceros. Si un informe es confidencial o delicado por naturaleza o describe un posible error de seguridad en el producto que los ciberdelincuentes puedan aprovechar, consulta Informes de problemas de seguridad y errores (repositorio de GitHub dotnet/aspnetcore).

  • Cliente no autorizado para ME-ID

    Información: Error de autorización de Microsoft.AspNetCore.Authorization.DefaultAuthorizationService[2]. No se cumplen estos requisitos: DenyAnonymousAuthorizationRequirement: se requiere un usuario autenticado.

    Error de devolución de llamada de inicio de sesión de ME-ID:

    • Error: unauthorized_client
    • Descripción: AADB2C90058: The provided application is not configured to allow public clients.

    Para resolver el error:

    1. En Azure Portal, accede al manifiesto de la aplicación.
    2. Establece el atributo allowPublicClient en null o true.

Cookies y datos de sitios

Las cookies y los datos de sitios pueden persistir durante las actualizaciones de la aplicación e interferir con las pruebas y la solución de problemas. Borra los elementos siguientes al realizar cambios en el código de la aplicación, cambios en la cuenta de usuario con el proveedor o cuando el proveedor modifique la configuración de la aplicación:

  • Cookies de inicio de sesión de usuario
  • Cookies de aplicación
  • Datos de sitios almacenados y en caché

El enfoque siguiente sirve para evitar que las cookies persistentes y los datos de sitios interfieran con las pruebas y la solución de problemas:

  • Configuración de un explorador
    • Usa un explorador para las pruebas, y configúralo para que elimine todas las cookies y los datos del sitio cada vez que se cierre.
    • Asegúrate de que el explorador se cierra manualmente o mediante el IDE siempre que se produzca cualquier cambio en la aplicación, el usuario de prueba o la configuración del proveedor.
  • Usa un comando personalizado para abrir un explorador en el modo incógnito o privado en Visual Studio:
    • Abre el cuadro de diálogo Examinar con mediante el botón Ejecutar de Visual Studio.
    • Selecciona el botón Agregar.
    • Proporciona la ruta de acceso al explorador en el campo Programa. Las siguientes rutas de acceso del archivo ejecutable son ubicaciones de instalación típicas para Windows 10. Si el explorador está instalado en una ubicación diferente o no usa Windows 10, proporciona la ruta de acceso al archivo ejecutable del explorador.
      • Microsoft Edge: C:\Program Files (x86)\Microsoft\Edge\Application\msedge.exe
      • Google Chrome: C:\Program Files (x86)\Google\Chrome\Application\chrome.exe
      • Mozilla Firefox: C:\Program Files\Mozilla Firefox\firefox.exe
    • En el campo Argumentos, proporciona la opción de línea de comandos que utiliza el explorador para abrirse en el modo incógnito o privado. Algunos exploradores requieren la dirección URL de la aplicación.
      • Microsoft Edge: Usa -inprivate.
      • Google Chrome: usa --incognito --new-window {URL}, donde el marcador de posición {URL} es la dirección URL que se va a abrir (por ejemplo, https://localhost:5001).
      • Mozilla Firefox: usa -private -url {URL}, donde el marcador de posición {URL} es la dirección URL que se va a abrir (por ejemplo, https://localhost:5001).
    • Proporciona un nombre en el campo Nombre descriptivo. Por ejemplo: Firefox Auth Testing.
    • Selecciona el botón Aceptar.
    • Para evitar tener que seleccionar el perfil de explorador para cada iteración de pruebas con una aplicación, establece el perfil como predeterminado con el botón Establecer como predeterminado.
    • Asegúrate de que el explorador se cierra mediante el IDE siempre que se produzca cualquier cambio en la aplicación, el usuario de prueba o la configuración del proveedor.

Actualizaciones de aplicaciones

Una aplicación en funcionamiento deja de ejecutarse inmediatamente después de actualizar el SDK de .NET Core en la máquina de desarrollo o de cambiar las versiones del paquete en la aplicación. En algunos casos, los paquetes incoherentes pueden interrumpir una aplicación al realizar actualizaciones importantes. La mayoría de estos problemas puede corregirse siguiendo estas instrucciones:

  1. Borra las memorias caché del paquete NuGet del sistema local ejecutando dotnet nuget locals all --clear desde un shell de comandos.
  2. Elimina las carpetas bin y obj del proyecto.
  3. Restaura el proyecto y vuelve a compilarlo.
  4. Elimina todos los archivos de la carpeta de implementación del servidor antes de volver a implementar la aplicación.

Nota

No se pueden usar versiones de paquetes que no sean compatibles con la plataforma de destino de la aplicación. Para obtener información sobre un paquete, usa la galería de NuGet o el explorador de paquetes FuGet.

Ejecución de la aplicación Server

Al probar y solucionar problemas de una Blazor Web App, asegúrate de que ejecutas la aplicación desde el proyecto del servidor.

Inspección del usuario

El siguiente componente UserClaims se puede usar directamente en las aplicaciones, o bien servir como base para una mayor personalización.

UserClaims.razor:

@page "/user-claims"
@using System.Security.Claims
@using Microsoft.AspNetCore.Authorization
@attribute [Authorize]

<PageTitle>User Claims</PageTitle>

<h1>User Claims</h1>

@if (claims.Any())
{
    <ul>
        @foreach (var claim in claims)
        {
            <li><b>@claim.Type:</b> @claim.Value</li>
        }
    </ul>
}

@code {
    private IEnumerable<Claim> claims = Enumerable.Empty<Claim>();

    [CascadingParameter]
    private Task<AuthenticationState>? AuthState { get; set; }

    protected override async Task OnInitializedAsync()
    {
        if (AuthState == null)
        {
            return;
        }

        var authState = await AuthState;
        claims = authState.User.Claims;
    }
}

Recursos adicionales