Desarrollo seguro con aplicaciones de página única (SPA)

Al desarrollar sistemas distribuidos nativos de la nube, la protección de estos sistemas puede introducir un nuevo nivel de complejidad.

Los sistemas locales se basan en los límites de seguridad que proporciona la red interna y usan los servicios de directorio para la seguridad de los usuarios. Pueden ejecutarse durante muchos años en este entorno seguro sin problemas. El traslado a la nube puede presentar nuevos riesgos de seguridad. En este artículo se describen las herramientas que puede usar para mitigar estos riesgos.

Una de estas herramientas es el control de acceso. El control de acceso identifica a los usuarios y regula lo que pueden hacer al interactuar con una aplicación.

Hay dos partes para el control de acceso:

  • La autenticación identifica al usuario.
  • La autorización determina lo que el usuario puede hacer en la aplicación.

OAuth, un marco abierto, ayuda a abordar estos desafíos y proporciona un protocolo para que los desarrolladores los usen al compilar sus sistemas. OAuth 2.0 es el estándar actual.

OAuth 2.0 proporciona acceso delegado seguro. Mediante la emisión de tokens de acceso, puede autorizar el acceso de terceros a los recursos protegidos sin proporcionar credenciales.

Azure Active Directory (Azure AD) es la solución integrada de Microsoft para administrar identidades en la nube. Se integra con sistemas locales para que los usuarios tengan una experiencia sin problemas al acceder a los servicios de protección en la nube.

En esta guía se muestra cómo usar Azure AD o OAuth 2.0 para proteger una aplicación de página única.

Flujos de OAuth

Los flujos de OAuth cubren muchos casos de uso, todos ellos con el respaldo de Azure AD Services. Los desarrolladores usan estos flujos para crear una aplicación segura, de modo que:

  • Los usuarios pueden acceder de forma segura a los sistemas cliente.
  • Los usuarios invitados pueden participar a través de transacciones de negocio a negocio.
  • Los usuarios pueden llegar a los consumidores finales a través de Azure Business to Consumers (Azure B2C).

Diagrama que muestra el flujo seguro de OAuth 2 entre una aplicación nativa y una API web.

Hay dos flujos de OAuth: concesión implícita y código de autorización. La concesión implícita es la más común, pero se recomienda usar el flujo de código de autorización.

Registro de la aplicación en Azure

Registre una entidad de servicio para la interfaz de usuario y la API con Azure AD Directory en Azure Portal.

  1. Inicie sesión en Azure Portal y, a continuación, busque Registros de aplicaciones.

  2. Seleccione Nuevo registro.

    Captura de pantalla que muestra la página Registro de aplicaciones con Nuevo registro seleccionado.

  3. Para registrar una nueva aplicación, necesita:

    • El nombre para mostrar de la aplicación.
    • El tipo de cuenta admitido.
    • El tipo de aplicación: web, SPA o cliente público o nativo (móvil y escritorio).
    • El URI de redireccionamiento. Cuando se autentica el usuario, Azure AD redirige el resultado al cliente.
      • Un ejemplo para el desarrollo local es http://localhost:4200.
      • Un ejemplo para producción es "https://portal.contoso.com".

    Captura de pantalla que muestra la ventana Registrar una aplicación.

  4. Seleccione Registrar.

  5. Una vez completado el registro, seleccione Información general y, a continuación, seleccione el nombre de la aplicación junto a Aplicación administrada en el directorio local.

    Captura de pantalla que muestra la página de información general con el nombre de la aplicación seleccionado.

  6. Seleccione Propiedades, cambie Asignación de usuario necesaria a para establecer los permisos de acceso para la aplicación y, a continuación, seleccione Guardar.

    Captura de pantalla que muestra la página de propiedades con Asignación de usuario activada.

  7. Seleccione Usuarios y grupos y agregue usuarios y grupos de seguridad nuevos o existentes.

    Captura de pantalla que muestra la página Usuarios y grupos con la opción Agregar usuario o grupo seleccionada.

  8. Los usuarios pueden acceder a la aplicación a través de Aplicaciones.

