OAuth con Android en el Racó de la FIB

Enviado por Jaume Moral en Jue, 09/05/2013 - 12:25

El Racó de la FIB tiene una API accesible vía OAuth. Utilizarla desde una aplicación móvil no es trivial y en este artículo intentaremos ver cómo hacerlo desde Android. Al mismo tiempo, es un ejemplo de cómo utilizar este tipo de autenticación en general para cualquier otra API que funcione con OAuth.

Primero, supondremos unos conocimientos mínimos de desarrollo en Android. Concretamente para seguir este ejemplo necesitaremos

Una vez tengamos eso, nos pondremos manos a la obra (o al código, en este caso).

La coreografía

El flujo de OAuth, también conocido como "OAuth dance", es lo que nos permite que nuestro sistema autorice a una aplicación de terceros a acceder a nuestra información. En nuestro caso, un usuario cualquiera del Racó dará permiso desde el Racó para que una aplicación desarrollada por nosotros consulte sus datos personales.

El baile consta de tres pasos: request, authorization y access.  Aunque antes tendremos que hacer las presentaciones previas: el Racó debe conocer la aplicación con la que bailará.

Conociendo a nuestra pareja: registrar la aplicación

Cualquier aplicación que tenga que acceder a información del Racó tiene que estar previamente registrada en el Racó. La URL para registrar una aplicación nueva es https://raco.fib.upc.edu/oauth/gestio-api/register.jsp

Una vez registrada la aplicación, el Racó nos generará dos valores: la clave y el secreto. Este par de valores irán incorporados en cada petición que la aplicación haga al Racó, de forma que cualquier petición deja rastro de la aplicación que la ha generado. Concretamente, la clave se pasa como un valor más dentro de la petición y el secreto se utiliza como clave para generar una firma digital de la petición. Como veremos, el hecho de utilizar una librería OAuth nos abstraerá de estos detalles: nos hemos de quedar con la idea de que las peticiones entre la aplicación y el Racó van firmadas.

Primer paso: Request

En este paso, la aplicación que estamos desarrollando pide un token al Racó, haciendo una petición signada. El Racó devuelve un token a la aplicación llamado "request token".

Segundo paso: Authorization

La forma de autorizar el "request token" es presentar una pantalla de Racó a la cual le pasamos como parámetro el "request token". Si el usuario que utiliza la aplicación la acepta, el Racó guardará que el "request token" está validado y retornará una redirección hacia nuestra aplicación, llamada Callback URL. Como nosotros no estamos desarrollando una aplicación web sino una aplicación Android, esta URL no será http:// sino un tipo de URL diferente que mapearemos en nuestra aplicación Android usando un Intent Filter. En resumen: desde el Racó el usuario valida su token y una vez validado se pasa de nuevo el control a la aplicación Android..

Tercer paso: Access

En estos momentos, tenemos un "request token" validado y lo que debemos hacer es intercambiarlo por un "access token". De nuevo, eso implica una petición firmada con las claves de la aplicación a una URL diferente. Esta petición nos retornará un nuevo token, que será el que nuestra aplicación se guardara y que permitirá acceder a los datos del usuario que ha autorizado. Puede que ahora os estéis preguntando… ¿por qué complicarse tanto la vida para obtener este token? ¿No podíamos utilizar el que ya teníamos? La respuesta es no, porque el "request token" lo hemos visto en la URL. En cambio, el intercambio por "access token" no implica ningún tipo de interacción web con el usuario, es totalmente entre la aplicación y el Racó. La clave de la seguridad de OAuth es que se mantenga en secreto.

Una vez realizados estos tres pasos ya no hay que volverlos a hacer. Mientras la aplicación mantenga el "access token" y este no caduque, se accederá a los datos del usuario sin que haya que volver a validar o autorizar

Paso a paso

Ahora ya sabemos cuál es nuestra coreografía. Vamos a hacerlo paso a paso para que queden claros los movimientos, con música (o código, como prefiráis).

