Utilización paso a paso de OAuth 1.0 en twitter con python bottle


El objetivo de esta entrada es explicar de forma detallada la utilización del protocolo OAth 1.0 en la API de twitter, para la que desarrollaremos una sencilla aplicación que nos permitirá escribir un “tweet” en nombre de un usuario cualquiera de twitter. La aplicación la desarrollaremos en Python utilizando el framework bottle y la desplegaremos en la PaaS OpenShift de RedHat.

Configuración de la aplicación en OpenShift

Esta sección es totalmente opcional y puede cambiarse por cualquier otro despliegue que conlleve el poder acceder a la aplicación web a través de una URL accesible desde Internet, ya que es preciso que twitter se pueda comunicar con nuestra aplicación para autorizarla.

Para una introducción a la plataforma de desarollo en la nube OpenShift se puede consultar Instalación de Joomla en OpenShift de este mismo blog.

Seguimos las indicaciones de bottle-openshift-quickstart para instalar el entorno de Python y bottle en un gear de OpenShift:

$ rhc app create -a bt -t python-2.6
$ cd bt/
$ git remote add upstream -m master git://github.com/openshift/bottle-openshift-quickstart.git
$ git pull -s recursive -X theirs upstream master
$ git push

que nos crea el repositorio bt con la siguiente estructura:

