ASP.NET MVC 5: iniciando sesión con proveedores externos (Google, Twitter, Facebook)

ASP.NET MVC 5: iniciando sesión con proveedores externos (Google, Twitter, Facebook)
Sin comentarios Facebook Twitter Flipboard E-mail

La versión 5 de MVC, no trae una lista de novedades expecialmente extensa. Aunque algunas de ellas son bastante jugosas. Por ejemplo el nuevo sistema para la identificación y autorización de usuarios: ASP.NET Identity.

En anteriores versiones de MVC podíamos utilizar proveedores externos para identificar usuarios sin demasiadas complicaciones. Con una cuenta de Google, Twitter o Facebook, podíamos obtener algunos datos del usuario sin tener que manejar contraseñas.

El problema aparecía cuándo queríamos utilizar los Membership Providers que existían en ASP.NET para gestionar los usuarios que ya habían iniciado sesión con un proveedor externo. Complejos, muy atados a SQL Server y tediosos de personalizar.

Por suerte esto ha cambiado en esta nueva versión. Ahora, con la ayuda de OWIN, solo necesitamos añadir los elementos necesarios para que todo funcione como un reloj.

¿Pero qué es OWIN?

Este no pretende ser un artículo sobre OWIN, así que vamos a resumirlo mucho. Si necesitáis más información podéis encontrarla en la página oficial de OWIN.

OWIN es una especificación abierta, que pretende reducir la dependencia de las aplicaciones del servidor web o de aplicaciones. Un poco como ya hacen node.js o Rack. Antes de OWIN nuestras aplicaciones web estaban montadas sobre una pila parecida a esta:

NoOWIN

Esto implicaba un sistema demasiado monolítico y muy difícil de evolucionar. OWIN trata de evitar estos problemas, con una aproximación diferente.

Con Owin

Los componentes que ahora tenemos son los siguientes:

  • Host: sería la capa más cercana al sistema operativo, encargada de gestionar los procesos que ejecutarán las aplicaciones.

  • Server: componente utilizado para escuchar las peticiones HTTP.

  • Middleware: componente intermedio que se encarga de procesar las peticiones para realizar alguna operación sobre ellas. Aquí entrarían frameworks y componentes como SignalR, Web API, Authentication etc.

  • Aplicación: nuestra aplicación desarrollada que hace uso de los otros elementos.

Lo bueno de estar arquitectura, es que al estar desacoplada, siempre podemos sustituir cualquier componente, por otro que haga la misma función. Por ejemplo como host podemos usar IIS (que también puede hacer de Server), pero también podemos usar OwinHost, que es un simple ejecutable que puede servir de host a nuestras aplicaciones. La idea es que con esta arquitectura podamos conseguir que nuestra aplicación funcione en cualquier plataforma con mucha más facilidad.

Es importante destacar que OWIN es solo una especificación. La implementación concreta de Microsoft de OWIN se llama Katana.

Creando una aplicación con inicio de sesión externo

Hacer esto es muy sencillo con MVC 5. Desde Visual Studio podemos crear un nuevo proyecto ASP.NET de tipo MVC, y elegir como opción de autenticación "cuentas de usuario individuales". De esta manera Visual Studio configurará todo por nosotros, y nos generará todo el código necesario.

Lo malo de hacerlo así, es que no profundizamos lo suficiente. Así que en este caso vamos a hacerlo paso a paso.

¿Qué componentes necesitamos?

Para hacer funcionar nuestro ejemplo, necesitamos instalar desde NuGet los siguientes componentes.

    Install-Package Microsoft.Owin.Host.SystemWeb
    Install-Package Microsoft.AspNet.Identity.Owin
    Install-Package Microsoft.Owin.Security.Twitter 
    Install-Package Microsoft.Owin.Security.Google

Con Microsoft.Owin.Host.SystemWeb, conseguiremos que nuestra aplicación ASP.NET funcione sobre IIS sin preocuparnos de tener que iniciar un Host o un Server.

Security.Twitter y Security.Google, nos proporcionan los middlewares para poder iniciar sesión con esos servicios. También existen middlewares para cuentas Microsoft o Facebook.

Microsoft.AspNet.Identity.Owin, nos proporciona los elementos necesarios para identificar y autorizar a los usuarios en nuestras aplicaciones. El sistema está basado en Claims. Ese tema daría para otra serie de artículos, así que no vamos a explicarlo en profundidad. Podéis encontrar más información en este enlace.

La clase Startup

Con OWIN necesitamos especificar los middlewares que vamos a utilizar. Esto se hace en una clase de inicio, que normalmente se llama Startup. Veamos el código

using Microsoft.AspNet.Identity;
using Microsoft.Owin;
using Microsoft.Owin.Security;
using Microsoft.Owin.Security.Cookies;
using Microsoft.Owin.Security.Google;
using Microsoft.Owin.Security.Twitter;
using Owin;
[assembly: OwinStartup(typeof(WebApplication1.App_Start.Startup))]
namespace WebApplication1.App_Start
{
    public class Startup
    {
       public void Configuration(IAppBuilder app)
       {
           app.UseCookieAuthentication(new CookieAuthenticationOptions
           {
               AuthenticationType = "Cookie",
               LoginPath = new PathString("/Account/Login")
           });
           app.UseExternalSignInCookie(DefaultAuthenticationTypes.ExternalCookie);    
           app.UseTwitterAuthentication(new TwitterAuthenticationOptions {
                ConsumerKey ="",
                ConsumerSecret = ""
           });
           app.UseGoogleAuthentication();
        }
    }
}

