Facebook Connect (iii): Eh! Que esta parte de mi web sólo es para usuarios de facebook! (ASP.NET MVC)

Este post va a ser cortito… En los dos primeros posts de esta serie hemos visto como podemos autenticar (logon) a un usuario de facebook en nuestra web y como podemos desautenticarlo (logoff).

Yo uso ASP.NET MVC para mis desarrollos web, no voy a enumerar ahora las ventajas que en mi opinión tiene MVC sobre Webforms, sinó comentaros como podemos evitar el acceso de usuarios que no estén autenticados en facebook a ciertas regiones de nuestra web.

En ASP.NET MVC tenemos lo que se llaman filtros. A grandes rasgos un filtro es código que se ejecuta antes y/o después de cada petición y que puede alterar el comportamiento por defecto de la petición. Digamos que es un modo rápido y sencillo de aplicar técnicas AOP en aplicaciones MVC. Los filtros se enganchan a las peticiones a las que afectan mediante el uso de atributos (que se aplican a las acciones de los controladores, en MVC cada petición es atendida por una accion _método_ de un controlador).

Qué cosas podemos hacer con filtros? Pues imaginad: hacer log de las peticiones, tratar los errores que se den de una forma coherente, comprimir el resultado de la petición, o porque no, validar que el usuario esté autenticado.

Ya hay en ASP.NET MVC, un filtro que comprueba si el usuario está autenticado:

[Authorize]
public ActionResult ChangePassword()
{
ViewData["PasswordLength"] = MembershipService.MinPasswordLength;
return View();
}

El filtro Authorize comprueba que el usuario está autenticado, y si no es el caso lo redirige a la página de login. De esta manera la acción ChangePassword sólo puede ser ejecutada por usuarios autenticados.

En mi caso, los usuarios pueden entrar en mi web usando una cuenta de facebook o bien una cuenta propia de mi web (autorización estándard de ASP.NET). Pero hay ciertas acciones que sólo están disponibles si el usuario se autentica via facebook. Así que me puse manos a la obra para crear un filtro parecido a [Authorize] pero que validase si el usuario está autenticado en facebook.

Creando el filtro…

Crear un filtro es realmente sencillo… el único punto delicado es saber que tipo de filtro queremos crear, ya que existen cuatro tipos:

  1. Authorization filters: Para implementar autorizaciones
  2. Action filters: Para implementar acciones a realizar antes o después de una acción de un controlador.
  3. Result filters: Para implementar acciones a realizar antes o después de que se haya ejecutado la vista asociada a una acción.
  4. Exception filters: Para gestionar errores producidos durante el tratamiento de una petición.

Para crearnos nuestro propio filtro derivamos de la clase FilterAttribute e implementamos una interfaz u otra. En mi caso, dado que quiero implementar un authorization filter debo implementar la interfaz IAuthorizationFilter, así que manos a la obra!

La interfaz es muy sencilla: tiene un sólo método:

void OnAuthorization(AuthorizationContext filterContext)

En este método es donde comprobaremos que el usuario está autorizado. Si no lo está usaremos las propiedades del objeto filterContext para modificar la respuesta y generar una respuesta nueva (p.ej. una redirección). En caso que el usuario esté autorizado no hacemos nada y dejamos que se procese la acción del controlador.

El código para saber si un usuario está autenticado en facebook, es muy simple:

ConnectSession cs = new ConnectSession(appKey, secretKey);
if (!cs.IsConnected())
{
// No está conectado via facebook
}

Lo que yo quiero es redirigir los usuarios no autenticados via facebook a otra acción. En lugar de redirigirlos a la acción de login (como hace [Authorize]) yo quiero poder decidir en cada caso a que acción redirigir el usuario. Es decir, poder tener algo como:

[FacebookAuthorize("Index", "Home")]
public ActionResult LinkFacebookUser()
{
return View();
}

Si el usuario no está autorizado en facebook, quiero redirigir la petición a la acción Index del controlador Home.

El código base del método OnAuthorization queda tal y como sigue:

public void OnAuthorization(AuthorizationContext filterContext)
{
string appKey = this.AppKey ?? ConfigurationManager.AppSettings["ApiKey"];
string secretKey = this.SecretKey ?? ConfigurationManager.AppSettings["Secret"];

ConnectSession cs = new ConnectSession(appKey, secretKey);
if (!cs.IsConnected())
{
// Redirigir usuario
}
}

Sólo nos queda lo más “fácil”: redirigir el usuario en caso de éste no se haya autenticado via facebook!

Redirigiendo el usuario

Para redirigir el usuario debemos crear un nuevo resultado y asignarlo a la propiedad Result del objeto filterContext recibido como parámetro. No hay ningún resultado de tipo “RedirectToActionResult”, en su lugar tenemos que usar el tipo RedirectToRouteResult, de la siguiente manera:

var rvalues = new RouteValueDictionary();
rvalues["action"] = this.Action;
if (!string.IsNullOrEmpty(this.Controller))
{
rvalues["controller"] = this.Controller;
}

filterContext.Result = new RedirectToRouteResult(rvalues);

Creamos un RouteValueDictionary y asignamos las entradas “action” y “controller” a la acción y controlador donde queremos redirigir el usuario, y a partir de este RouteValueDictionary creamos el RedirectToRouteResult que asignamos a la propiedad Result.

Y listos! Con esto ya tenemos nuestro propio filtro creado!

Os dejo aquí el código fuente completo, para que lo podáis examinar libremente!

/// <summary>
/// ActionFilter de MVC per redirigir la petició a un altre vista si l'usuari
/// no està autenticat a Facebook
/// </summary>
public class FacebookAuthorize : FilterAttribute, IAuthorizationFilter
{

/// <summary>
/// Clau pública de l'API
/// </summary>
public string AppKey { get; set; }

/// <summary>
/// Clau privada de l'API
/// </summary>
public string SecretKey { get; set; }

/// <summary>
/// Controlador al qual es transfereix la petició (<c>null</c> significa
/// el controlador actual).
/// </summary>
public string Controller { get; private set; }

/// <summary>
/// Acció a la que es transfereix la petició
/// </summary>
public string Action { get; private set; }

/// <summary>
/// Construeix un objecte <c>FacebookAuthorize</c> que redirigirà la petició
/// a l'acció <paramref name="action"/> del controlador actual si l'usuari no
/// està autenticat a facebook.
/// </summary>
/// <param name="action">Acció a la qual cal redirigir.</param>
public FacebookAuthorize(string action)
{
this.Action = action;
}

/// <summary>
/// Construeix un objecte <c>FacebookAuthorize</c> que redirigirà la petició
/// a l'acció <paramref name="action"/> del controlador <paramref name="controller" />
/// si l'usuari no està autenticat a facebook.</param>
/// </summary>
/// <param name="action">Acció a la qual cal redirigir.</param>
/// <param name="controller">Controlador al qual cal redirigir.</param>
public FacebookAuthorize(string action, string controller)
{
this.Action = action;
this.Controller = controller;
}

/// <summary>
/// Valida la petició
/// </summary>
/// <param name="filterContext">Contexte de ASP.NET MVC</param>
public void OnAuthorization(AuthorizationContext filterContext)
{
string appKey = this.AppKey ?? ConfigurationManager.AppSettings["ApiKey"];
string secretKey = this.SecretKey ?? ConfigurationManager.AppSettings["Secret"];

ConnectSession cs = new ConnectSession(appKey, secretKey);
if (!cs.IsConnected())
{
var rvalues = new RouteValueDictionary();
rvalues["action"] = this.Action;
if (!string.IsNullOrEmpty(this.Controller))
{
rvalues["controller"] = this.Controller;
}

filterContext.Result = new RedirectToRouteResult(rvalues);
}
}
}

Un saludo!!!!

PD: Al final el post no ha sido tan cortito… aiinsss! Si es que cuando empiezo a escribir no paro!! 😛

6 comentarios sobre “Facebook Connect (iii): Eh! Que esta parte de mi web sólo es para usuarios de facebook! (ASP.NET MVC)”

Deja un comentario

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