Share via


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

En este artículo se describe cómo proteger una aplicación web Blazor con OpenID Connect (OIDC) mediante una aplicación de ejemplo en el repositorio de GitHub de 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. Cambie 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 aplicación web Blazor 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 aplicación web Blazor, 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 aplicación web Blazor.

Acceda 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 aplicación web Blazor del lado servidor (BlazorWebAppOidc)

El proyecto BlazorWebAppOidc es el proyecto del lado servidor de la aplicación web Blazor.

El archivo BlazorWebAppOidc.http se puede usar para probar la solicitud de datos meteorológicos. Tenga 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, vea Usar archivos .http en Visual Studio 2022.

Nota:

El proyecto de servidor usa IHttpContextAccessor/HttpContext, pero nunca para componentes representados de forma interactiva. Para más información, consulte 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:

Para Microsoft Entra ID y 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 OIDC y Cookie 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 más información sobre la implementación de Microsoft Identity Web, consulte los recursos vinculados.

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 la identidad 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, consulte 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 de forma predeterminada para reducir el tamaño de la 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/a3942615-d115-4eb7-bc84-9974abcf5064/v2.0/ (usa el id. de inquilino a3942615-d115-4eb7-bc84-9974abcf5064)
    • Id. de cliente ({CLIENT ID}): 4ba4de56-9cef-45d9-83fa-a4c18f9f5f0f
    oidcOptions.Authority = "https://login.microsoftonline.com/a3942615-d115-4eb7-bc84-9974abcf5064/v2.0/";
    oidcOptions.ClientId = "4ba4de56-9cef-45d9-83fa-a4c18f9f5f0f";
    

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

    La entidad "común" debe usarse para aplicaciones multiinquilino. También puede 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/";
    
  • ClientSecret: secreto de cliente de OIDC.

    El siguiente ejemplo solamente sirve de prueba y demostración. No almacene el secreto de cliente en el ensamblado de la aplicación ni compruebe el secreto en el control de código fuente. Almacene el secreto de cliente en Secretos de usuario, Azure Key Vault o una variable de entorno.

    La configuración del esquema de autenticación se lee automáticamente de builder.Configuration["Authentication:Schemes:{SCHEME NAME}:{PropertyName}"], donde el marcador de posición {SCHEME NAME} es el esquema, que es MicrosoftOidc de forma predeterminada. Dado que la configuración está preconfigurada, se puede leer automáticamente un secreto de cliente a través de la clave de configuración Authentication:Schemes:MicrosoftOidc:ClientSecret. En el servidor que usa variables de entorno, asigne un nombre a la variable de entorno Authentication__Schemes__MicrosoftOidc__ClientSecret:

    set Authentication__Schemes__MicrosoftOidc__ClientSecret={CLIENT SECRET}
    

    Solo para demostración y pruebas, ClientSecret se puede establecer directamente. No establezca el valor directamente para las aplicaciones de producción implementadas. Para mejorar ligeramente la seguridad, compile condicionalmente la línea con el símbolo DEBUG:

    #if DEBUG
    oidcOptions.ClientSecret = "{CLIENT SECRET}";
    #endif
    

    Ejemplo:

    Secreto de cliente ({CLIENT SECRET}): 463471c8c4...f90d674bc9 (abreviado para la visualización)

    #if DEBUG
    oidcOptions.ClientSecret = "463471c8c4...137f90d674bc9";
    #endif
    
  • 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 active 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 asignan manualmente las notificaciones de nombre y rol:

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 = JwtRegisteredClaimNames.Name;
oidcOptions.TokenValidationParameters.RoleClaimType = "role";
  • 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, establezca 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 identidades.

      En Entra o Azure Portal, establezca 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 usa 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 puede usar la autoridad "común" con Microsoft Identity Web. Para obtener más información, consulte 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, establezca 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 más información, consulte 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

Inspeccione 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).
  • 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 aplicación web Blazor 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 agregue al proyecto, agregue 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 Web Apps Blazor, consulte Llamada a una API web desde una aplicación de ASP.NET Core Blazor.

Proyecto de aplicación web Blazor del lado cliente (BlazorWebAppOidc.Client)

El proyecto BlazorWebAppOidc.Client es el proyecto del lado cliente de la aplicación web Blazor.

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). Cambie 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 aplicación web Blazor 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 BFF (BackEnd for Frontend) 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 aplicación web Blazor 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.

Advertencia de paquete en versión preliminar

Advertencia

Las tecnologías y los paquetes usados por la aplicación de ejemplo BlazorWebAppOidcBff y que se describen en este artículo se encuentran en versión preliminar en este momento. El contenido del artículo, la API y la aplicación de ejemplo no se admiten en este momento y no se recomiendan actualmente para su uso en producción. La aplicación de ejemplo y las instrucciones están sujetas a cambios sin previo aviso.

Requisito previo

.NET Aspire requiere Visual Studio versión 17.10 o 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 aplicación web Blazor.
  • BlazorWebAppOidc.Client: proyecto del lado cliente de la aplicación web Blazor.

Acceda 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.

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

Proyectos de .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, consulte la documentación de .NET Aspire.

Confirme cumple los requisitos previos para .NET Aspire. Para más información, consulte la sección Requisitos previos de Inicio rápido: Compilación de la primera aplicación .NET Aspire.

Proyecto de aplicación web Blazor del lado servidor (BlazorWebAppOidc)

El proyecto BlazorWebAppOidc es el proyecto del lado servidor de la aplicación web Blazor. 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. Tenga 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, vea Usar archivos .http en Visual Studio 2022.

Nota:

El proyecto de servidor usa IHttpContextAccessor/HttpContext, pero nunca para componentes representados de forma interactiva. Para más información, consulte 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:

Para Microsoft Entra ID y 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 OIDC y Cookie 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 más información sobre la implementación de Microsoft Identity Web, consulte los recursos vinculados.

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 la identidad 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, consulte 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}): 4ba4de56-9cef-45d9-83fa-a4c18f9f5f0f
    • Ámbito configurado para los datos meteorológicos de MinimalApiJwt ({API NAME}): Weather.Get
    oidcOptions.Scope.Add("https://contoso.onmicrosoft.com/4ba4de56-9cef-45d9-83fa-a4c18f9f5f0f/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}): 4ba4de56-9cef-45d9-83fa-a4c18f9f5f0f
    • Ámbito configurado para los datos meteorológicos de MinimalApiJwt ({API NAME}): Weather.Get
    oidcOptions.Scope.Add("api://4ba4de56-9cef-45d9-83fa-a4c18f9f5f0f/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/a3942615-d115-4eb7-bc84-9974abcf5064/v2.0/ (usa el id. de inquilino a3942615-d115-4eb7-bc84-9974abcf5064)
    • Id. de cliente ({CLIENT ID}): 4ba4de56-9cef-45d9-83fa-a4c18f9f5f0f
    oidcOptions.Authority = "https://login.microsoftonline.com/a3942615-d115-4eb7-bc84-9974abcf5064/v2.0/";
    oidcOptions.ClientId = "4ba4de56-9cef-45d9-83fa-a4c18f9f5f0f";
    

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

    La entidad "común" debe usarse para aplicaciones multiinquilino. También puede 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/";
    
  • ClientSecret: secreto de cliente de OIDC.

    El siguiente ejemplo solamente sirve de prueba y demostración. No almacene el secreto de cliente en el ensamblado de la aplicación ni compruebe el secreto en el control de código fuente. Almacene el secreto de cliente en Secretos de usuario, Azure Key Vault o una variable de entorno.

    La configuración del esquema de autenticación se lee automáticamente de builder.Configuration["Authentication:Schemes:{SCHEME NAME}:{PropertyName}"], donde el marcador de posición {SCHEME NAME} es el esquema, que es MicrosoftOidc de forma predeterminada. Dado que la configuración está preconfigurada, se puede leer automáticamente un secreto de cliente a través de la clave de configuración Authentication:Schemes:MicrosoftOidc:ClientSecret. En el servidor que usa variables de entorno, asigne un nombre a la variable de entorno Authentication__Schemes__MicrosoftOidc__ClientSecret:

    set Authentication__Schemes__MicrosoftOidc__ClientSecret={CLIENT SECRET}
    

    Solo para demostración y pruebas, ClientSecret se puede establecer directamente. No establezca el valor directamente para las aplicaciones de producción implementadas. Para mejorar ligeramente la seguridad, compile condicionalmente la línea con el símbolo DEBUG:

    #if DEBUG
    oidcOptions.ClientSecret = "{CLIENT SECRET}";
    #endif
    

    Ejemplo:

    Secreto de cliente ({CLIENT SECRET}): 463471c8c4...f90d674bc9 (abreviado para la visualización)

    #if DEBUG
    oidcOptions.ClientSecret = "463471c8c4...137f90d674bc9";
    #endif
    
  • 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 active 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 asignan manualmente las notificaciones de nombre y rol:

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 = JwtRegisteredClaimNames.Name;
oidcOptions.TokenValidationParameters.RoleClaimType = "role";
  • 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, establezca 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 identidades.

      En Entra o Azure Portal, establezca 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 usa 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 puede usar la autoridad "común" con Microsoft Identity Web. Para obtener más información, consulte 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, establezca 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 más información, consulte 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

Inspeccione 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).
  • 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 aplicación web Blazor 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 Web Apps Blazor, consulte Llamada a una API web desde una aplicación de ASP.NET Core Blazor.

Proyecto de aplicación web Blazor del lado cliente (BlazorWebAppOidc.Client)

El proyecto BlazorWebAppOidc.Client es el proyecto del lado cliente de la aplicación web Blazor.

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 aplicación web Blazor (BlazorWebAppOidc) se redirigen mediante proxy al proyecto MinimalApiJwt.

Configuración

Configure 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}): 4ba4de56-9cef-45d9-83fa-a4c18f9f5f0f
    jwtOptions.Audience = "https://contoso.onmicrosoft.com/4ba4de56-9cef-45d9-83fa-a4c18f9f5f0f";
    

    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}): 4ba4de56-9cef-45d9-83fa-a4c18f9f5f0f

    jwtOptions.Audience = "api://4ba4de56-9cef-45d9-83fa-a4c18f9f5f0f";
    
  • Authority: establece la autoridad para realizar llamadas de OpenID Connect. Haga 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/a3942615-d115-4eb7-bc84-9974abcf5064/v2.0/ (usa el id. de inquilino a3942615-d115-4eb7-bc84-9974abcf5064)

    jwtOptions.Authority = "https://login.microsoftonline.com/a3942615-d115-4eb7-bc84-9974abcf5064/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 identidades:

    jwtOptions.Authority = "https://sts.windows.net/a3942615-d115-4eb7-bc84-9974abcf5064/";
    

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 agregue al proyecto, agregue el atributo [Authorize] al controlador o la acción.