La clase Startup se ejecuta al iniciar nuestra aplicación. En el ejemplo, vemos que estamos utilizando el middleware para identificación con cookies (UseCookieAuthentication) y los middlewares para utilizar identificación con Twitter y Google. Con Twitter es imprescindible utilizar unas claves (ConsumerKey y ConsumerSecret), que podemos generar desde la página de desarrolladores de Twitter.

Protegiendo páginas de nuestra aplicación

Lo primero que necesitamos, es crear una página protegida en nuestra aplicación. Una página que requiera autorización para poder acceder a ella. En nuestro caso tenemos un controlador que contiene una página protegida con el atributo Authorize.

    public class HomeController : Controller
    {
        public ActionResult Index()
        {
            return View();
        }
        [Authorize]
        public ActionResult ProtectedPage()
        {   
            ViewBag.UserName = HttpContext.GetOwinContext().Authentication.User.Identity.Name;
            return View();
        }   
    }

Si ejecutamos la aplicación e intentamos acceder a dicha página sin iniciar sesión, seremos redirigidos a la página de Login.

Mostrando las opciones de identificación de usuarios

Cuando se carga la página de Login, es necesario que mostremos los proveedores externos de forma dinámica. Si añadimos otro proveedor más tarde, no tendremos que cambiar nada y se mostrará de forma automática. En nuestro caso, tenemos un controlador, llamado AccountController, que se encarga de toda la gestión de cuentas de usuario. El método encargado de mostrar las opciones de identificación, es el siguiente:

public ActionResult Login(string returnUrl)
{
  ViewBag.returnUrl = returnUrl;
  var authProviders = HttpContext.GetOwinContext().Authentication.GetExternalAuthenticationTypes();
  return View(authProviders);
}

Este método carga el contexto OWIN y accede a los middlewares de identificación que hay registrados. Después se los pasa a la vista, que los presentará en pantalla, en forma de botones. También guarda la dirección original a la que se intentó acceder, para redireccionar al usuario una vez se haya iniciado sesión.

proveedores

El proceso de inicio de sesión

Cuándo se pulsa en alguno de los botones mostrados anteriormente, se llama al siguiente método de AccountController:

[HttpPost]
[AllowAnonymous]
public ActionResult Login(string provider, string returnUrl)
{
  AuthenticationProperties authProperties = new AuthenticationProperties();
  authProperties.RedirectUri =  Url.Action("ExternalLoginCallback", "Account", new { ReturnUrl=returnUrl});
  HttpContext.GetOwinContext().Authentication.Challenge(authProperties, provider);
  return new HttpUnauthorizedResult();
}

Este método recibe el nombre del proveedor con el que se quiere iniciar sesión, que en nuestro caso será Google o Twitter. También registramos la URL a la que se llamará de una vez se haya realizado la operación de inicio de sesión con el proveedor externo.

Una vez tenemos las opciones necesarias configuradas, se inicia el proceso de identificación con el proveedor de forma asíncrona.

inicio sesión

Comprobando si se ha iniciado sesión correctamente

Una vez finalizado el proceso de acceso a Google o Twitter, se devuelve el flujo del programa al método ExternalLoginCallBack.

[AllowAnonymous]
public async Task ExternalLoginCallback(string returnUrl)
{
  var authenticationManager =  HttpContext.GetOwinContext().Authentication;          
  var authenticateResult = await authenticationManager.AuthenticateAsync("ExternalCookie");
  if (authenticateResult == null)
    return RedirectToAction("Login");
  authenticationManager.SignOut("ExternalCookie");
  var claims = authenticateResult.Identity.Claims.ToList();
  var identityClaims = new ClaimsIdentity(claims, "Cookie");
  authenticationManager.SignIn(identityClaims);
  return Redirect(returnUrl);
}

Este método comprueba hemos iniciado sesión correcatmente. Si no es así, seremos redirigidos una vez más a la pantalla de Login.

Si la cookie externa existe, es que el proceso de inicio de sesión ha ido bien. En este caso Twitter nos devuelve los Claims diciendo que somos un usuario de Twitter, que hemos iniciado sesión, y que hemos dado perimiso a la aplicación para acceder a algunos datos. Usamos esos mismos Claims para identificar al usuario en nuestra aplicación. Después redirigimos a la página protegida que en este caso si podrá ser visualizada.

sesión iniciada

En este caso nosotros no estamos realizando operaciones adicionales, ya que nuestra aplicación es muy sencilla. Pero si nuestra aplicación tuviese más funcionalidades, lo normal sería asociar un identificador de usuario a la cuenta del proveedor externo. Así crearíamos una cuenta de usuario en nuestra aplicación, que se utilizaría en futuras visitas del usuario. Lo bueno de ASP.NET Identity, es que es muy sencillo realizar esas operaciones. Podremos usar cualquier base de datos relacional con el esquema que deseemos, o incluso utilizar MongoDB para guardar los datos del usuario. Y eso sin tener que heredar de clases muy complejas como eran los antiguos Membership Provider.

Cerrando sesión

Para finalizar, hemos implementado un método para cerrar sesión. Es muy sencillo:

[Authorize]
public ActionResult SingOut()
{
  HttpContext.GetOwinContext().Authentication.SignOut("Cookie");
  return RedirectToAction("Index", "Home");
}

En este caso no tiene mucho misterio. Simplemente se cierra sesión y se devuelve al usuario a la página de inicio.

Y terminamos aquí. Cualquier aplicación web que hagamos hoy en día, debe permitir iniciar sesión con uno o varios de estos servicios, por lo que si somos desarrolladores ASP.NET no viene mal estar al tanto de como funciona

En GenbetaDev | Tutorial de iniciación en ASP.NET MVC con Visual Studio 2013 , Imagen | BuildArk

Comentarios cerrados
Inicio