Configuración de los detalles de configuración en la aplicación cliente

Después de crear y configurar el registro de aplicaciones en Azure, puede configurar los detalles de configuración en la aplicación cliente. Para un marco de una sola página como Angular, Microsoft ha desarrollado la biblioteca @Azure/msal-angular para ayudarle a integrar Azure AD en la aplicación cliente.

  1. Instale una biblioteca @Azure/msal-angular.

  2. Configure la biblioteca.

    • protectedResourceMap contiene una lista de recursos protegidos y sus ámbitos en una matriz: [[recurso protegido], [ámbitos para el recurso]].
    • clientID y authority, que es el identificador de inquilino, se proporcionan al objeto de configuración.
    • Para las solicitudes HTTP protegidas, el cliente inserta una nueva propiedad de encabezado denominada Autorización. Contiene el token de portador para el usuario autenticado. El token de portador proporciona al servicio OAuth 2.0 de bajada un punto de entrada seguro. Puede incluir metadatos para el servicio al autorizar la solicitud.
export const protectedResourceMap: [string, string[]][]] = [
    ['https://graph.microsoft.com/v1.0/me', ['user.read']],
    ['https://localhost:5001/api/weatherforecast', ['api://ae05da8f-07d0-4ae6-aef1-18a6af68e5dd/access_as_user']]
];

function MSALConfigFactory(): Configuration {
    return {
        auth: {
            clientId: 'eba23c0b-1e86-4f68-b1d2-9c54d96083de',
            authority: 'https://login.microsoftonline.com/1c302616-bc6a-45a6-9c07-838c89d55003',
            redirectUri: 'http://localhost:4200',
            validateAuthority: true,
            postLogoutRedirectUri: 'http://localhost:4200',
            navigateToLoginRequestUrl: true
        },
        cache: {
            cacheLocation: 'sessionStorage',

            storeAuthStateInCookie: false //set to false, not ie 11
        }
    };
}

Para obtener más información sobre cómo configurar una biblioteca Angular, consulte Tutorial: Inicio de sesión de usuarios y llamada a Microsoft Graph API desde una aplicación de página única (SPA) de Angular mediante el uso del flujo de código de autenticación.

Prueba de la autenticación de la aplicación

Para probar el proceso de autenticación, haga que un usuario con acceso y un usuario sin acceso intenten iniciar sesión en el cliente.

El usuario inicia sesión en la aplicación y se redirige a su inquilino de Azure AD.

  • Si el usuario es válido, se autentica y se inicia sesión.
  • Si el usuario no es válido, la aplicación devuelve un error.

Consumo de un servidor de recursos o recursos protegidos

Para consumir un recurso protegido, cree otro registro de aplicación. Una vez completado el registro de aplicación, la API cambia el token de portador para permitir el acceso.

Exposición de la API

  1. Cree otro registro de aplicación en Azure.

  2. Seleccione Exponer una API y, a continuación, seleccione Agregar un ámbito.

    Captura de pantalla que muestra la página Exponer una API con Agregar un ámbito seleccionado.

  3. Escriba el URI de id. de aplicación y, a continuación, seleccione Guardar y continuar. La API usa este permiso para validar la solicitud.

    Captura de pantalla que muestra la ventana Agregar un ámbito con un URI de id. de aplicación especificado y la opción Guardar y continuar seleccionada.

  4. Configure el nombre del ámbito y la información de consentimiento. Si selecciona Solo administradores, solo los administradores pueden conceder consentimiento para el directorio.

    Captura de pantalla que muestra la ventana de configuración Agregar un ámbito con valores de ejemplo especificados.

Adición de la API al registro de aplicación

