Como yo, somos muchos los que hemos tenido que aprender a utilizar este “nuevo” protocolo llamado OAuth para poder interactuar con la creciente lista de servicios y aplicaciones web que lo implementan. Algunos de los más conocidos que implementan la versión 1.0a del protocolo son Twitter, Youtube o LinkedIn.
¿Qué es OAuth?
Como bien resume Google, el protocolo OAuth proporciona una manera estándar de acceder a los datos protegidos de diferentes sitios web.
O dicho de otro modo, OAuth es un protocolo abierto y estándar que permite a un sitio web A acceder de un modo seguro, previa autorización del usuario, a datos de acceso restringido de dicho usuario albergados en otro sitio web B mediante una API que soporta OAuth y que B pone a disposición de A.
El aspecto más importante en cuanto a la seguridad que ofrece OAuth es que el sitio web A (OAuth Consumer) no almacena los credenciales de acceso (usuario y contraseña) que el usuario utiliza en su cuenta en el sitio web B (Service Provider).
Usuarios, OAuth Consumer y OAuth Service Provider
Para poder hablar de OAuth, se necesitan tres partes, un servidor o proveedor de servicios, usuarios y un consumidor:
- OAuth Service Provider o proveedor de servicios OAuth: Sitios o servicios web que contienen información de usuarios cuyo acceso es restringido. Algunos de los más conocidos son Facebook, Twitter o Youtube. Estos proveedores ponen a disposición de los desarrolladores una API que soporta el protocolo de autenticación OAuth.
- Usuarios: Sin los usuarios, no existiría OAuth. Por usuario se entiende cualquier persona que tiene una cuenta de usuario en un Service Provider.
- OAuth Consumer o consumidor OAuth: Cualquier sitio o aplicación web, móvil o de escritorio que solicita permiso a un usuario para acceder a sus datos de acceso restringido que alberga un Service Provider. El usuario puede autorizar o denegar el acceso del consumer a sus datos. Es necesario que el consumer soporte el protocolo HTTP y que utilice la versión de OAuth que el Service Provider haya implementado.
API Key y Callback URL
Cada OAuth Service Provider os proporcionará un API Key (un string de letras y numeros) para identificar que las peticiones que recibe mediante su API vienen de un OAuth Consumer autorizado, es decir, vuestra aplicación.
A su vez, cada OAuth Service Provider os pedirá que indiquéis un Callback URL, es decir, una dirección URL que apunte a un archivo de vuestra aplicación el cual se encargará de procesar la respuesta de autorización (o desautorización) de acceso a los datos de la cuenta del Usuario en el OAuth Service Provider.
Diagramas de flujo de un OAuth Consumer en OAuth 1.0a
Echad un vistazo a los diagramas de flujo OAuth 1.0a de Twitter y Youtube para haceros una idea general del proceso de autenticación (el flujo es idéntico pero explicado de diferente modo). Merece la pena.
En ambos diagramas se explica el flujo de un OAuth Consumer, sin embargo, en el diagrama de Youtube se incluye también al Usuario como parte del proceso y se explica punto por punto qué está ocurriendo, de modo que es el más ilustrativo.
Podemos resumir que el flujo en la autenticación mediante OAuth 1.0a es el siguiente:
- Obtenemos un Request Token
- Solicitamos la autorización del Usuario (para acceder a los datos de su cuenta) enviandole a una página especial de login del Service Provider.
- Cambiamos el Request Token por un Access Token
- Guardamos el Access Token
Librerías OAuth para PHP
Implementar OAuth sin usar una librería o extensión de tu lenguaje es una locura y no se lo recomiendo a nadie. En la página oficial de OAuth existe una lista de las librerías y extensiones que existen para cada lenguaje, de modo que visitadla.
En margenn utilizamos el lenguaje PHP. De todas las librerías OAuth 1.0a que existen para este lenguaje os recomiendo alguna de estas tres opciones:
- Librería oauth-php. Muy utilizada. Open source.
- Librería de Zend Framework. Podeis abstraerla del framework de Zend con un poco de trabajo.
- Extensión PECL para PHP. La mejor opción de las tres ya que es la extensión oficial para PHP y es nativa. Podéis encontrar en el manual de PHP su documentación. El único inconveniente es que muy pocos proveedores de alojamiento la tienen instalada (en el caso de alojamiento compartido) por lo que la opción más utilizada es la librería oauth-php.
Todas estas librerías soportan exclusivamente la versión 1.0a del protocolo OAuth. Para la versión 2.0 (working draft, utilizada por Facebook) del protocolo no existe ninguna librería por ahora (aunque no es necesaria ya que es muy sencilla su implementación).
Implementación utilizando PHP, PECL OAuth y Twitter
En este artículo, explicaré cómo implementar el proceso de autenticación mediante OAuth 1.0a del lado de un OAuth consumer. En este caso, la aplicación web que construiremos se conectará con Twitter para leer los status de nuestra cuenta en Twitter y utilizaremos la extensión PECL OAuth de PHP comentada anteriormente ya que en nuestro servidor sí la tenemos instalada y hay muy pocos ejemplos de su uso.
Requisitos de la extensión PECL OAuth de PHP:
- PHP 5.1 o superior
- ext/hash
- libcurl con soporte HTTPS (CURL).
En primer lugar, necesitamos registrar nuestra aplicación en Twitter. Para ello visitamos la página de registro de aplicaciones y rellenamos toda la información poniendo especial atención en el campo “Callback URL”.
Una vez registrada, Twitter nos dará nuestro API key y nos indicará las URLs que tendremos que utilizar en el proceso de autenticación OAuth, a saber:
- URL para solicitar un Request Token: http://api.twitter.com/oauth/request_token
- URL para solicitar autorización del Usuario: https://api.twitter.com/oauth/authorize
- URL para cambiar el Request Token por un Access Token: https://api.twitter.com/oauth/access_token
Nota: Todos los Service Providers nos facilitan en su documentación para desarrolladores toda la información que necesitamos. En el caso de Twitter podeis encontrar toda la información aquí.
Comencemos…
En margenn utilizamos el framework CodeIgniter pero en este artículo voy a abstraer el código del framework.
Mostraré el código de cada archivo. Como he comentado suficientemente (en mi opinión) el código en cada archivo, creo que no son necesarias explicaciones adicionales.
Así mismo, os recomiendo que copies y peguéis el código de cada archivo en vuestro editor de texto favorito ya que podréis leerlo mucho mejor.
He creado 4 archivos:
- index.php: La vista inicial
- controller.php: Recibe las peticiones de la vista y actúa en consecuencia.
- callback.php: Archivo que recibirá la respuesta de Twitter a la autorización OAuth.
- class.twitter.php: Clase para la API de Twitter que implementa la autenticación OAuth.
Contenido de index.php:
<?php session_start(); ?>
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<meta charset="UTF-8">
<title>Twitter OAuth</title>
</head>
<body>
<div>
<p>Esta aplicación (OAuth Consumer) se conectará a tu cuenta en Twitter utilizando el protocolo de autenticación OAuth 1.0a y mostrará tus últimos 10 "status".</p>
<p><a href="controller.php?q=latest_statuses">Muéstrame mis 10 últimos "status" en Twitter</a></p>
</div>
</body>
</html>
Contenido de controller.php:
<?php
session_start();
// Guardamos en la sesión la URL de la página que el usuario quería visitar
$_SESSION['previous_uri'] = 'http://margenn.com' . $_SERVER['REQUEST_URI'];
// Incluimos la clase Twitter
require_once 'class.twitter.php';
// Esta función muestra por pantalla de un modo legible tanto arrays como strings json
function console($data)
{
header("Content-type: text/html; charset=UTF-8");
$format = (is_array($data)) ? 'array' : 'json';
highlight_string('<?php ' . print_r(($format === 'json' ? json_decode($data, TRUE) : $data), TRUE) . ' ?>');
exit;
}
// Lógica del controlador
if (! empty($_GET))
{
$TWITTER = new Twitter();
$query = $_GET['q'];
switch($query)
{
case 'latest_statuses':
console($TWITTER->get_latest_statuses());
break;
}
}
Contenido de la clase class.twitter.php:
<?php
// OAuth 1.0a http://dev.twitter.com/auth#at-twitter
class Twitter {
// Flag for callback controller
public $oauth_access_granted = FALSE;
// Twitter
const URL = 'http://twitter.com/';
const API_URL = 'http://api.twitter.com/';
const VERSION = '1';
const FORMAT = '.json'; // JSON como formato de respuesta
// OAuth
private $oauth;
private $consumer_key = 'XXXXXXXXXXXXXXXXX'; // === API Key
private $consumer_secret = 'XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX';
private $request_token_url = 'https://api.twitter.com/oauth/request_token';
private $access_token_url = 'https://api.twitter.com/oauth/access_token';
private $auth_url = 'https://api.twitter.com/oauth/authorize';
function __construct()
{
// Al inicializar la clase, el constructor intenta autentificar al usuario mediante OAuth.
$this->authenticate_user();
}
// OAUTH
function authenticate_user()
{
$this->oauth = new OAuth($this->consumer_key, $this->consumer_secret, OAUTH_SIG_METHOD_HMACSHA1, OAUTH_AUTH_TYPE_AUTHORIZATION);
// Si no existe en la sesión el Access Token, lo obtenemos de la BBDD si ya lo teniamos guardado o,
//si no lo tenemos guardado en la BBDD (primera vez que el usuario se autentifica mediante OAuth), lo solicitamos a Twitter.
if (! isset($_SESSION['twitter_access_token']))
{
// Pedimos a la BBDD el Access Token y el Access Token Secret del usuario para Twitter
// $twitter_access_token = ...;
// $twitter_access_token_secret = ...;
// Si existen en la BBDD, almacenamos en la sesión sus valores:
// $_SESSION['twitter_access_token'] = $twitter_access_token;
// $_SESSION['twitter_access_token_secret'] = $twitter_access_token_secret;
// Si no existen en la BBDD quiere decir que el usuario no ha autorizado el acceso a nuestra aplicación o
// que es la primera vez que se conecta a Twitter utilizando nuestra aplicación y por tanto, iniciamos el
// proceso de autorización, necesitamos el Access Token:
if ($this->get_access_token() === TRUE)
{
// Indicamos que hemos obtenido el Access Token satisfactoriamente
$this->oauth_access_granted = TRUE;
}
}
$this->oauth->setToken($_SESSION['twitter_access_token'], $_SESSION['twitter_access_token_secret']);
}
function get_access_token()
{
// Si no existe el parámetro "oauth_token" en la URL quiere decir que aún no tenemos el Request Token
// con la autorización del usuario para poder cambiarlo por un Access Token de modo que debemos solicitar primero el Request Token
if (! isset($_GET['oauth_token']))
{
$this->get_request_token();
}
else
{
$this->oauth->setToken($_SESSION['twitter_request_token'], $_SESSION['twitter_request_token_secret']);
// Cambiamos el Request Token por un Access Token
$twitter_response = $this->oauth->getAccessToken($this->access_token_url); // CURL
// Guardamos Access Token y Access Token Secret en la BBDD:
// Access Token -> $twitter_response['oauth_token']
// Access Token Secret -> $twitter_response['oauth_token_secret']
// Guardamos en sesion Access Token y Access Token Secret
$_SESSION['twitter_access_token'] = $twitter_response['oauth_token'];
$_SESSION['twitter_access_token_secret'] = $twitter_response['oauth_token_secret'];
// Informamos que la obtención del Access Token ha sido exitosa
return TRUE;
}
}
function get_request_token()
{
$twitter_response = $this->oauth->getRequestToken($this->request_token_url, 'URL/absoluta/al/archivo/callback.php'); // CURL
// Guardamos en sesion el Request Token y el Request Token Secret
$_SESSION['twitter_request_token'] = $twitter_response['oauth_token'];
$_SESSION['twitter_request_token_secret'] = $twitter_response['oauth_token_secret'];
// Solicitamos la autorización al usuario para que nuestra aplicación pueda acceder a Twitter en su nombre
$this->get_authorization();
}
function get_authorization()
{
// Redirigimos al usuario a la página de autorización (login OAuth) del Service Provider (Twitter)
// incluyendo en la URL el Request Token obtenido. Una vez que el usuario autorice o no a nuestra aplicación (Oauth Consumer),
// Twitter enviará la respuesta via GET al archivo callback.php (Callback URL)
header('Location: ' . $this->auth_url . '?oauth_token=' . $_SESSION['twitter_request_token']);
}
// Metodo "wrapper" responsable de realizar peticiones a Twitter mediante la clase OAuth de PHP (que usa CURL)
function fetch($url, $data = array(), $method = OAUTH_HTTP_METHOD_GET)
{
$this->oauth->fetch($url, $data, $method);
// Tomar medidas en caso de respuestas erroneas
// Devolvemos la respuesta
return $this->oauth->getLastResponse();
}
// Método que solicita los últimos 10 statuses en Twitter
function get_latest_statuses()
{
return $this->fetch(self::API_URL . self::VERSION . '/statuses/user_timeline' . self::FORMAT . '?count=10');
}
}
Contenido del archivo callback.php:
<?php
session_start();
if (! empty($_GET))
{
require_once 'class.twitter.php';
// En este momento, si el usuario ha autorizado el acceso a nuestra aplicación, podremos cambiar el Request Token
// que teniamos por un Access Token y si conseguimos el Access Token, la propiedad "oauth_access_granted" de la clase
// tendrá como valor el buleano TRUE.
$TWITTER = new Twitter();
// Si el usuario ha autorizado el acceso a Twitter de nuestra aplicación, redirigimos al usuario a la pagina
// en la que mostramos el contenido que esperaba. Si no ha autorizado el acceso, le informamos
if ($TWITTER->oauth_access_granted === TRUE)
{
header('Location: ' . $_SESSION['previous_uri']);
}
else
{
die('<h1>Has impedido que LA APLICACIÓN acceda a tu cuenta en Twitter!!!</h1>');
}
}
Espero que este ejemplo de implementación os sirva de ayuda. Así mismo, pongo a vuestra disposición una demo en vivo que utiliza el código que os he mostrado.
Cualquier duda sobre el proceso o el código, sugerencias e inquietudes, comentadlas ;)
Hasta el próximo artículo!!