.
|-- data
|-- libs
|-- README
|-- README.md
|-- setup.py
`-- wsgi
    |-- application
    |-- mybottleapp.py
    `-- static
        `-- README

Editamos el fichero setup.py y añadimos a la línea “install_requires” los paquetes requests y requests-oauthlib que vamos a utilizar para interactuar con la API de twitter:

      install_requires=['bottle','requests','requests-oauthlib'],

Actualizamos el repositorio git y entre los mensajes que aparecen podremos ver la instalación de los paquetes adicionales, como en el extracto siguiente en el que se detalla la instalación de requests-oauthlib:

...
remote: Searching for requests-oauthlib
remote: Reading http://mirror1.ops.rhcloud.com/mirror/python/web/simple/requests-oauthlib/
remote: Reading https://github.com/requests/requests-oauthlib
remote: Best match: requests-oauthlib 0.3.0
remote: Downloading http://mirror1.ops.rhcloud.com/mirror/python/web/packages/source/r/requests-oauthlib/requests-oauthlib-0.3.0.tar.gz#md5=7fb00175766308ed5cf40185304eb0c6
remote: Processing requests-oauthlib-0.3.0.tar.gz
remote: Running requests-oauthlib-0.3.0/setup.py -q bdist_egg --dist-dir /tmp/easy_install-y1XGE5/requests-oauthlib-0.3.0/egg-dist-tmp-TupYTU
remote: zip_safe flag not set; analyzing archive contents...
remote: Adding requests-oauthlib 0.3.0 to easy-install.pth file
...

Python bottle

Python bottle es un sencillo framework para el desarrollo de aplicaciones web en python. Bottle no es tan completo como django, pero mucho es más sencillo de utilizar y por tanto más apropiado para realizar aplicaciones tan sencillas como la de esta entrada. Cualquier persona que tenga cierta familiaridad con la utilización de métodos HTTP y nociones de programación en python puede comenzar a desarrollar una aplicación web sencilla en pocos minutos utilizando bottle.

OAuth 1.0 y su utilización en twitter

Lo primero que hay que dejar claro es que OAuth es un mecanismo de autorización no de autenticación, en nuestro caso concreto lo que vamos a hacer es que un usuario cualquiera de twitter autorice a nuestra aplicación para que escriba un tweet por él, para lo cual utilizaremos la versión 1.0 de OAuth que implementa la API de twitter.

Funcionamiento de OAuth1

OAuth 1.0 está descrito con detalle en oauth.net/core/1.0/ y para comprenderlo hay que conocer algunos de los términos que utiliza:

  • Proveedor de servicio (Service Provider): Aplicación web que utiliza OAuth como mecanismo de autorización (en nuestro caso twitter).
  • Usuario: Persona que tiene una cuenta en el proveedor de servicios.
  • Cliente (Consumer): Sitio web o aplicación que utiliza OAuth para acceder al proveedor de servicio en nombre del usuario (en nuestro caso bt).
  • Recursos protegidos (Protected Resource(s)): Datos controlados por el proveedor de servicios a los que el cliente puede acceder tras autenticarse.
  • Clave de cliente (Consumer Key): Valor utilizado por el cliente para identificarse frente al proveedor de servicios. Esta clave normalmente es un token, es decir una cadena aleatoria de cierto tamaño que identifica de forma única al cliente.
  • Secreto de cliente (Consumer Secret): Valor secreto utilizado por el cliente para garantizar que es el propietario de la clave de cliente.
  • Token de solicitud (Request Token): Valor utilizado por el cliente para obtener autorización del usuario y que se utiliza para obtener un token de autorización.
  • Token de acceso (Access Token): Valor utilizado por el cliente para obtener acceso a los recursos protegidos en nombre del usuario, en lugar de utilizar las credenciales del usuario en el proveedor de servicios (nombre de usuario y contraseña por ejemplo).
  • Token Secret: Valor secreto utilizado por el cliente para garantizar que es el propietario de cualquiera de los tokens que utilice.

Podemos resumir brevemente el funcionamiento de OAuth 1.0 de la siguiente forma: El objetivo es conseguir que el cliente consiga mediante la autorización del usuario, un token de acceso para poder utilizar los recursos restringidos del proveedor de servicios sin que el usuario tenga que darle su contraseña, además en OAuth 1.0 este token de acceso no caduca, por lo que una vez obtenido es válido siempre, salvo que el usuario lo revoque.

Cabecera OAuth

Para incluir los parámetros de OAuth necesarios en cada interacción entre el cliente y el proveedor de servicios se utiliza una cabecera “Authorization” como la siguiente:

                Authorization: OAuth realm="http://sp.example.com/",
                oauth_consumer_key="0685bd9184jfhq22",
                oauth_signature_method="HMAC-SHA1",
                oauth_signature="wOJIO9A2W5mFwDgiDvZbTSMK%2FPY%3D",
                oauth_timestamp="137131200",
                oauth_nonce="4572616e48616d6d65724c61686176",
                oauth_version="1.0"

en la que los parámetros que se incluyen son variable y se pasan en formato clave=valor separados por comas.

Utilización de OAuth 1.0

En el siguiente diagrama, enlazado de oauth.net, se pueden ver todos los pasos que hay que seguir en el proceso de autenticación con OAuth:

otuh authentication flow

Los parámetros de la cabecera de autorización cambian en cada paso, por lo que deberemos generar para cada uno de ellos una cabecera con los parámetros adecuados.

Requests-OAuthlib

Existen muchas bibliotecas en python para dar soporte OAuth a una aplicación, en nuestro caso hemos seleccionado requests-ouathlib porque vamos a utilizar la biblioteca requests para interactuar con la API de twitter.

Utilizamos requests-oauthlib para generar en cada paso la cabecera de autorización, pasándole los parámetros necesarios que conocemos y dejando que requests-oauthlib genere el resto de parámetros de OAuth necesarios, por ejemplo para utilizar una cabecera de autorización como la mostrada anteriormente tendríamos que utilizar el siguiente código:

# Creamos una tupla a la que pasamos la clave y el secreto del cliente que tenemos definidos previamente.
# requests-oauthlib nos creará los parámetros timestamp, nonce, version, etc. aunque podemos definirlos 
# explícitamente si lo deseamos.
oauth = OAuth1(client_key=CONSUMER_KEY, client_secret=CONSUMER_SECRET)
# Definimos la url del proveedor de servicios que vamos a utilizar:0
request_token_url = 'https://api.twitter.com/oauth/request_token'
# Hacemos la llamada a la API del proveedor de servicios utilizando la cabecera de autorización definida:
r = requests.post(url=request_token_url, auth=oauth)

Desarrollo paso a paso

Vamos a hacer el desarrollo paso a paso para que sea todo más claro.

Damos de alta la aplicación en twitter

En primer lugar es necesario dar de alta la aplicación en twitter, para conseguir una clave y secreto de cliente. Accedemos a My applications del sitio de desarrollo de twitter y damos de alta una nueva aplicación en la que tendremos que definir entre otros parámetros la URL de respuesta (callback URL) y twitter nos facilitará una clave y secretos de cliente únicos:

twitter app create

Importación de bibliotecas y definición de constantes

En primer lugar cargamos las bibliotecas que vamos a utilizar en todo el código y definimos las constantes de la aplicación:

# -*- encoding: utf-8 -*-
from bottle import default_app, get, post, template, request, static_file, response
import requests
from requests_oauthlib import OAuth1
from urlparse import parse_qs

REQUEST_TOKEN_URL = "https://api.twitter.com/oauth/request_token"
AUTHENTICATE_URL = "https://api.twitter.com/oauth/authenticate?oauth_token="
ACCESS_TOKEN_URL = "https://api.twitter.com/oauth/access_token"

CONSUMER_KEY = "iVj..."
CONSUMER_SECRET = "ZpI..."

Selección de un objeto para almacenar los tokens

Una vez que un usuario autoriza a nuestro cliente para interactuar con twitter en su nombre, lo normal sería que almacenáramos de forma permanente todos los tokens para no volver a solicitar autorización al usuario (en una base de datos por ejemplo), pero en nuestro caso, que se trata de una simple aplicación para aprender a utilizar la OAuth en twitter, vamos a utilizar un simple diccionario en python como objeto para almacenar los tokens que se generen. De esta manera no almacenamos los tokens del usuario y será necesario volver a solicitar autorización cada vez que se quiera utilizar el cliente.

TOKENS = {}

Ahora vamos a ir paso a paso siguiendo el diagrama “OAuth Authentication Flow”

Paso A

Definimos la página de inicio y llamamos a la función get_request_token() para obtener el token de solicitud:

@get('/')
def index():
    get_request_token()

La función get_request_token() está definida previamente. En esta función accedemos a la API de twitter con los parámetros del cliente y obtenemos los tokens de solicitud y su secreto (No es necesario que pasemos como parámetro oauth_callback porque esta URL la definimos al dar de alta la aplicación en twitter):

def get_request_token():
    oauth = OAuth1(CONSUMER_KEY,
                   client_secret=CONSUMER_SECRET,
    )
    r = requests.post(url=REQUEST_TOKEN_URL, auth=oauth)

Paso B

Seguimos dentro de la función get_request_token y almacenamos el token de solictud y se secreto en el diccionario TOKENS

:

    credentials = parse_qs(r.content)
    TOKENS["request_token"] = credentials.get('oauth_token')[0]
    TOKENS["request_token_secret"] = credentials.get('oauth_token_secret')[0]

Construimos la URL a la que vamos a redirigir al usuario para que autorice al cliente:

    authorize_url = AUTHENTICATE_URL + TOKENS["request_token"]
    return template('index.tpl', authorize_url=authorize_url)

Este paso implica la utilización del siguiente template de bottle:

<html xmlns="http://www.w3.org/1999/xhtml">
  <head>
    <title>.::Usando la API de twitter::.</title>
  </head>
  <body>
    <a href="{{authorize_url}}">twittear</a>
  </body>
</html>

Paso C

Se redirige al usuario al sitio de twitter para que autorice el cliente, explicándole los permisos que se otorga. En el caso de que el usuario no tuviera abierta sesión en twitter se le solicitará usuario y contraseña:

Autorización de twitter

Paso D

Una vez que el usuario autoriza a el cliente, twitter lo redirige a la URL de respuesta definida, pasando por GET el parámetro oauth_verifier que capturamos para utilizar después:

@get('/twittear')
def get_verifier():
    TOKENS["verifier"] = request.query.oauth_verifier

Paso E

Solicitamos el token de acceso a twitter utilizando el token de solicitud y el parámetro oauth_verifier obtenido en el paso previo:

    get_access_token(TOKENS)
def get_access_token(TOKENS):
    oauth = OAuth1(CONSUMER_KEY,
                   client_secret=CONSUMER_SECRET,
                   resource_owner_key=TOKENS["request_token"],
                   resource_owner_secret=TOKENS["request_token_secret"],
                   verifier=TOKENS["verifier"],
    )

    r = requests.post(url=ACCESS_TOKEN_URL, auth=oauth)

Paso F

Seguimos en la función get_access_token. Si los parámetros que pasamos a twitter son correctos, se autoriza a el cliente a acceder a los recursos protegidos del usuario que lo ha autorizado, proporcionando al cliente un token de acceso:

    credentials = parse_qs(r.content)
    TOKENS["access_token"] = credentials.get('oauth_token')[0]
    TOKENS["access_token_secret"] = credentials.get('oauth_token_secret')[0]

Paso G

Una vez que hemos conseguido el token de acceso, podemos utilizar los recursos protegidos del usuario a los que se nos ha dado acceso, por ejemplo escribir un “tweet” en su nombre:

    return template('tweet')

@post('/twittear')
def tweet_submit():
    texto = request.forms.get("tweet")
    oauth = OAuth1(CONSUMER_KEY,
                   client_secret=CONSUMER_SECRET,
                   resource_owner_key=TOKENS["access_token"],
                   resource_owner_secret=TOKENS["access_token_secret"])

    url = 'https://api.twitter.com/1.1/statuses/update.json'

    r = requests.post(url=url,
                      data={"status":texto},
                      auth=oauth)
    if r.status_code == 200:
        return "<p>Tweet enviado correctamente</p>"
    else:
        return "<p>Hubo un problema al enviar el tweet</p>"

Para escribir el tweet necesitamos el siguiente template:

<html xmlns="http://www.w3.org/1999/xhtml">
  <head>
    <title>.::Usando la API de twitter::.</title>
  </head>
  <body>
    <p>Buen momento para un tweet:</p>
    <br />
    <form action="/twittear" method="post">
      <p><textarea name="tweet" id="textbox" rows="3" cols="50"></textarea></p>
      <p><input type="submit" class="button" value="Enviar" /></p>
    </form>
  </body>
</html>

Código

El código completo está disponible en github y la “aplicación” estará un tiempo disponible para pruebas en bt-amolina.rhcloud.com, aunque en cuanto necesite utilizar ese gear la eliminaré ;).

Actualización [16/12/2013]: La aplicación bt-amolina.rhcloud.com ya no está disponible.

Conclusiones

En esta entrada hemos podido ver de forma detallada y paso a paso la manera de utilizar OAuth 1.0 en twitter, aunque lo importante es que la mayor parte del código es bastante general por lo que es fácilmente reutilizable con cualquier otra API web que utilice OAuth 1.0 como mecanismo de autorización de clientes.

, , , ,

  1. Deja un comentario

Responder

Introduce tus datos o haz clic en un icono para iniciar sesión:

Logo de WordPress.com

Estás comentando usando tu cuenta de WordPress.com. Cerrar sesión / Cambiar )

Imagen de Twitter

Estás comentando usando tu cuenta de Twitter. Cerrar sesión / Cambiar )

Foto de Facebook

Estás comentando usando tu cuenta de Facebook. Cerrar sesión / Cambiar )

Google+ photo

Estás comentando usando tu cuenta de Google+. Cerrar sesión / Cambiar )

Conectando a %s

A %d blogueros les gusta esto: