margenn

 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:

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!!

Tags: oauth php pecl twitter oauth 1.0a

Estamos siendo testigos de un revuelo considerable en torno a EllisLab, la compañía de Oregon que desarrolla los CMS comerciales ExpressionEngine y MojoMotor, los cuales corren sobre su framework de código libre CodeIgniter.

En poco menos de dos meses hemos visto cómo empleados de EllisLab, que llegaron desde la comunidad de CodeIgniter, abandonan la compañía por falta de motivación. Como es el caso de Derek Allard o más recientemente de Dan Horrigan.

Se ha hecho patente el hastío de relevantes seguidores de ExpressionEngine y su frustrante relación con EllisLab, condensado en el artículo A Plea to EllisLab, el cual recibió respuesta oficial por parte del presidente Leslie Camacho, I Hear You.

En escasas dos semanas tras la tormenta de ExpressionEngine se abre otro frente, esta vez contra CodeIgniter, representado una vez más por varios destacados miembros de la comunidad y catalizado por el artículo What happens next? de Phil Sturgeon, en respuesta al comunicado de Derek Jones, CTO de EllisLab, What’s Happening Now?.

Con todo ésto, ya hubo quien no dudó en anunciar el declive y muerte de CodeIgniter. Una oleada de agoreros lo promulgaba en twitter.

La comunidad

¿Ha muerto? No. De hecho está mejor que nunca. ¿Morirá? No a corto plazo. En mi opinión, tiene exactamente las mismas expectativas de vida que cualquier otro framework popular. Y si lo hiciera, más bien sería una reencarnación, al estilo del nuevo framework que se está gestando, FuelPHP, o como ocurriera hace ya hace más de dos años con Kohana.

CodeIgniter es sinónimo de simplicidad, rendimiento y flexibilidad. Además de tener una excelente documentación y comunidad. Tiene la virtud de haber bajado la barrera de entrada a los frameworks MVC, al igual que lo hiciera PHP como lenguaje de programación web. Incluso Rasmus Lerdorf, creador de PHP, no dudó en destacar esas cualidades frente a otros frameworks.

Todo ésto ha atraído cada vez a más gente hasta convertirlo en mainstream. Y aquí es donde la compañía EllisLab ve la oportunidad…

La compañía

EllisLab depende de CodeIgniter. Ahora es la base de sus productos. Ya no es un mero subproducto de ExpressionEngine, un experimento. Ahora CodeIgniter es la base de su estrategia, para bien o para mal. Han invertido muchos recursos en reescribir enteramente ExpressionEngine 2.0 sobre CodeIgniter (algo que según Joel Spolsky nunca deberías hacer).

Por un lado EllisLab está interesada en la comunidad de desarrolladores a la que poder atraer hacia sus productos y en el ecosistema de desarrolladores y proveedores de soluciones para sus productos. Pero por otro lado no quiere perder ni un ápice de control sobre lo que sustenta sus productos, CodeIgniter.

En esa encrucijada se encuentra.

Si ya cuando CodeIgniter no era la base de sus productos era difícil que aceptaran contribuciones ahora se plantea aún más complicado.

Por un lado desean una comunidad de desarrolladores en torno a sus productos, pero por otro lado desconfían del valor que puedan aportar a la mejora y extensión del mismo. No es una relación bidireccional. No hay comunicación. Sin comunicación no hay confianza, y sin confianza no hay expectativas ni relación posible.

Entendámonos, hablemos

Éso es lo que quería decir con “EllisLab 2.0”. Una comunicación más transparente y fluida entre compañía y comunidad. Y unos mecanismos que garanticen la participación.

El cambio que demanda la comunidad.

Y en esa dirección han empezado a dar algún pequeño y tímido paso, con el nuevo ExpressionEngine Forecast.

Veamos como gestionan el crecimiento y su comunidad.

Suerte.

Tags: codeigniter expressionengine mojomotor framework php ellislab

CodeIgniter 2.0 Ya! No esperes más.

Posted on October 20, 2010 by

Vale, parece ser que a nadie le pilla por sorpresa que la publicación de CodeIgniter 2.0 no se haya realizado todavía. Al fin y al cabo, EllisLab no se ha caracterizado precisamente por la filosofía de “Release early, release often”.

A fecha de hoy, la última versión estable 1.7.2 fue publicada hace ya más de un año (septiembre 2009). Pero eso no es nada comparado con el enorme retraso que sufrió el anticipado anuncio de ExpressionEngine 2.0 (CMS de EllisLab desarrollado sobre CodeIgniter) en febrero de 2008, según el cual estaría disponible para el verano de 2008, pero no llegaron a publicar una versión estable hasta julio de 2010. Nada menos que dos años por encima de lo previsto.

Con estos antecendentes podría parecer que CodeIgniter 2.0 queda todavía muy lejos y que por tanto no deberíamos considerarlo para código de producción.

¡Nada de eso!

CodeIgniter 2.0 a pesar de no estar oficialmente lanzado es de hecho más estable que la actual versión 1.7.2. Cabe destacar que los dos productos comerciales de EllisLab, ExpressionEgine y MojoMotor, están desarrollados sobre CodeIgniter 2.0.

En palabras de Phil Sturgeon (miembro destacado de la comunidad de CodeIgniter):

I’ll mention that every bug noticed in 1.7.2 is fixed in 2.0. That means in many ways 2.0 is actually more stable than 1.7.2, even if it is not a “final release”.


Si finalmente te decides a dar el paso visita el repositorio de CodeIgniter para descargar el código fuente o para clonarlo. Y no dejes de echar un vistazo a los siguientes artículos:

¿A qué esperas?

Tags: codeigniter development desarrollo php framework