{"id":1297,"date":"2013-05-09T10:25:27","date_gmt":"2013-05-09T08:25:27","guid":{"rendered":"https:\/\/inlab.fib.upc.edu\/?p=1297"},"modified":"2013-05-09T10:25:27","modified_gmt":"2013-05-09T08:25:27","slug":"oauth-con-android-en-el-raco-de-la-fib","status":"publish","type":"post","link":"https:\/\/inlab.fib.upc.edu\/es\/uncategorized-ca\/oauth-con-android-en-el-raco-de-la-fib","title":{"rendered":"OAuth con Android en el Rac\u00f3 de la FIB"},"content":{"rendered":"<p>El Rac\u00f3 de la FIB tiene una API accesible v\u00eda OAuth. Utilizarla desde una aplicaci\u00f3n m\u00f3vil no es trivial y en este art\u00edculo intentaremos ver c\u00f3mo hacerlo desde Android. Al mismo tiempo, es un ejemplo de c\u00f3mo utilizar este tipo de autenticaci\u00f3n en general para cualquier otra API que funcione con OAuth.<\/p>\n<p>Primero, supondremos unos conocimientos m\u00ednimos de desarrollo en Android<\/p>\n<p><!--more--><\/p>\n<p>El Rac\u00f3 de la FIB tiene una API accesible v\u00eda OAuth. Utilizarla desde una aplicaci\u00f3n m\u00f3vil no es trivial y en este art\u00edculo intentaremos ver c\u00f3mo hacerlo desde Android. Al mismo tiempo, es un ejemplo de c\u00f3mo utilizar este tipo de autenticaci\u00f3n en general para cualquier otra API que funcione con OAuth.<\/p>\n<p>Primero, supondremos unos conocimientos m\u00ednimos de desarrollo en Android. Concretamente para seguir este ejemplo necesitaremos<\/p>\n<ul>\n<li>Un entorno de desarrollo Android correctamente configurado, con Eclipse y ADT, tal y como en <a href=\"http:\/\/developer.android.com\/sdk\/installing\/installing-adt.html\">http:\/\/developer.android.com\/sdk\/installing\/installing-adt.html<\/a><\/li>\n<li>Conocimientos b\u00e1sicos: qu\u00e9 son las Activities de Android, los Intents e Intent Filters y las AsyncTasks<\/li>\n<li>Una m\u00ednima idea de qu\u00e9 es OAuth<\/li>\n<\/ul>\n<p>Una vez tengamos eso, nos pondremos manos a la obra (o al c\u00f3digo, en este caso).<\/p>\n<h2>La coreograf\u00eda<\/h2>\n<p>El flujo de OAuth, tambi\u00e9n conocido como \u00abOAuth dance\u00bb, es lo que nos permite que nuestro sistema autorice a una aplicaci\u00f3n de terceros a acceder a nuestra informaci\u00f3n. En nuestro caso, un usuario cualquiera del Rac\u00f3 dar\u00e1 permiso desde el Rac\u00f3 para que una aplicaci\u00f3n desarrollada por nosotros consulte sus datos personales.<\/p>\n<p>El baile consta de tres pasos: <em>request<\/em>, <em>authorization<\/em> y <em>access<\/em>.&nbsp; Aunque antes tendremos que hacer las presentaciones previas: el Rac\u00f3 debe conocer la aplicaci\u00f3n con la que bailar\u00e1.<\/p>\n<h3><strong>Conociendo a nuestra pareja: registrar la aplicaci\u00f3n<\/strong><\/h3>\n<p>Cualquier aplicaci\u00f3n que tenga que acceder a informaci\u00f3n del Rac\u00f3 tiene que estar previamente registrada en el Rac\u00f3. La URL para registrar una aplicaci\u00f3n nueva es <a href=\"https:\/\/raco.fib.upc.edu\/oauth\/gestio-api\/register.jsp\">https:\/\/raco.fib.upc.edu\/oauth\/gestio-api\/register.jsp<\/a><\/p>\n<p>Una vez registrada la aplicaci\u00f3n, el Rac\u00f3 nos generar\u00e1 dos valores: la clave y el secreto. Este par de valores ir\u00e1n incorporados en cada petici\u00f3n que la aplicaci\u00f3n haga al Rac\u00f3, de forma que cualquier petici\u00f3n deja rastro de la aplicaci\u00f3n que la ha generado. Concretamente, la clave se pasa como un valor m\u00e1s dentro de la petici\u00f3n y el secreto se utiliza como clave para generar una firma digital de la petici\u00f3n. Como veremos, el hecho de utilizar una librer\u00eda OAuth nos abstraer\u00e1 de estos detalles: nos hemos de quedar con la idea de que las peticiones entre la aplicaci\u00f3n y el Rac\u00f3 van firmadas.<\/p>\n<h3><strong>Primer paso: Request<\/strong><\/h3>\n<p>En este paso, la aplicaci\u00f3n que estamos desarrollando pide un&nbsp;<em>token<\/em>&nbsp;al Rac\u00f3, haciendo una petici\u00f3n signada. El Rac\u00f3 devuelve un&nbsp;<em>token&nbsp;<\/em>a la aplicaci\u00f3n llamado \u00abrequest token\u00bb.<\/p>\n<h3><strong>Segundo paso: Authorization<\/strong><\/h3>\n<p>La forma de autorizar el \u00abrequest token\u00bb es presentar una pantalla de Rac\u00f3 a la cual le pasamos como par\u00e1metro el \u00abrequest token\u00bb. Si el usuario que utiliza la aplicaci\u00f3n la acepta, el Rac\u00f3 guardar\u00e1 que el \u00abrequest token\u00bb est\u00e1 validado y retornar\u00e1 una redirecci\u00f3n hacia nuestra aplicaci\u00f3n, llamada Callback URL. Como nosotros no estamos desarrollando una aplicaci\u00f3n web sino una aplicaci\u00f3n Android, esta URL no ser\u00e1 http:\/\/ sino un tipo de URL diferente que mapearemos en nuestra aplicaci\u00f3n Android usando un Intent Filter. En resumen: desde el Rac\u00f3 el usuario valida su token y una vez validado se pasa de nuevo el control a la aplicaci\u00f3n Android..<\/p>\n<h3><strong>Tercer paso: Access<\/strong><\/h3>\n<p>En estos momentos, tenemos un \u00abrequest token\u00bb validado y lo que debemos hacer es intercambiarlo por un \u00abaccess token\u00bb. De nuevo, eso implica una petici\u00f3n firmada con las claves de la aplicaci\u00f3n a una URL diferente. Esta petici\u00f3n nos retornar\u00e1 un nuevo <em>token<\/em>, que ser\u00e1 el que nuestra aplicaci\u00f3n se guardara y que permitir\u00e1 acceder a los datos del usuario que ha autorizado. Puede que ahora os est\u00e9is preguntando\u2026 \u00bfpor qu\u00e9 complicarse tanto la vida para obtener este<em> token<\/em>? \u00bfNo pod\u00edamos utilizar el que ya ten\u00edamos? La respuesta es no, porque el \u00abrequest token\u00bb lo hemos visto en la URL. En cambio, el intercambio por \u00abaccess token\u00bb no implica ning\u00fan tipo de interacci\u00f3n web con el usuario, es totalmente entre la aplicaci\u00f3n y el Rac\u00f3. La clave de la seguridad de OAuth es que se mantenga en secreto.<\/p>\n<p>Una vez realizados estos tres pasos ya no hay que volverlos a hacer. Mientras la aplicaci\u00f3n mantenga el \u00abaccess token\u00bb y este no caduque, se acceder\u00e1 a los datos del usuario sin que haya que volver a validar o autorizar<\/p>\n<p><img fetchpriority=\"high\" decoding=\"async\" class=\" size-full wp-image-1162\" alt=\"\" src=\"https:\/\/inlab.fib.upc.edu\/wp-content\/uploads\/2013\/05\/oauth_seq.gif\" style=\"height:370px; width:572px\" width=\"572\" height=\"370\" \/><\/p>\n<h2><strong>Paso a paso<\/strong><\/h2>\n<p>Ahora ya sabemos cu\u00e1l es nuestra coreograf\u00eda. Vamos a hacerlo paso a paso para que queden claros los movimientos, con m\u00fasica (o c\u00f3digo, como prefir\u00e1is).<\/p>\n<p>Primero de todo, necesitaremos una librer\u00eda que nos ayude a firmar las peticiones siguiendo el protocolo OAuth. Utilizaremos la librer\u00eda SignPost, muy f\u00e1cil de utilizar. Y la inicializaremos con los datos b\u00e1sicos: las claves de la aplicaci\u00f3n y las URL de los diferentes pasos. SignPost nos proporciona un objeto Provider (que nos abstrae del Rac\u00f3 en este caso) y un Consumer (que representa a la aplicaci\u00f3n que estamos desarrollando. As\u00ed pues, dentro de nuestra Activity tendremos los siguientes atributos:<\/p>\n<pre>\r\n    OAuthProvider provider = new DefaultOAuthProvider(\r\n        \"https:\/\/raco.fib.upc.edu\/oauth\/request_token\",\r\n        \"https:\/\/raco.fib.upc.edu\/oauth\/access_token\",\r\n        \"https:\/\/raco.fib.upc.edu\/oauth\/protected\/authorize\");    \r\n\r\n    String callback=\"raco:\/\/raco\";\r\n    String key=\"4812b0b0-b1a9-11e2-9e96-0800200c9a66\";\r\n    String secret=\"54efb080-b1a9-11e2-9e96-0800200c9a66\";\r\n    DefaultOAuthConsumer consumer;<\/pre>\n<p>Para hacer la petici\u00f3n del Request Token, nos encontramos r\u00e1pidamente con una limitaci\u00f3n de Android: no podemos poner c\u00f3digo que haga peticiones a Internet dentro del <em>thread<\/em> principal de la aplicaci\u00f3n, as\u00ed que haremos las peticiones dentro de una AsyncTask. Una vez tengamos el token, los guardamos (en el localStorage por ejemplo) y abrimos un navegador con la URL de validaci\u00f3n del token.<\/p>\n<pre>\r\n    class DemanaRequestTokenAsync extends AsyncTask&lt;Void, Void, String&gt; {\r\n        \r\n        @Override\r\n        protected String doInBackground(Void... params) {\r\n            String authURL=null;\r\n            try {\r\n                authURL = provider.retrieveRequestToken(consumer, callback);\r\n                guardaTokens();\r\n            } catch (Exception e) {\r\n                e.printStackTrace();\r\n            }\r\n            return authURL;\r\n        }\r\n\r\n        \/\/ Una vez tengo el token abro ya el navegador. De evento en evento...\r\n        \/\/ \u00a1Importante! Antes de continuar tengo que guardar los tokens\r\n        \r\n        @Override\r\n        protected void onPostExecute(String authURL) {\r\n            startActivity(new Intent(Intent.ACTION_VIEW, Uri.parse(authURL)));\r\n        }\r\n    }<\/pre>\n<p>Cuando validamos el token en nuestro navegador, nos redirigir\u00e1 a la URL de callback, que ser\u00e1 del estilo raco:\/\/raco. \u00bfC\u00f3mo hacemos que eso acabe ejecutando una parte de nuestra aplicaci\u00f3n en lugar de abrir una p\u00e1gina nueva en el navegador web? Muy f\u00e1cil: utilizaremos un Intent Filter que asociar\u00e1 este protocolo con nuestra Activity<\/p>\n<pre>\r\n    &lt;intent-filter&gt;\r\n        &lt;action android:name=\"android.intent.action.VIEW\"&gt;&lt;\/action&gt;\r\n        &lt;category android:name=\"android.intent.category.DEFAULT\"&gt;&lt;\/category&gt;\r\n        &lt;category android:name=\"android.intent.category.BROWSABLE\"&gt;&lt;\/category&gt;\r\n        &lt;data android:host=\"raco\" android:scheme=\"raco\"&gt;&lt;\/data&gt;\r\n    &lt;\/intent-filter&gt;<\/pre>\n<pre>\r\n   \r\n    public void onResume() {\r\n        super.onResume();\r\n        Uri uri = this.getIntent().getData();\r\n        recuperaTokens();\r\n\r\n        \/\/ Este es el caso en que volvemos con token. Si no, no hacemos nada\r\n        if (uri != null &amp;&amp; uri.toString().startsWith(callback)) {\r\n            new DemanaAccessTokenAsync().execute();\r\n        }\r\n    }<\/pre>\n<p>Como antes, el c\u00f3digo para pedir el \u00abaccess token\u00bb hay que ejecutarlo de forma as\u00edncrona, as\u00ed que volvemos a hacer una AsyncTask similar a la que hemos utilizado para pedir el \u00abrequest token\u00bb. La tarea hace la petici\u00f3n y cuando acaba guardamos el \u00abaccess token\u00bb. Aqu\u00ed acaba nuestro baile: ya hemos conseguido de nuestra pareja lo que busc\u00e1bamos.<\/p>\n<pre>\r\n    class DemanaAccessTokenAsync extends AsyncTask&lt;Void, Void, Void&gt; {\r\n        @Override\r\n        protected Void doInBackground(Void... params) {\r\n            try {\r\n                provider.retrieveAccessToken(consumer,null);\r\n                guardaTokens(true);\r\n            } catch (Exception e) {\r\n                Log.d(\"oauth\",\"ha petat al access token\");\r\n            }\r\n            return null;\r\n        }\r\n\r\n        \/\/ Una vez tengo el access token y secreto, ya puedo comenzar a trabajar.\r\n        \r\n        @Override\r\n        protected void onPostExecute(Void result) {\r\n            \/\/ Aqui el consumer se ha actualizado con los 2 access tokens que ya le permiten hacer las peticiones\r\n            \/\/ a nuestros webservices. Estos valores se tendr\u00edan que guardar y antes que nada hacer un\r\n            \/\/ consumer.setTokenWithSecret si existen.\r\n        }\r\n    }<\/pre>\n<h2>Se ha acabado el baile. Ahora comienza lo interesante.<\/h2>\n<p>En estos momentos ya tenemos nuestro preciado Access token: la pareja ya se conoce bastante bien y puede comenzar la conversaci\u00f3n. Como ejemplo, pediremos la informaci\u00f3n b\u00e1sica del usuario y obtendremos el nombre completo. Podemos hacer muchas otras cosas, como podemos ver en la <a href=\"https:\/\/raco.fib.upc.edu\/oauth\/gestio-api\/api.jsp\" target=\"_blank\" rel=\"noopener\">p\u00e1gina de la API del Rac\u00f3<\/a> donde est\u00e1n los diferentes datos que podemos obtener. En el c\u00f3digo utilizaremos el objeto consumer que hemos usado hasta ahora y que contiene los \u00abaccess token\u00bb necesarios para poder hacer la petici\u00f3n firmada. Puede ser que los tengamos porque hemos hecho todo el baile porque ya los ten\u00edamos guardados en el localStorage de una petici\u00f3n previa.<\/p>\n<pre>\r\n    public String getNom() {\r\n            String json=demana(\"https:\/\/raco.fib.upc.edu\/api-v1\/info-personal.json\";);\r\n            JSONObject jObject = new JSONObject(json);\r\n            return jObject.getString(\"nom\"));\r\n    }\r\n        \r\n    private String demana (String url) {\r\n        StringBuffer aux=new StringBuffer();\r\n        try {\r\n        URL u = new URL(url);\r\n        HttpURLConnection request = (HttpURLConnection) u.openConnection();\r\n        consumer.sign(request);\r\n        BufferedReader in = new BufferedReader(new InputStreamReader(\r\n                request.getInputStream()));\r\n        String inputLine;\r\n        while ((inputLine = in.readLine()) != null) {\r\n            aux.append(inputLine);\r\n        }\r\n        } catch (Exception e) {\r\n            Log.i(\"oauth\",e.getMessage());\r\n        }\r\n        return aux.toString();\r\n\r\n    }<\/pre>\n<h2>Conclusiones<\/h2>\n<p>En este ejemplo hemos visto c\u00f3mo utilizar desde Android una API con OAuth 1.0 con la librer\u00eda SignPost. Aunque hayamos usado de ejemplo el Rac\u00f3, esto es aplicable a cualquier otra web que utilice OAuth 1.0. Muchas API, sin embargo, ya han migrado a OAuth 2.0, que entre otras cosas simplifica la forma de acceder.<\/p>\n<p>&nbsp;<\/p>\n","protected":false},"excerpt":{"rendered":"<p>El Rac\u00f3 de la FIB tiene una API accesible v\u00eda OAuth. Utilizarla desde una aplicaci\u00f3n m\u00f3vil no es trivial y en este art\u00edculo intentaremos ver c\u00f3mo hacerlo desde Android. Al mismo tiempo, es un ejemplo de c\u00f3mo utilizar este tipo de autenticaci\u00f3n en general para cualquier otra API que funcione con OAuth. Primero, supondremos unos [&hellip;]<\/p>\n","protected":false},"author":594,"featured_media":1159,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"_acf_changed":false,"footnotes":""},"categories":[1],"tags":[],"experteses":[],"class_list":["post-1297","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-uncategorized-ca"],"acf":[],"_links":{"self":[{"href":"https:\/\/inlab.fib.upc.edu\/es\/wp-json\/wp\/v2\/posts\/1297","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/inlab.fib.upc.edu\/es\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/inlab.fib.upc.edu\/es\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/inlab.fib.upc.edu\/es\/wp-json\/wp\/v2\/users\/594"}],"replies":[{"embeddable":true,"href":"https:\/\/inlab.fib.upc.edu\/es\/wp-json\/wp\/v2\/comments?post=1297"}],"version-history":[{"count":0,"href":"https:\/\/inlab.fib.upc.edu\/es\/wp-json\/wp\/v2\/posts\/1297\/revisions"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/inlab.fib.upc.edu\/es\/wp-json\/wp\/v2\/media\/1159"}],"wp:attachment":[{"href":"https:\/\/inlab.fib.upc.edu\/es\/wp-json\/wp\/v2\/media?parent=1297"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/inlab.fib.upc.edu\/es\/wp-json\/wp\/v2\/categories?post=1297"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/inlab.fib.upc.edu\/es\/wp-json\/wp\/v2\/tags?post=1297"},{"taxonomy":"experteses","embeddable":true,"href":"https:\/\/inlab.fib.upc.edu\/es\/wp-json\/wp\/v2\/experteses?post=1297"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}