Securizar aplicaciones Windows 8 con ACS

Después de unos cuántos post sobre gestión de identidad y seguridad basada en claims, en esta nueva entrada toca hablar sobre cómo securizar aplicaciones Windows 8, escenario que es algo distinto a los que hemos visto hasta ahora.

En muchos casos, cuando realizamos aplicaciones Windows 8 éstas necesitan hacer uso de servicios de backend, WebAPI por ejemplo, los cuáles pueden estar securizados empleado Windows Azure Active Directory (WAAD) o Windows Azure Access Control, tal y como hemos visto en los post anteriores. Cualquiera de las dos opciones pueden ser válidas para el caso dónde nos encontramos.

En cualquiera de los dos escenarios, la aplicación Windows 8 tendrá que pedir al usuario que se autentique contra el proveedor de identidad configurado para que una vez autenticado la aplicación pueda hacer llamadas a los servicios de backend.

En el post sobre cómo securizar un servicio WebAPI veíamos cómo incluir la seguridad en el servicio y cómo poder hacer un cliente C# que llame a este servicio, pidiendo al usuario las credenciales de autenticación. En ese ejemplo hacíamos uso de Windows Azure Authentication Library para simplicar la seguridad. El código era este:

 

var authContext = new AuthenticationContext("https://estoyenlanube.accesscontrol.windows.net");
AssertionCredential credential = authContext.AcquireToken("http://localhost:29350/");
var token = credential.CreateAuthorizationHeader();

HttpClient httpClient = new HttpClient();
httpClient.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));
httpClient.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Authorization", token);

var response = await httpClient.GetStringAsync("http://localhost:29350/api/values");

Si intentamos hacer lo mismo en una aplicación Windows 8, veremos cómo también está disponible la librería Windows Azure Authentication Library, pero con una gran diferencia, que ésta sólo funciona si el servicio WebAPI está securizado usando Windows Azure Active Directory, si usamos ACS, este código NO funcionará!

Entonces, si usamos Windows 8 y WAAD el código será prácticamente igual, salvo que el método AcquireToken nos pedirá un ClientID que podemos encontrar dentro del portal de Windows Azure.

y entonces….Qué pasa si usamos ACS para la securización? Pues que el tema se complica un poco; Tendremos que hacer uso del WebAuthenticationBroker y hacer alguna pequeña modificación en nuestro servicio WebAPI.

A modo de resumen, el proceso de autenticación que implementaremos será el siguiente:

  • La aplicación Windows 8 usará WebAuthenticationBroker, pidiendo a ACS que autentique el usuario.
  • El usuario tiene que introducir las credenciales para el proveedor de identidad configurado.
  • Una vez que el ACS autentica al usuario, éste le debe redirigir a un controlador del servicio WebAPI.
  • El servicio WebAPI generará un token de autenticación, que devolverá a a la aplicación Windows 8.
  • La aplicación Windows 8 podrá usar este token para hacer las llamadas al servicio WebAPI.

Ahora el código…

En la aplicación Windows 8 el código sería similar al siguiente:

 

WebAuthenticationResult webAuthenticationResult = await WebAuthenticationBroker.AuthenticateAsync(
                WebAuthenticationOptions.None,
                new Uri("https://estoyenlanube.accesscontrol.windows.net:443/v2/wsfederation?wa=wsignin1.0&wtrealm=https://estoyenlanube.azurewebsites.net/&wreply=https://estoyenlanube.azurewebsites.net/api/federation"),
                new Uri("https://estoyenlanube.azurewebsites.net/api/federation/end"));

            if (webAuthenticationResult.ResponseStatus == WebAuthenticationStatus.Success)
            {
               

 

En este ejemplo, el namespace del ACS es “estoyenlanube”, mientras que el servicio WebAPI lo tengo desplegado en https://estoyenlanube.azurewebsites.net. Lo tengo desplegamos en un servidor real porque ACS no es capaz de redirigir a direcciones localhost.

https://estoyenlanube.accesscontrol.windows.net:443/v2/wsfederation?wa=wsignin1.0&wtrealm=https://estoyenlanube.azurewebsites.net/&wreply=https://estoyenlanube.azurewebsites.net/api/federation

Con esta URL le estamos pidiendo al WebAuhenticationBroker que autentique usando el namespace estoyenlanube y el relaying party https://estoyenlanube.azurewebsites.net, Así mismo, le decimos que una vez autentique al usuario redirija la petición a un controlar del servicio WebAPI, el cuál generará el token.

El servicio WebAPI recibirá la petición y generará el token para que el cliente Windows 8 lo obtenga:

public class FederationController : ApiController
    {
        [HttpPost]
        public HttpResponseMessage Post()
        {
            var response = this.Request.CreateResponse(HttpStatusCode.Redirect);
            response.Headers.Add("Location", "/api/federation/end?token=" + ExtractBootstrapToken());
            return response;
        }

        protected virtual string ExtractBootstrapToken()
        {
            var bootstrapContext = ClaimsPrincipal.Current.Identities.First().BootstrapContext as BootstrapContext;
            JWTSecurityToken jwt = bootstrapContext.SecurityToken as JWTSecurityToken;
            return jwt.RawData;
        }
    }
 
 

En el fichero de configuración del servicio WebAPI también es necesario cambiar la configuración para que el controlador pueda generar el token:

<identityConfiguration saveBootstrapContext="true">

Finalmente, el cliente Windows 8, si la autenticación es correcta, podrá obtener el valor del parámetro token y usarlo para realizar las llamadas correspondientes al servidor:

var token = webAuthenticationResult.ResponseData.Substring(webAuthenticationResult.ResponseData.IndexOf("token=", StringComparison.Ordinal) + 6);

        HttpClient httpClient = new HttpClient();

        httpClient.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));

        httpClient.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Authorization", "Bearer " + token);

        var response = await httpClient.GetStringAsync("http://localhost:29350/api/values");

 

Y ya tenemos todo el proceso 🙂

Ibon Landa

bon Landa lleva más de 15 años dedicado al desarrollo de software. Durante este tiempo ha trabajado en diferentes empresas en las cuáles ha podido trabajar en diferentes entornos y tecnologías. Actualmente está focalizado principalmente en tareas de desarrollo, arquitectura, en las herramientas del ciclo de vida y en todo lo relacionado con la plataforma de Cloud Computing Microsoft Azure, área en el que ha sido reconocido como MVP. Participa de forma activa en la comunidad, escribiendo su blog, manteniendo un portal sobre Microsoft Azure y colaborando con Microsoft y grupos de usuarios en eventos de formación, talleres y giras de producto.

Deja un comentario

Tu dirección de correo electrónico no será publicada. Los campos obligatorios están marcados con *