Como hacer seguros tus servicios WebApi

Buenas! Este post surge a raíz del comentario de Felix Manuel en mi post anterior Inyección de dependencias per-Request. En él Felix comentaba que le gustaría ver algo sobre autenticación y autorización de WebApi… así que vamos a ello.

Todo lo que comentaré en este post va destinado a servicios WebApi que queramos tener en internet. No hablaré nada de otros escenarios como intranets que pueden tener otras soluciones.

Vamos a partir del template de proyecto ASP.NET MVC4 – WebApi

AuthorizeAttribute

En ASP.NET MVC si queremos añadir seguridad a un controlador, basta con que usemos [Authorize] para decorar todas aquellas acciones que requieran seguridad.

¿Qué ocurre si aplicamos lo mismo a WebApi?

public class SecureController : ApiController

{

    [Authorize]

    public string GetSecure()

    {

        return "Acces granted!";

    }

}

Asegúrate de usar el AuthorizeAttribute que está en System.Web.Http, no el que está en System.Web.Mvc!

Con esto, si realizo una llamada GET a /api/secure obtengo un mensaje de error (recuerda que puedes recibirlo en json o xml dependiendo de la cabecera accept que mandes):

<Error><Message>Authorization has been denied for this request.</Message></Error>

Bien… parece que el filtro realiza su trabajo, pero ¿qué hace exactamente? Para ello echemos un vistazo a su código fuente. El método que realiza el trabajo es IsAuthorized que tiene un código como el siguiente:

IPrincipal user = Thread.CurrentPrincipal;

if (user == null || !user.Identity.IsAuthenticated)

{

    return false;

}

Bueno… pues parece que está claro: [Authorize] se limita a validar que haya un CurrentPrincipal que esté autorizado.

¿Así que la pregunta nos rebota: quien crea y coloca el IPrincipal autorizado?

Para verlo, realicemos una prueba: Vamos a crear un controlador nuevo con un método GET (para llamarlo fácilmente desde el navegador) para autenticarnos, usando FormsAuthentication (el mecanismo clásico de autenticación de ASP.NET).

public class AuthController : ApiController

{

    public string Get(string id)

    {

        FormsAuthentication.SetAuthCookie(id ?? "FooUser", false);

        return "You are autenthicated now";

    }

}

Vale, ahora puedes probar de realizar desde el navegador:

  1. Una llamada a /api/Auth/XXX (XXX nombre de usuario que quieras)
  2. Otra llamada a api/Secure y ver… como sigues sin estar autenticado.

La llamada  a FormsAuthentication lo que hace es colocar una cookie (llamada comúnmente ASPXAUTH) que es la cookie de autenticación de asp.net. Pero por alguna razón la estamos obviando. Bien, esta razón es que NO hemos definido modelo de seguridad en web.config. Si lo abres verás que hay la línea:

<authentication mode="None" />

Modificala para que sea:

<authentication mode="Forms" />

¡Y listos! Si repites el par de llamadas anterior, ahora verás como la llamada a /api/Secure si que te funciona, ya que ahora el proveedor de autenticación Forms utiliza la cookie y en base a ella crea un IPrincipal y lo autentica.

¿Qué, sencillo no? Pues no tanto…

Cuando usar y cuando no usar autenticación por Forms

Usar la autenticación Forms NO es mala idea si tus servicios WebApi van a ser utilizados tan solo desde el contexto de una aplicación web que antes haya autenticado al usuario. ¿Y por qué? Bueno… pues porque las cookies son algo muy del mundo web.

Pero si creas una API para ser usada en otros dispositivos o aplicaciones (p. ej. una aplicación nativa de móvil) usar cookies para autenticarnos es una mala idea, ya que entonces obligas a quien usa tus servicios a que ponga código para leer las cookies recibidas y enviarlas de nuevo. Eso, si usas un navegador, lo hace el navegador automáticamente.