Ahora que ha definido los permisos y expuesto la API, debe agregar la API al registro de la aplicación para el cliente.

  1. En el registro de aplicación, seleccione Permisos de la API y, luego, Agregar un permiso.

    Captura de pantalla que muestra la página de permisos de API con la opción Agregar un permiso seleccionada.

  2. Seleccione Mis API y, a continuación, seleccione el registro de API que ha creado.

    Captura de pantalla que muestra la pestaña Mis API con la API seleccionada.

  3. Seleccione el ámbito que creó para exponer el permiso de API y, a continuación, seleccione Agregar permisos.

    Captura de pantalla que muestra el ámbito seleccionado con el botón Agregar permisos seleccionado.

Ahora la API está agregada a la aplicación. Puesto que es posible que tenga que volver a conceder consentimiento para acceder a la API, considere la posibilidad de conceder el consentimiento del administrador para que los usuarios no tengan que volver a dar su consentimiento.

Captura de pantalla que muestra la API agregada a la aplicación.

Adición de la API a la asignación de recursos protegidos

Ahora que la configuración de Azure Portal está completa, el cliente de la interfaz de usuario puede consumir el recurso. Agregue la API a la asignación de recursos protegidos para asegurarse de que la interfaz de usuario adjunta el token de portador correcto para la solicitud de API.

export const protectedResourceMap: [string, string[]][] = [
    ['https://graph.microsoft.com/v1.0/me', ['user.read']],
    ['https://localhost:5001/api/weatherforecast', ['api://eba23c0b-1e86-4f68-b1d2-9c54d96083de/access_as_user']]
];

Cuando la aplicación cliente intenta acceder al recurso, la biblioteca cliente de MSAL se autentica en Azure AD a través de un iframe oculto y, a continuación, devuelve un token de portador para el recurso. El token de portador solo se agrega para las solicitudes que coinciden con el punto de conexión, en este caso https://localhost:5001/api/weatherforecast.

Si la API que configuró con los registros de aplicaciones pertinentes recibe un token de portador con un URI de id. de aplicación no válido, rechaza la solicitud y devuelve un mensaje 401 no autorizado.

En el ejemplo siguiente, el servicio back-end se escribe en .NET Core. En el ejemplo se muestran las propiedades de configuración de la API. ClientId tiene el URI de id. de aplicación en el formato de api://{clientId}.

"AzureAD": {
  "Instance": "https://login.microsoftonline.com/",
  "Domain": "yourName.onmicrosoft.com",
  "TenantId": "1c302616-bc6a-45a6-9c07-838c89d55003",
  "ClientId": "api://ae05da8f-07d0-4ae6-aef1-18a6af68e5dd"
},

Dentro de la clase de inicio de la API de .NET Core, el esquema de autenticación y las opciones se agregan al método de configuración de servicios.

services.Addauthentication(AzureADDefaults.BearerAuthenticationScheme).AddAzureADBearer(options => Configuration.Bind("AzureAD",
  options));

Cuando el cliente llama a la API, el token de portador se agrega a la solicitud.

Captura de pantalla que muestra el token de portador agregado a la solicitud.

Puede ir a jwt.ms y pegar el token de portador en un formato legible.

Captura de pantalla que muestra el token de portador en un formato legible.

Puede ver que el URI de API está dentro de la propiedad aud. Esta propiedad identifica el destinatario previsto del token, que es la API. Si la API no es el destinatario previsto, rechaza automáticamente la solicitud con una respuesta HTTP 401.

La propiedad scp contiene el conjunto de ámbitos expuestos por la aplicación. Si se agregan ámbitos no válidos a través del cliente, Azure AD devuelve un error que solicita una autorización adicional para el ámbito.

Captura de pantalla que muestra las propiedades del token en el archivo jwt.ms.

Uso del manifiesto de aplicación para definir aún más la autorización