Primero de todo, necesitaremos una librería que nos ayude a firmar las peticiones siguiendo el protocolo OAuth. Utilizaremos la librería SignPost, muy fácil de utilizar. Y la inicializaremos con los datos básicos: las claves de la aplicación y las URL de los diferentes pasos. SignPost nos proporciona un objeto Provider (que nos abstrae del Racó en este caso) y un Consumer (que representa a la aplicación que estamos desarrollando. Así pues, dentro de nuestra Activity tendremos los siguientes atributos:

    OAuthProvider provider = new DefaultOAuthProvider(
        "https://raco.fib.upc.edu/oauth/request_token",
        "https://raco.fib.upc.edu/oauth/access_token",
        "https://raco.fib.upc.edu/oauth/protected/authorize");    

    String callback="raco://raco";
    String key="4812b0b0-b1a9-11e2-9e96-0800200c9a66";
    String secret="54efb080-b1a9-11e2-9e96-0800200c9a66";
    DefaultOAuthConsumer consumer;

Para hacer la petición del Request Token, nos encontramos rápidamente con una limitación de Android: no podemos poner código que haga peticiones a Internet dentro del thread principal de la aplicación, así 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ón del token.

    class DemanaRequestTokenAsync extends AsyncTask<Void, Void, String> {
        
        @Override
        protected String doInBackground(Void... params) {
            String authURL=null;
            try {
                authURL = provider.retrieveRequestToken(consumer, callback);
                guardaTokens();
            } catch (Exception e) {
                e.printStackTrace();
            }
            return authURL;
        }

        // Una vez tengo el token abro ya el navegador. De evento en evento...
        // ¡Importante! Antes de continuar tengo que guardar los tokens
        
        @Override
        protected void onPostExecute(String authURL) {
            startActivity(new Intent(Intent.ACTION_VIEW, Uri.parse(authURL)));
        }
    }

Cuando validamos el token en nuestro navegador, nos redirigirá a la URL de callback, que será del estilo raco://raco. ¿Cómo hacemos que eso acabe ejecutando una parte de nuestra aplicación en lugar de abrir una página nueva en el navegador web? Muy fácil: utilizaremos un Intent Filter que asociará este protocolo con nuestra Activity

    <intent-filter>
        <action android:name="android.intent.action.VIEW"></action>
        <category android:name="android.intent.category.DEFAULT"></category>
        <category android:name="android.intent.category.BROWSABLE"></category>
        <data android:host="raco" android:scheme="raco"></data>
    </intent-filter>
   
    public void onResume() {
        super.onResume();
        Uri uri = this.getIntent().getData();
        recuperaTokens();

        // Este es el caso en que volvemos con token. Si no, no hacemos nada
        if (uri != null && uri.toString().startsWith(callback)) {
            new DemanaAccessTokenAsync().execute();
        }
    }

Como antes, el código para pedir el "access token" hay que ejecutarlo de forma asíncrona, así que volvemos a hacer una AsyncTask similar a la que hemos utilizado para pedir el "request token". La tarea hace la petición y cuando acaba guardamos el "access token". Aquí acaba nuestro baile: ya hemos conseguido de nuestra pareja lo que buscábamos.

    class DemanaAccessTokenAsync extends AsyncTask<Void, Void, Void> {
        @Override
        protected Void doInBackground(Void... params) {
            try {
                provider.retrieveAccessToken(consumer,null);
                guardaTokens(true);
            } catch (Exception e) {
                Log.d("oauth","ha petat al access token");
            }
            return null;
        }

        // Una vez tengo el access token y secreto, ya puedo comenzar a trabajar.
        
        @Override
        protected void onPostExecute(Void result) {
            // Aqui el consumer se ha actualizado con los 2 access tokens que ya le permiten hacer las peticiones
            // a nuestros webservices. Estos valores se tendrían que guardar y antes que nada hacer un
            // consumer.setTokenWithSecret si existen.
        }
    }

Se ha acabado el baile. Ahora comienza lo interesante.

En estos momentos ya tenemos nuestro preciado Access token: la pareja ya se conoce bastante bien y puede comenzar la conversación. Como ejemplo, pediremos la información básica del usuario y obtendremos el nombre completo. Podemos hacer muchas otras cosas, como podemos ver en la página de la API del Racó donde están los diferentes datos que podemos obtener. En el código utilizaremos el objeto consumer que hemos usado hasta ahora y que contiene los "access token" necesarios para poder hacer la petición firmada. Puede ser que los tengamos porque hemos hecho todo el baile porque ya los teníamos guardados en el localStorage de una petición previa.

    public String getNom() {
            String json=demana("https://raco.fib.upc.edu/api-v1/info-personal.json";);
            JSONObject jObject = new JSONObject(json);
            return jObject.getString("nom"));
    }
        
    private String demana (String url) {
        StringBuffer aux=new StringBuffer();
        try {
        URL u = new URL(url);
        HttpURLConnection request = (HttpURLConnection) u.openConnection();
        consumer.sign(request);
        BufferedReader in = new BufferedReader(new InputStreamReader(
                request.getInputStream()));
        String inputLine;
        while ((inputLine = in.readLine()) != null) {
            aux.append(inputLine);
        }
        } catch (Exception e) {
            Log.i("oauth",e.getMessage());
        }
        return aux.toString();

    }

Conclusiones

En este ejemplo hemos visto cómo utilizar desde Android una API con OAuth 1.0 con la librería SignPost. Aunque hayamos usado de ejemplo el Racó, 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.

 

Síguenos en

Els nostres articles del bloc d'inLab FIB

         
         

inLab FIB incorpora esCert

Icona ESCERT

inLab es miembro de