Así que lo dicho: si tus servicios WebApi van a ser utilizados tan solo dentro de una aplicación web (p. ej. para darte soporte a llamadas Ajax) puedes usar el mecanismo que acabamos de ver. En este caso, habitualmente, los controladores WebApi se despliegan en el mismo proyecto que los de ASP.NET MVC (si no recuerda que deberás lidiar con cross-domain). Y quien autentica al usuario (o sea quien tiene la llamada a FormsAuthentication.SetAuthCookie) es un controlador de MVC (el que responde al formulario de Login, claro). Y los controladores WebApi los tienes decorados tan solo con el [Authorize].

Pero vamos a olvidarnos de este escenario y planteemos otro: estás creando simplemente una API. El consumidor de esta API va a ser una aplicación de móvil (p. ej.) o dicho de otro modo, no va a ser un browser.

Entonces NO debemos usar autenticación por Forms y debemos buscar otros mecanismos. La pregunta es… ¿Cuales?

Autenticación básica

Bueno… exploremos esta opción. La autenticación básica de HTTP es muy sencilla. Aunque tiene un flujo que empieza por mandar un 401 con una cabecera específica todo eso lo obviaremos (eso es porque los navegadores sepan que hay un proceso de autenticación pero en nuestro caso el cliente no es un navegador).

Asi, si nos centramos en como es una petición autenticada que use autenticación básica, es muy sencillo: Tiene una cabecera Authorization con este formato:

Authorization: Basic username:password

La única salvedad es que username:password se envía codificado en BASE64.

Vale… ¿y como implementamos eso en WebApi? Pues hay varias maneras, p. ej. nos podríamos crear un filtro de autorización propio:

public class BasicAuthorizeAttribute : AuthorizationFilterAttribute

{

    public override void OnAuthorization(HttpActionContext actionContext)

    {

        var headers = actionContext.Request.Headers;

        if (headers.Authorization != null && headers.Authorization.Scheme == "Basic")

        {

            try

            {

                var userPwd = Encoding.UTF8.GetString(Convert.FromBase64String(headers.Authorization.Parameter));

                var user = userPwd.Substring(0, userPwd.IndexOf(‘:’));

                var password = userPwd.Substring(userPwd.IndexOf(‘:’) + 1);

                // Validamos user y password (aquí asumimos que siempre son ok)

            }

            catch (Exception)

            {

                PutUnauthorizedResult(actionContext, "Invalid Authorization header");

            }

        }

        else

        {

            // No hay el header Authorization

            PutUnauthorizedResult(actionContext, "Auhtorization needed");

        }

    }

 

    private void PutUnauthorizedResult(HttpActionContext actionContext, string msg)

    {

        actionContext.Response = new HttpResponseMessage(HttpStatusCode.Unauthorized)

        {

            Content = new StringContent(msg)

        };

    }

}

Nota: Para realizar tareas de autorización (como este caso) debemos usar un filtro de autorización. No uses para ello un filtro de acción (es decir, no derives de ActionFilterAttribute y redefinas OnActionExecuting). La razón es que los filtros de autorización se ejecutan antes que los filtros de acción, ya que precisamente su tarea es esa: autorizar la llamada a una acción. No está de más que te descargues el poster con el ciclo de vida de una petición WebApi de http://www.microsoft.com/en-us/download/details.aspx?id=36476