Otras prácticas de autorización se implementan mediante el manifiesto de aplicación para el registro de aplicación de API. Puesto que ha definido explícitamente usuarios, puede agregar más niveles de autorización y permitir que los miembros de un grupo de seguridad específico accedan a recursos más confidenciales.

  1. En el registro de aplicación, seleccione Manifiesto.

    Captura de pantalla que muestra la página de manifiesto.

  2. Edite los pares clave-valor del objeto JSON según sea necesario.

En general, es mejor emitir solo SecurityGroup. Si usa All, se emiten grupos de seguridad, roles de Azure AD y grupos de distribución. El token tiene un límite establecido de 200 y, si se alcanza ese límite, se agrega una notificación de uso por encima del límite. La notificación apunta al punto de conexión de Graph para recuperar la lista de grupos para el usuario.

Después de la configuración, el token jwt tiene una nueva propiedad, groups, que contiene los identificadores de objeto únicos que se pueden usar para aplicar la autorización.

La API se puede configurar con una directiva que busca una notificación y un valor necesarios para la autorización basada en roles a través del controlador de directivas.

public void ConfigureServices(IServiceCollection services)
{

    services.AddAuthentication(AzureADDefaults.BearerAuthenticationScheme).AddAzureADBearer(options => Configuration.Bind("AzureAD",
      options));

    services.AddAuthorization(options =>
    {

        options.AddPolicy("DensuAegisReportsAdmin", policyBuilder =>
        {
            policyBuilder.RequireClaim("groups", "ebde25e7-d254-474e-ae33-cd491aa98ebf"); //This would be an environment variable
        });
});

    JWTSecurityTokenHandler.DefaultMapInboundClaims = false;
    services.AddCors();

    services.AddControllers();
}

El controlador de la API puede tener agregadas las atribuciones pertinentes. Estos atributos ofrecen más seguridad y ayudan a confirmar que las personas autenticadas están autorizadas para acceder al recurso protegido.

[Route("admin")]
[Authorize("DensuAegisReportsAdmin")]
public IActionResult GetForcastsForAdmin()
{
    var user = User.Claims;

    var groups = User.Claims.Where(c => c.Type == "groups").Select(c => c.Value).ToList();
    var userName = UserClaims.Where(c => c.Type == "unique_name").Select(c => c.Value).FirstOrDefault();
    // SecurityGroup = groups
    var rng = new Random();
    var forecasts = Enumerable.Range(1, 5).Select(index => new WeatherForecast
    {
        Date = DateTime.Now.AddDays(index),
        TemperatureC = rng.Next(-20, 55),
        Summary = Summaries[rng.Next(Summaries.Length)],

    })
    .ToArray();

    return Ok(new
    {
        User = userName
        ,
        SecurityGroup = groups
        ,
        Forcasts = forecasts
    });
}

Se pueden crear más roles con el manifiesto de aplicación que son únicos para el registro de aplicación. A continuación, se pueden crear más grupos en el contexto de la aplicación.

Captura de pantalla que muestra un ejemplo de un appRole agregado al manifiesto.

Por ejemplo, puede crear un rol personalizado denominado AppAdmin que sea único para el registro de aplicación. Con la compilación de la aplicación empresarial, puede asignar usuarios o grupos de seguridad a ese rol.

Cuando se llama al recurso protegido después del cambio de configuración, el token de portador tiene la propiedad roles dentro del token de portador.

Captura de pantalla que muestra la propiedad de rol en el token de portador.

La API se configura mediante el generador de directivas en Configurar servicios.

            // Adding authorization policies that enforce authorization using Azure AD roles.
            services.AddAuthorization(options =>
            {
                options.AddPolicy(AuthorizationPolicies.AssignmentToAppAdminRoleRequired, policy =>
                  policy.RequireRole(AppRole.AppAdmin));
            });

La ruta protegida usa la directiva de autorización para asegurarse de que el usuario autenticado está en el rol pertinente antes de autorizar la solicitud.

Pasos siguientes