Redireccionamiento a la página principal 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 identidades 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 el usuario se devuelva a la página principal 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 principal 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. El valor value del campo oculto de ReturnUrl se establece en la página principal en /. 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 />
                <input type="hidden" name="ReturnUrl" value="/" />
                <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 recibe un error del valor nonce durante el desarrollo y las pruebas de autenticación, use 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 más información, consulte la sección Cookie 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.

Solución de problemas

Registro

La aplicación de servidor es una aplicación estándar ASP.NET Core. Consulte 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, consulte 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. Tenga 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. Compruebe cuidadosamente la configuración que busca la aplicación y la configuración incorrecta de IP.

    Si la configuración parece correcta:

    • Analice los registros de la aplicación.

    • Examine 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á instrucciones sobre las herramientas de desarrollo:

    El equipo de documentación responde a los comentarios y los errores en los artículos (abra 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, abra una incidencia con la unidad de producto ASP.NET Core. No abra una incidencia con la unidad de producto hasta que haya investigado exhaustivamente su causa y no pueda resolverlo por su 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 atacantes pueden aprovechar, vea 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
    • Description (Descripción): AADB2C90058: The provided application is not configured to allow public clients.

    Para resolver el error:

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

Cookies y datos del sitio

Las Cookies y los datos del sitio pueden persistir entre las actualizaciones de la aplicación e interferir con las pruebas y la solución de problemas. Borre 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 del usuario
  • cookies de aplicaciones
  • Datos de sitios almacenados y en caché

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

  • Configuración de un explorador
    • Use un explorador para las pruebas, y configúrelo para que elimine todas las cookies y los datos del sitio cada vez que se cierre.
    • Asegúrese 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.
  • Use un comando personalizado para abrir un explorador en el modo incógnito o privado en Visual Studio:
    • Abra el cuadro de diálogo Examinar con mediante el botón Ejecutar de Visual Studio.
    • Seleccione el botón Agregar.
    • Proporcione 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, proporcione 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, proporcione 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: Use -inprivate.
      • Google Chrome: Use --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: Use -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).
    • Proporcione un nombre en el campo Nombre descriptivo. Por ejemplo: Firefox Auth Testing.
    • Seleccione el botón Aceptar.
    • Para evitar tener que seleccionar el perfil de explorador para cada iteración de pruebas con una aplicación, establezca el perfil como predeterminado con el botón Establecer como predeterminado.
    • Asegúrese 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. Borre las memorias caché del paquete NuGet del sistema local ejecutando dotnet nuget locals all --clear desde un shell de comandos.
  2. Elimine las carpetas bin y obj del proyecto.
  3. Restaure el proyecto y vuelva a compilarlo.
  4. Elimine 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, use 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 aplicación web Blazor, asegúrese de que la ejecuta 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.Count() > 0)
{
    <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