Ahora tan solo debemos decorar la acción Secure del controlador con [BasicAuthorize] en lugar de [Authorize] y ya tenemos la autenticación básica implementada (para pasar de cadena a base64 puedes usar cualquiera de las páginas que hay por ahí que lo hacen como http://www.motobit.com/util/base64-decoder-encoder.asp):

image

Si eliminar el tag Authorization verás como la respuesta pasa a ser un 401.

Por supuesto, la autenticación básica de HTTP es tan insegura que tan solo debería usarse sobre HTTPS.

Vale… pero quizá te estás preguntando si tenemos más alternativas que usar un filtro de autorización para validar si una petición está autorizada. Y la respuesta es que sí. Tenemos al menos dos más:

  1. Usar un Message Handler
  2. Usar un módulo de autenticación de ASP.NET

¿Te parece si exploramos ambas?

Usando un Message Handler

Los Message Handlers se ejecutan incluso antes que los filtros de autorización. Es más, se ejecutan incluso antes de que el pipeline de WebApi seleccione un controlador así que son un lugar propicio para poner código de autorización.

Los Message Handlers tienen la potestad de generar ellos mismos un resultado y entonces todo el resto de la pipeline de WebApi es eliminada. Es decir es posible que un Message Handler intercepte una petición, genere un resultado y entonces esta petición no será transferida al controlador a la cual debería haber llegado.

Esto puede parecer que hace que los Message Handlers sean un buen sitio para autorizar peticiones y lo son, pero NO son un buen sitio para rechazar peticiones no autorizadas. Me explico: sigamos con nuestro ejemplo de autenticación básica.

Si usas un Message Handler y rechazamos todas las peticiones que NO tengan la cabecera Authorize entonces tendremos un problema si necesitamos tener alguna parte de la API pública. Un Message Handler se ejecuta siempre. Para todas las peticiones.

Si quieres usar un Message Handler para autorización lo que debes hacer no es rechazar las peticiones que estén autorizadas (en nuestro caso que no tengan la cabecera Authorize). No. En su lugar debes autorizar aquellas peticiones válidas. ¿Como? Colocando un IPrincipal en el Thread. Y luego, en los controladores utilizas [Authorize].

Veamos un ejemplo rápido:

public class BasicAuthMessageHandler : DelegatingHandler

{

    protected override Task<HttpResponseMessage> SendAsync(HttpRequestMessage request,

        CancellationToken cancellationToken)

    {

        var headers = request.Headers;

        if (headers.Authorization != null && headers.Authorization.Scheme == "Basic")

        {

            var userPwd = Encoding.UTF8.GetString(Convert.FromBase64String(headers.Authorization.Parameter));

            var user = userPwd.Substring(0, userPwd.IndexOf(‘:’));

            var password = userPwd.Substring(userPwd.IndexOf(‘:’) + 1);

            // Validamos user y password (aquí asumimos que siempre son ok)

            var principal = new GenericPrincipal(new GenericIdentity(user), null);

            PutPrincipal(principal);

        }

 

        return base.SendAsync(request, cancellationToken);

    }

 

    private void PutPrincipal(IPrincipal principal)

    {

        Thread.CurrentPrincipal = principal;

        if (HttpContext.Current != null)

        {

            HttpContext.Current.User = principal;

        }

    }

}

Este Message Handler crea y coloca un GenericPrincipal si la cabecera Authorize está presente. Luego en el controlador debemos usar [Authorize] para aquellas acciones que requieran de usuario autenticado.

Acuerdate de registrar el Message Handler desde Application_Start:

config.MessageHandlers.Add(new BasicAuthMessageHandler());

Por lo tanto estamos usando una combinación entre un Message Handler y un filtro de autorización (Authorize).

Usando un módulo de autenticación

Bien… exploremos esta otra alternativa. Ahora vamos a usar un módulo de autenticación (un HttpModule) para realizar las mismas tareas que realiza el Message Handler, es decir para crear un IPrincipal.

¿Y qué diferencias hay entre usar un HttpModule o un Message Handler? Pues buena pregunta, básicamente las siguientes:

  1. Un HttpModule es algo específico de IIS. No lo puedes usar en aplicaciones WebApi que no estén hospedadas en IIS. Por otro lado un Message Handler es algo propio de WebApi.
  2. Un HttpModule ve todas las peticiones que estén dirigidas al pipeline de ASP.NET. Sean peticiones de WebApi o no. Un Message Handler tan solo ve las peticiones WebApi.
  3. Un HttpModule se ejecuta antes en el pipeline que un Message Handler. De hecho se ejecuta antes que cualquier parte de WebApi.

Por norma general, es preferible usar un HttpModule si sabes que vas a hospedar tu WebApi siempre en un IIS. Si no, si quieres tener la posibilidad de hospedar tu WebApi en otros procesos entonces usa un Message Handler.

¿Listo para nuestro módulo de autenticación? Aquí lo tienes:

public class BasicAuthModule : IHttpModule

{

     public void Init(HttpApplication context)

     {

         context.AuthenticateRequest += OnAuthenticateRequest;

     }

 

     private void OnAuthenticateRequest(object sender, EventArgs e)

     {

         var application = (HttpApplication)sender;

         var request = new HttpRequestWrapper(application.Request);

 

         var headers = request.Headers;

         var authData = headers["Authorization"];

         if (!string.IsNullOrEmpty(authData))

         {

             var scheme = authData.Substring(0, authData.IndexOf(‘ ‘));

             var parameter = authData.Substring(scheme.Length).Trim();

             var userPwd = Encoding.UTF8.GetString(Convert.FromBase64String(parameter));

             var user = userPwd.Substring(0, userPwd.IndexOf(‘:’));

             var password = userPwd.Substring(userPwd.IndexOf(‘:’) + 1);

             // Validamos user y password (aquí asumimos que siempre son ok)

             var principal = new GenericPrincipal(new GenericIdentity(user), null);

             PutPrincipal(principal);

         }

     }

 

     public void Dispose()

     {

 

     }

 

     private void PutPrincipal(IPrincipal principal)

     {

         Thread.CurrentPrincipal = principal;

         if (HttpContext.Current != null)

         {

             HttpContext.Current.User = principal;

         }

     }

}

Puedes ver como el código es muy parecido al del Message Handler. Ahora debemos registrarlo. Los HttpModules se registran en el web.config, dentro de la sección modules de system.webServer:

<system.webServer>

  <modules>

    <add name="BasicAuthHttpModule"

      type="MvcSecureDemo.BasicAuthModule, MvcSecureDemo"/>

  </modules>

</system.webServer>

La situación es la misma que teníamos en el caso del Message Handler: en los controladores debemos usar [Authorize] para proteger las acciones que requieran usuarios autenticados.

Más allá de autenticación básica

Vale… hemos explorado como securizar nuestros servicios WebApi (a través de filtros de autorización, Message Handlers y HttpModules). Lo hemos hecho a partir de la autenticación básica de HTTP porque es muy sencilla y así el código no queda liado. 😉

Pero autenticación básica NO es un buen mecanismo. No lo es, a menos que uses HTTPS, ya que el login y el password viajan en texto plano (recuerda que Base64 no es cifrado).

Bien, ¿qué debéríamos hacer si no tenemos HTTPS? Una solución pasa por encriptar realmente el login y el password. Esto sigue teniendo un riesgo, tan pequeño como seguro sea nuestro algoritmo de encriptación y segura esté la clave que usemos).

Exploremos otra alternativa, otra que signifique que ni el login ni el password viajan por la red. Nadie con un sniffer será capaz de saber nuestra password escuchando los mensajes que nos intercambiamos con el servidor… Veamos que deberíamos hacer.

Partamos de la suposición de que tanto el cliente (una aplicación móvil p. ej.) y el servidor comparten un código, llamésmole código de acceso. Da igual como lo han compartido (p. ej. a través de un email). Lo importante es que lo tienen.

Entonces básicamente si el cliente adjunta este código en una cabecera (sigamos suponiendo la misma cabecera Authorization) la llamada se considera autenticada y en caso contrario se considera válida. Este código (insisto: obtenido previamente) identifica al cliente (la aplicación móvil) y al usuario de dicho cliente, por lo que adjuntando este código una aplicación puede hacer peticiones en nombre del usuario.

Vale, no parece que hayamos progresado mucho verdad: alguien con un sniffer pilla nuestra petición y ya tiene el código. Muy seguro el sistema no parece.

Bueno… sigamos suponiendo. Sigamos suponiendo que además de este código, tanto cliente como servidor comparten otro código. Un código secreto. Lo de secreto viene porque este segundo código jamás viajará por la red. Nunca. Lo comparten al inicio de los tiempos (p. ej. por mail) y luego el cliente se lo guarda y el servidor también.

Ahora lo que debe hacer el cliente es mandar la petición pero usará el código secreto para generar un hash del código de acceso. Y la cabecera Authorization contendrá este hash. Cuando el servidor recibe una petición del cliente debe:

  • Calcular el hash del código de acceso del cliente usando el código secreto (que ambos comparten)
  • Si el resultado es el mismo que ha enviado el cliente, la petición se considera autenticada.

