Lidiando con oAuth (1/n) – Pseudoautenticacion oAuth

 ·  ☕ 10 min  ·  ✍️ eiximenis

    Nota: Este post ha sido importado de mi blog de geeks.ms. Es posible que algo no se vea del todo "correctamente". En cualquier caso puedes acceder a la versión original aquí

    Muy buenas! Este post es el primero de una serie de “n” donde veremos como podemos lidiar un poco con oAuth 1.0a. Vamos a ver como implementar un cliente y lo más interesante un proveedor.

    Para seguir esta serie de posts recomiendo la lectura del documento “Entendiendo oAuth” que he dejado en Slideshare (http://www.slideshare.net/eduardtomas/entendiendo-o-auth) donde se describe brevemente el procolo oAuth y los distintos flujos asociados a él.

    Comentaros también que he dejado en codeplex una librería que os va a permtir crear de forma extremadamente fácil un proveedor de oAuth para ASP.NET y también un cliente. Podéis encontrar la librería en https://epnukeoauth.codeplex.com/ (de momento está tan solo el código fuente, debéis compilarla vosotros mismos).

    Breve, brevísima, introducción a oAuth

    oAuth es un protocolo de autorización pensado especialmente para aplicaciones web, aunque su uso se ha extendido en aplicaciones móviles y de escritorio. Lo que permite es que una aplicación (cliente) pueda acceder a los recursos de un servidor (usualmente una API estilo REST) en nombre de un determinado usuario, sin que en ningún momento la aplicación cliente tenga acceso a las credenciales de dicho usuario. Para conseguir esto hay un juego a 3 bandas entre la aplicación cliente, el servidor y quien posee les credenciales del usuario (que sí, suele ser el servidor).

    Pongamos twitter como ejemplo. Cuando instalas un cliente de twitter, este cliente debe conectarse a tu cuenta para poder leer tweets y enviar tweets en tu nombre. Para ello el cliente no te solicitará tus credenciales si no que en su lugar:

    1. La aplicación cliente hará una petición al proveedor de servicios pasándole su token de aplicación (que identifica a la aplicación cliente). Este es el inicio de todo, signfica que la aplicación “quiere empezar una negociación oAuth”.
    2. Si el proveedor de servicios acepta la aplicación le mandará un token temporal.
    3. La aplicación cliente abrirá una ventana de navegador hacia una URL de quien pueda autenticar el usuario. En este caso twitter. Y le pasará a twitter este token temporal, diciéndole “Hey! El proveedor de servicios me acepta como cliente (tengo este token que lo demuestra) pero necesito que el usuario se autentique).”
    4. El usuario hará login en twitter con tus credenciales. Insisto: en twitter. En ningún momento la aplicación cliente recibe las credenciales.
    5. Twitter validará las credenciales y si son correctas pedirá que confirmes que das acceso a la aplicación”XXX” a tu cuenta. Si confirmas, se genera un token, el cual es enviado (el mecanismo exacto no importa ahora) a la aplicación cliente.
    6. La aplicación cliente, usa este token para hablar con el proveedor de servicios diciéndole: “Hey! Tengo este token que me han dado con el cual se supone que tengo acceso a los servicios”. En el caso de twitter el proveedor de servicios es el propio twitter.
    7. El proveedor de servicios valida este token y si es correcto, le manda otro token al cliente. Este otro token es el que sirve (durante un tiempo determinado) para hacer llamados a los servicios en nombre del usuario que se autenticó. El resto de tokens pasan a ser inváldos.
    8. El cliente usa este segundo token para hacer llamadas al proveedor de servicios.

    Este es el escenario más complejo de oAuth (el llamado 3-legged). En el fondo cada vez que hemos hablado de “token” hay realmente dos tokens (uno público que se manda junto con la petición y otro secreto que se usa para firmar digitalmente la petición).

    El principal problema a la hora de crear un proveedor de servicios de oAuth es todo el tema de la firma digital, ya que se debe ser extremadamente cuidadoso en este punto. Probablemente el 90% de implementaciones fallidas de oAuth, fallan en este punto.

    Pseudo Autenticación oAuth

    Este sea probablemente uno de los usos más conocidos de oAuth, el de usarlo para “autenticar” un usuario (el famoso botón de sign-in with twitter p.ej.). Vamos a ver como podemos crear una web que tenga una sección privada a la que se pueda acceder a través de un botón de “sign with twitter”. Vamos a usar la librería que os comentaba antes para hacerlo.

    La idea es acceder a una acción de un controlador que sea privada:

    1. public class PrivateController : Controller
    2. {
    3.     [Authorize]
    4.     public ActionResult Index()
    5.     {
    6.         return View();
    7.     }
    8. }

    Fijaos que la acción está marcada como privada de la forma estándard en ASP.NET MVC (usando [Authorize]). Si intentamos navegar a /Private/Index al no estar autenticados en ASP.NET MVC seremos redirigidos a la vista de login:

    image

    La vista muestra un formulario de login estándard (eso seria para entrar sin usar twitter, vamos a obviar este punto) y un enlace para entrar usando twitter. Si miramos el código fuente de la vista /Views/Account/LogOn.cshtml el enlace para usar twitter llama a la acción LogOnTwitter del controlador Account. Esta es la acción que empieza todo el flujo de oAuth. Veamos primero su código:

    1. public ActionResult LogOnTwitter()
    2. {
    3.     var oauthSession = new OAuthClientSession(ConfigurationManager.AppSettings["consumer-key"],
    4.       
      60; ConfigurationManager.AppSettings["consumer-secret"],
    5.         new NonceGenerator32Bytes());
    6.     var uri = ConfigurationManager.AppSettings["request_token"];
    7.     oauthSession.RequestTemporaryCredentials(uri, "POST", "http://127.0.0.1:64983/Account/Callback");
    8.     Session["oauth"] = oauthSession;
    9.     return Redirect(oauthSession.GetAuthorizationUri(ConfigurationManager.AppSettings["authorize"]));
    10. }

    Creamos un objeto de la clase OAuthClientSession. Esta clase está pensada para encapsular toda una sesión oAuth, desde el inicio hasta la obtención de los tokens finales.

    El constructor de OAuthClienteSession (que se encuentra en Epnuke.OAuth) es:

    1. public OAuthClientSession(string ckey, string csecret, INonceGenerator nonceGenerator)
    2. {
    3.     _consumerKey = ckey;
    4.     _consumerSecret = csecret;
    5.     _nonceGenerator = nonceGenerator;
    6. }

    Listemos brevemento los parámetros:

    • ckey: Es el consumer key. Este es la clave que te da el proveedor (en este caso twitter) cuando registras tu aplicación en twitter. Todas las aplicaciones que quieran usar oAuth deben ser registradas en twitter.
    • csecret: Es el consumer secret. Es la otra clave que te da el proveedor cuando registras tu aplicación.
    • nonceGenerator: Es el generador de nonces. El nonce es un parámetro que debe ser introducido en cada llamada oAuth y que básicamente “debe ser único”. La librería incluye un generador de nonces llamado NonceGenerator32Bytes. La especificación oAuth no dice NADA al respecto del formato que debe tener el nonce.

    Una vez tenemos el objeto oauthSession creado llamamos al método RequestTemporaryCredentials y empezamos la danza de oAuth. Llamara este método hará lo siguiente:

    1. Creará una petición http oAuth. Una petición http es oAuth si tiene una cabecera “oAuth” con un formato determinado.
    2. Envia esta petición alproveedor de servicios. En el caso de twitter esto es la URL https://api.twitter.com/oauth/request_token. Esta petición servirá para indicar al proveedor de servicios que la “aplicación XXX quiere iniciar una sesión oAuth”.

    Si miramos con fiddler, ahí podremos ver nuestra petición:

    image

    Los datos enviados son:

    POST https://api.twitter.com/oauth/request_token HTTP/1.1
    Authorization: OAuth oauth_consumer_key="dbMEy71w8pGLPpbmxlwg”, oauth_signature_method="HMAC-SHA1”, oauth_version="1.0”, oauth_timestamp="1338538452”, oauth_nonce="v1TsKDiFyVZNjQ9dfffWS3AMRsWsvM3iycTJilGMY”, oauth_callback="http%3A%2F%2F127.0.0.1%3A64983%2FAccount%2FCallback”, oauth_signature=”%2BsRXDw7GrMcZZ4Xp%2Bvyf%2FVNnhaE%3D”
    Host: api.twitter.com

    Y los datos recibidos (elimino algunos no relevantes):

    HTTP/1.1 200 OK
    Date: Fri, 01 Jun 2012 08:14:17 GMT
    Status: 200 OK
    Content-Length: 147
    Content-Type: text/html; charset=utf-8
    Pragma: no-cache
    ETag: “6f0566f3a5d5e338f2cefbc9bd1a7c1f”
    X-Runtime: 0.01432
    X-Transaction: 91371892a8469732
    X-MID: 48cd92e044b19b57d1a6b5e471ccd03d2915ce48
    Expires: Tue, 31 Mar 1981 05:00:00 GMT
    Vary: Accept-Encoding
    Server: tfe

    oauth_token=P6xUReTuLm7cDxbSAbDZ8jUriKBIO3zvFfTemXc6VM&
    oauth_token_secret=yJi3IP7CRts2o68Rl2CQOgNsjRKFvZ8pFxMPJ7TKozY&
    oauth_callback_confirmed=true

    Fijaos en los datos de la cabecera de la petición (en azul). Esos datos conforman la cabecera oAuth y se van enviando a cada petición. Fijaos la firma digital que se computa usando el consumer secret y en como hemos enviado el consumer key (para identificar la aplicación que hace la petición).

    Bueno, ya tenemos la respuesta de twitter: nos ha mandado el primer token (como dije antes siempre hay uno público y el secreto que usaremos para firmar la petición): oauth_token y oauth_token_secret. Este token (oauth_token y oauth_token_secret) es el token que dice que “twitter ha aceptado la aplicación cliente XXX como un cliente oAuth válido)”.

    Ahora debemos recojer el token (oauth_token) y mandarlo a quien posee las credenciales, en este caso el mismo twitter (la URL exacta es https://api.twitter.com/oauth/authenticate). Eso es lo que hace el Redirect() de la última línea de la acción LogOnTwitter. Y el resultado:

    image

    Fijaos en la URL: https://api.twitter.com/oauth/authenticate?oauth_token=P6xUReTuLm7cDxbSAbDZ8jUriKBIO3zvFfTemXc6VM

    Hemos pasado el oauth_token que hemos obtenido en el punto anterior. De esta manera el autenticador (twitter) sabe que el proveedor de servicios (la api de twitter) sabe quien es esa aplicación que se autodenomina “Epnuke OAuth Library Test”).

    Ahora entro las credenciales de twitter… y c

    ontinúa el baile:

    image

    Las dos peticiones marcadas de amarillo son la petición que acabamos de realizar a api.twitter.com/oauth/authenticate (el Redirect() que teníamos) y otra petición a 127.0.0.1/Account/Callback

    ¿Quien hace esta petición? Pues bien, mi navegador claro pero ¿por que? Pues bien porque una vez me he autenticado twitter debe comunicar a mi aplicación que el usuario está autenticado. Y esto lo hace mandando un código HTTP de redirección hacia esta URL. Y porque esta URL, como la sabe twitter? Pues básicamente porque esta URL (la llamada URL de callback) se introduce cuando se registra la aplicación en twitter. Fijaos en un tema importante: esto tan solo es posible si la aplicación que se quiere conectar con twitter es capaz de exponer un endpoint http. Es decir es, básicamente, una aplicación web. En el caso de que no sea así, existe otro mecanismo (en otro post lo trataremos).

    Sigamos… Twitter nos ha mandado via querystring un código, llamado oauth_verifier que sirve para indicarnos que el autenticador ha aceptado las credenciales del usuario. Veamos el código de la acción Callback:

    1. public ActionResult Callback(string oauth_verifier)
    2. {
    3.     var oauthSession = Session["oauth"] as OAuthClientSession;
    4.     oauthSession.Authorize(oauth_verifier, ConfigurationManager.AppSettings["access_token"]);
    5.  
    6.     if (oauthSession.IsAuthorized)
    7.     {
    8.         var name = oauthSession.GetAdditionalData("screen_name");
    9.         FormsAuthentication.SetAuthCookie(name, false);
    10.         return RedirectToAction("Index", "Private");
    11.     }
    12.     else
    13.     {
    14.         return RedirectToAction("Index", "Home");
    15.     }
    16.  
    17. }

    Recuperamos el objeto oAuthSession (que habíamos guardado a la sesión), recogemos el oauth_verifier y llamamos al método “Authorize”. Dicho método hace lo siguiente:

    1. Creará una petición oAuth (con el header oAuth y la firma digital)
    2. Enviará dicha petición al proveedor de servicios. Para decirle: “Hey! El autenticador ha aceptado las credenciales del usuario y me ha devuelto este token. Puedes darme el token final? Gracias”.

    Si seguimos mirando fiddler:

    image

    Fijaos como esta última petición NO ha hecho Chrome sino el servidor web (es la petición del método Authorize). De nuevo si miramos los datos de la petición:

    POST https://api.twitter.com/oauth/access_token?oauth_verifier=heqcwkOPuopuMdsoqdMYP8My6kycqOuLYFSeIFK33w HTTP/1.1
    Authorization: OAuth oauth_consumer_key="dbMEy71w8pGLPpbmxlwg”, oauth_signature_method="HMAC-SHA1”, oauth_version="1.0”, oauth_timestamp="1338539804”, oauth_nonce="NQZmiFzvGpaBlY2JW4DAhdL1RBJrGq7qmlygByoIc”, oauth_token="P6xUReTuLm7cDxbSAbDZ8jUriKBIO3zvFfTemXc6VM”, oauth_signature="kiAPOn%2F8Ybb5oDfd6aPqsG6M750%3D”
    Host: api.twitter.com
    Connection: Keep-Alive

    Y los de la respuesta (quito datos no relevantes)

    HTTP/1.1 200 OK
    Date: Fri, 01 Jun 2012 08:37:03 GMT
    Status: 200 OK
    X-MID: ee5c8c5470911804d71f005a28c69a5be0b72cf4
    ETag: “4521b768dcce87386275e193df917953”
    Expires: Tue, 31 Mar 1981 05:00:00 GMT
    Content-Length: 162
    X-Frame-Options: SAMEORIGIN
    Pragma: no-cache
    Last-Modified: Fri, 01 Jun 2012 08:37:03 GMT
    X-Runtime: 0.04706
    Content-Type: text/html; charset=utf-8
    Vary: Accept-Encoding
    Server: tfe

    oauth_token=84274067-2TMYBrU2cH4x6B3zsYeKYOdCyOpCrxoxKeYZCuDQ&
    oauth_token_secret=TYRlbDfgb6gVGLxcoVWBTuJyR60pwC3w6fvaq2aw9g&
    user_id=xxxxxxxx&
    screen_name=eiximenis

    Bueno, hemos recibido otro par de tokens. Ese par de tokens son el par de tokens “final” y que nos van a permitir hacer llamadas oAuth a twitter en nombre del usuario. Además recibimos el ID interno del usuario y su nombre.

    En este punto hemos terminado el baile de tokens oAuth. Ya tenemos los tokens finales para “hacer cosas” en nombre del usuario.

    Pero nosotros no queríamos hacer nada en nombre del usuario, tan solo usar oAuth para autenticar el usuario. ¡Bueno, ningún problema! El resto de código de la Accion Callback es puro ASP.NET:

    1. Nos aseguramos que la respuesta de oAuth ha sido correcta y que el usuario está autorizado según oAuth (oauthSession.IsAuthorized)
    2. Recogemos el valor del parámetro screen_name (ese parámetro NO es estándard de oAuth, lo envía twitter)
    3. Llamam
    os a FormsAuthorization.SetAuthCookie para autenticar el usuario dentro de ASP.NET. 
    

    ¡Y listos! ¡Hemos terminado!

    image

    ¡Hemos conseguido autenticarnos a nuesta web usando las credenciales de twitter!

    En posteriores posts iremos explorando como generar estas peticiones oAuth, siempre apoyándonos en el código fuente de la librería que he dejado en CodePlex.

    Un saludo!

    PD: Si os descargáis el código fuente de Codeplex está este ejemplo.

    Si quieres, puedes invitarme a un café xD

    eiximenis
    ESCRITO POR
    eiximenis
    Compulsive Developer