Vale… hay un problemilla ahora. El código secreto debe ser distinto por cada cliente, pero si tan solo recibimos en la cabecera Authorization el hash del código de acceso… ¿como sabemos qué código secreto debemos usar para calcular el hash?. Es decir, como sabemos de qué cliente es la petición.

Pues nada. Hacemos que cliente y servidor comparten otro elemento: un identificador de cliente. Ahora el cliente en la cabecera Authorization mandará su código identificador y el hash de su código de acceso (calculado con su código secreto). Cuando el servidor recibe la petición, sabe de que cliente es (por el identificador de cliente de la cabecera Authorization) y podrá usar el código secreto de este cliente para calcular el hash del código de acceso de este cliente y validar que sea el mismo que el clienté envía.

¿Te parece seguro el sistema ahora? No lo es en absoluto. Alguien que pille una petición podrá hacer lo que quiera, porque tendrá el hash del código de acceso. Estamos igual que al principio… pero a un paso de la solución.

El problema de que si nos pillan una petición luego ya nos puedan suplantar siempre es porque estamos calculando un hash de algo que jamás se modifica: el código de acceso. Porque en lugar de calcular un hash de dicho código no calculamos un hash de una cadena que esté compuesta de:

  1. La URL de destino
  2. Los datos en query string/formdata
  3. El código de acceso

Los datos 1 y2 son variables (distintos por cada petición que hagamos). Así cada petición tendrá un hash distinto. Cuando el servidor reciba la petición calculará esta cadena con la URL, la query string/formdata y el código de acceso del cliente y usará el código secreto del cliente para calcular el hash. Si coinciden la petición queda autenticada.

Ahora si alguien nos pilla una petición con un sniffer, tan solo podrá hacer una cosa: enviarla otra vez al servidor. No podrá modificarla, porque si lo hace (p. ej. modifica la querystring para cambiar un parámetro) automáticamente el hash pasa a ser inválido. Y no puede construir un hash nuevo porque no tiene acceso al código secreto.

Así pues un hacker nos puede pillar peticiones y puede ver su contenido. Hasta puede guardarlas y reenviarlas al servidor más adelante. Pero NO puede crear peticiones falsas en nuestro nombre.

Vayamos un paso más allá. ¿Por qué no adjuntamos el tiempo en que se realiza la petición? Es decir el cliente añade en una cabecera la fecha y hora en que realiza la petición. Y usa este dato también para calcular el hash. Ahora el servidor puede validar que la fecha/hora que envía el cliente sea (aproximadamente) la actual. Si el servidor recibe una petición de un cliente con una fecha/hora de hace 4 minutos, la puede aceptar pero si es de hace 1 hora la puede rechazar, ya que seguramente es una petición interceptada, guardada y mandada luego. Dado que la fecha/hora se usa también para calcular el hash, el hacker que nos intercepte la petición no puede falsear este dato.

Incluso podríamos hacer algo más: añadir un número (llamésmole nonce) de secuencia. La idea es que dos peticiones del mismo cliente jamás pueden repetir el nonce. Por supuesto el nonce se manda en otra cabecera y también se usa para calcular el hash. Si ahora un hacker nos intercepta la petición y la intenta mandar al cabo de 3 minutos… será rechazada porque este nonce ya ha sido usado.

Ahora estamos protegidos de todos los ataques, excepto de un Man-in-the-middle. Ojo, no tenemos privacidad (tanto las peticiones como las respuestas van sobre HTTP en texto llano), pero nuestras credenciales están seguras y nadie puede crear peticiones en nuestro nombre ni guardarse peticiones válidas y enviarlas más adelante. Si requerimos privacidad y protección contra ataques tipo Man-in-the-middle entonces lo más rápido es usar HTTPS.

Por supuesto este sistema que he descrito, no me lo he inventado yo. Este sistema lo inventó Blain Cook y le dio el nombre de oAuth. Lo que he descrito aquí es (rápido y mal, por supuesto) el flujo básico de oAuth 1.0.

Las ventajas de oAuth son que en ningún momento las credenciales del usuario circulan por la red y que ofrece un buen balance de seguridad siempre y cuando el código secreto no se vea comprometido. Es pues una muy buena opción para proteger tus servicios WebApi.

Y por supuesto, usar otros mecanismos de autenticación no modifica en nada lo dicho en este post, tan solo “complica” el código que has de poner en tu filtro de autorización, message handler o HttpModule.

Un saludo!

11 comentarios en “Como hacer seguros tus servicios WebApi”

  1. Excelente post!
    En algunas aplicaciones que necesitan “exponer datos” lo hago con webapi y con la “idea de oAuth” que explicas.
    Como es de puro conocimiento es la tecnica que utilizan la mayoria de las API publica de los servicios mas conocidos (Facebook, Twitter, etc) un key (identificador) y un secret (para encriptar los datos)

  2. Realmente un gran post me gustaría añadir un par de comentarios, 1. siempre que se deban enviar datos sensibles (y no solo aplica para web api) deberiamos usar https, 2. lo del hash es una técnica comunmente usada, y no esta mal, sin embargo he visto como en estos días aún se sigue usando MD5 que ya esta roto (deberiamos usar SHA512), otro error común es que no crean hash diferentes, siempre usan el mismo, y para este punto tu ejemplo es excelente!

    Gracias por compartir tu conocimiento, saludos!

  3. Eduard, enhorabuena por el blog. Es un gustazo leer este tipo de material y con este nivel tan alto en español.

    Tengo unas preguntas: si usamos SSL para seguridad, entendido como el sistema más fiable, de qué depende el uso de uno u otro método de autenticación (básica, HTTP handlers, etc? Me refiero usando IIS, Microsoft en estado puro. Tu usas la plantilla de MVC como base para tu ejemplo, no sería suficiente con eso? Te digo esto porque hay tantos blogs con mil explicaciones con diferentes métodos que al final es demasiada información la que manejar, y entiendo que habrá razones para elegir uno u otro método, porque si no, se añade una complejidad innecesaria al proyecto.

    Qué hay de los sistemas de token? Seguro que conoces Thinktecture.IdentityModel, qué opinión te merece?

    Muchas gracias y sigue así, aquí tienes un nuevo seguidor!

  4. @Beni
    El problema de la autenticación básica es que login y password viajan sin encriptar por la red (en Base64). Eso lo hace totalmente inutilizable en escenarios donde NO haya https. Con https NO hay problema alguno en utilizar autenticación básica, ya que dado que el canal ya está encriptado, no necesitamos encriptar o hashear de nuevo la información.

    Hay tres elementos que debemos diferenciar:
    1. El canal usado (básicamente http o https)
    2. El protocolo de autenticación que usemos (autenticación básica, digest, oauth, etc)
    3. La implementación de dicho protocolo (filtro, httpmodule, message handler).

    En este post me he centrado en el punto 3, es decir cuento COMO implementar técnicamente cualquier protoclo de autenticación. Las 3 opciones fundamentales (en webapi bajo IIS) son las 3 que menciono: filtro, httpmodule o message handler).

    Por lo tanto puedes implementar autenticación básica con cualquiera de estos 3 mecanismos al igual que puedes implementar oauth con cualquiera de esos 3 mecanismos. Como digo en el post usar un filtro NO es buena idea ya que el filtro debe autorizar peticiones y no autenticarlas. Así que a la práctica nos quedan 2 opciones: message handlers o httpmodule. En un escenario con IIS un httpmodule es la mejor forma. En un escenario con self-hosted webapi no tenemos httpmodule así que nos toca tirar con message handlers. La ventaja de un httpmdoule versus un message handler es que el primero se ejecuta antes de todo el pipeline de webapi, así que puede autenticar también otras peticiones.

    La plantilla de MVC como base (aplicación internet) utiliza el método de autenticación basado en forms, que es un método basado en cookies. Este método está bien para la web (los navegadores envían las cookies automáticamente) pero no para otros clientes que no entienden de cookies. Recuerda que [Authorize] NO autentica, autoriza que no es lo mismo. Si la petición NO está autenticada [Authorize] la deniega (si no estás ni autenticado es obvio que no puedo autorizarte a nada) pero [Authorize] no define como te autenticas (con cookie, con basic, con oauth?).

    Los sistemas de token (como oauth que “describo” al final del post) tienen su uso, en dos escenarios:
    1. Cuando NO hay canal seguro (es decir vamos bajo http) y no podemos transferir las credenciales (login/pwd) por la red.
    2. Cuando, incluso teniendo canal seguro, NO confiamos en el cliente y no queremos que este tenga acceso a nuestras credenciales. Imagina que tu te instalas una aplicación de twitter en tu móvil. Si le das tus credenciales a la aplicación esta puede hacer lo que quiera, y tu a lo mejor (como usuario) no confías en esta aplicación o bien NO quieres otorgarle todos los permisos (p. ej. que solo pueda leer pero no publicar). Eso es posible con sistemas basados en tokens como oauth.
    Sobre Thinktecture.IdentityModel lo conozco pero no lo he usado, así que poco te puedo decir 🙂

    Espero haberte aclarado!!!
    Saludos!

  5. Eduard:

    Sí, me has aclarado bastante. El problema es que yo lo estaba viendo todo como un uno (el login propiamente dicho del sitio web MVC y la seguridad de los servicios web.api) y una cosa es un pantalla de login y otra las peticiones de datos a la API una vez que el usuario está “dentro” de la aplicación. En mi caso, que es MVC+Web.API con IIS, todo con HTTPS, tu ejemplo con HttpModule es perfecto.

    Tan sólo dos comentarios:

    – Cuando dices que el Message Handler hay que registrarlo en Application_Start, no sería en WebApiConfig?
    – Y dos: no he conseguido hacer funcionar el ejemplo con HttpModule por el web.config.

    El primer parámetro en “type” entiendo es el ensamblado? Cuál es el segundo?

    Muchas gracias de nuevo.

    Saludos.

  6. Buenas!
    Si, en el WebApiConfig que se llama tan solo desde del Application_Start (todas esas clases XXXConfig se llaman tan solo desde el Application_Start y son para “organizar” código).

    Sobre lo del web.config, el primer parámetro es el nombre completo de la clase (namespace.clase) y el segundo es el ensamblado.

    Un saludo!

    PD: Si tus servicios WebApi son TAN SOLO para la aplicación web y están contenidos en la misma aplicación web, entonces puedes usar la propia seguridad de Forms para autenticar tus servicios web. Es decir, NO necesitas hacer nada especifico (ni HttpModule, ni Message Handler) ni nada. Si el usuario está autenticado en tu aplicación web, lo estará en tus servicios web (asegúrate tan solo de usar en el web.config. Lo tendrás si has partido del template de “Internet Application” de MVC y NO lo tendrás si has partido del template de WebApi). Si es el caso puedes usar directamente [Authorize] para decorar los servicios web que requieren usuario autenticado (el [Authorize] de WebApi, no el de MVC).

    Saludos!

  7. Excelente post, tengo una pregunta.
    Si quiero implementar OAuth en mis servicios, como realizaría el cálculo del hash de código de acceso tanto del lado del cliente como del servidor?
    Saludos!

  8. Excelente post, explicado con mucha claridad, lo entendí a la primera.
    Tengo una duda cuando explicas el proceso de oAuth, que pasa cuando mi servicios están alojados en servidores con diferente zona horaria a la zona horaria del cliente, esto es transparente o se debe realizar alguna configuración para el calculo del tiempo. Gracias por tu tiempo y compartir tu experiencia.

    Saludos !

    1. Buenas Adrián.
      En general la recomendación es: trabaja siempre en UTC y convierte desde y hacia UTC. Te ahorrarás muchos quebraderos de cabeza.

      Dicho esto, entiendo que cuando dices “cálculo de tiempos” te refieres al cálculo para saber el tiempo en que el cliente hace una petición. Las dos opciones más sencillas son, o que el cliente te envíe las horas en UTC (el cliente sabe su timezone y por lo tanto puede calcular la fecha UTC equivalente) o bien que el cliente te envíe la fecha local y el timezone (y entonces el servidor convierte a un timezone común (idealmente UTC)).

      Saludos!

Deja un comentario

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