Gracias, Wikipedia.
TreeWeb::Artículos::PHP::Login with Google
Permalink: http://www.treeweb.es/u/1281/ 01/02/2015

Login with Google

Hace unos minutos he acabado la integración de www.wikaan.com con el sistema de autenticación de Google. Delegar la autenticación en terceros es una buena opción si la base de usuarios que manejamos no es colosal y no tenemos al menos un motivo de peso como la seguridad o la imagen institucional.

Se podrían enumerar las ventajas de la delegación de autenticación en la siguiente lista:
  • Es fácil y rápido de integrar con nuestro sistema
  • Ahorra tiempo de desarrollo
  • Es más seguro y fiable que cualquier cosa que podamos desarrollar por nosotros mismos
  • Facilita la vida al usuario al no tener que gestionar otra contraseña
Si no queremos depender de un único proveedor de autenticación, siempre podemos integrar varios y que el propio usuario elija con cuál accede.

Para evitar una dependencia ciega con estos proveedores, debemos asegurarnos de que en la autenticación nos envían al menos el email del usuario. De esta forma, en caso de que el proveedor desapareciese, podríamos implementar nuestro propio sistema de autenticación sin perder la base de usuarios.

Conseguir las keys

Antes de empezar, debemos dirigirnos a https://code.google.com/apis/console/, dar de alta una aplicación y generar unas claves en el apartado 'Client ID for web applications' con lo que obtendremos un Client ID y un Client secret.

Además, hay que indicar la url de nuestra web a la que nos redirigirá Google (lo veremos más adelante).

El proceso

Lo que en principio debería haber sido un proceso sencillo, se ha empezado a complicar innecesariamente. Al buscar 'google oauth php' encontramos un SDK para integrar en nuestro código, con algunos ejemplos. Este SDK no sólo autentica, sino que permite autorizar e interactuar con infinidad de APIs de Google. Pensando que sería poco código, me encuentro con esto:
    0 
    1 -------------------------------------------------------------------------------
    2 Language                     files          blank        comment           code
    3 -------------------------------------------------------------------------------
    4 PHP                            153          10467          30218         157837
    5 XML                              1             28             41             88
    6 CSS                              1             16             14             83
    7 YAML                             1              6              2             22
    8 -------------------------------------------------------------------------------
    9 SUM:                           156          10517          30275         158030
   10 -------------------------------------------------------------------------------

   11 
Nada más y nada menos que 158K líneas de PHP, !casi 5 veces más que lo que tiene el propio Wikaan! Así que he decidido hacer una muy pequeña librería para utilizar la autenticación y sólo acceder a la información básica del usuario (nombre, email y foto).

Los chicos de Google tienen explicado cómo funciona en detalle https://developers.google.com/accounts/docs/OAuth2WebServer.

El flujo es uno de los que se especifica en el estándar de OAuth2:
En concreto estos pasos:
  1. El servidor genera una página html con un enlace que apunta a Google Auth (para generar este enlace, es necesario el Client ID e indicar los scopes, entre otras cosas).
  2. Al hacer clic, el navegador carga una página de Google pidiendo autenticación y permisos. Al aceptar, Google redirige al usuario a nuestra web con un código en la url (Authorization code).
  3. Nuestro servidor envía ese código a Google Auth, junto con el Client secret para intercambiarlo por un Access token. Con este token, podemos acceder a toda la información a la que nos han autorizado en los scopes.
  4. Con el Access token en nuestro poder, volvemos a llamar a Google para que nos devuelva la información que finalmente buscamos (en nuestro caso nombre, email y foto).
  5. Google nos devuelve la info codificada en JSON.
La info que nos devuelve tiene esta pinta:
    0 
    1 {
    2     "id": 100000000000000000000,
    3     "email": "ejemplo@gmail.com",
    4     "verified_email": 1,
    5     "name": "Fulanito Fulanitez",
    6     "given_name": "Fulanito",
    7     "family_name": "Fulanitez",
    8     "link": "https://plus.google.com/+FulanitoFulanitez",
    9     "picture": "https://lh5.googleusercontent.com/AAAAA/BBBBB/CCCCC/DDDDD/photo.jpg",
   10     "gender": "male"
   11 }

   12 

El resultado

El código generado está colgado en un gist en: https://gist.github.com/GerardoOscarJT/9c8e3b19e87bcac67342.

A continuación pongo un ejemplo con todos los fragmentos juntos, suponiendo que tenemos el archivo en nuestraweb.com/login.php :
    0 
    1 <?php
    2 
    3 $client_id 
' < your client id > ';
    4 
$client_secret ' < your client secret > ';
    5 
$redirect_uri 'http://nuestraweb.com/login.php'// Example: http://yourweb.com/callback.php
    6 
$scopes 'https://www.googleapis.com/auth/userinfo.email'// Separated by spaces
    7 
    8 
    9 
class Lib {
   10 
   11     public static function 
doRequest($method$url$headers$body) {
   12 
   13         
$opts = array('http' =>
   14             array(
   15             
'method'  => $method,
   16             
'header'  => implode("\n"$headers),
   17             
'content' => $body,
   18             )
   19         );
   20 
   21         
$context  stream_context_create($opts);
   22 
   23         return 
file_get_contents($urlfalse$context);
   24     }
   25 
   26     public static function 
doPostForm($url$headers$body) {
   27 
   28         
$headers[] = 'Content-type: application/x-www-form-urlencoded';
   29         
$headers array_unique($headers);
   30 
   31         return 
Lib::doRequest('POST'$url$headershttp_build_query($body));
   32     }
   33 
   34 }
   35 
   36 
   37 class 
GoogleAuth {
   38 
   39     private 
$config = array();
   40 
   41     public function 
__construct($client_id$client_secret$redirect_uri$scopes) {
   42         
$this->config = array(
   43             
'client_id' => $client_id,
   44             
'client_secret' => $client_secret,
   45             
'redirect_uri' => $redirect_uri,
   46             
'scopes' => $scopes,
   47         );
   48     }
   49 
   50     public function 
getAuthLink() {
   51         return
   52         
"https://accounts.google.com/o/oauth2/auth?".
   53         
"redirect_uri=".urlencode($this->config['redirect_uri']).
   54         
"&response_type=code".
   55         
"&client_id=".urlencode($this->config['client_id']).
   56         
"&scope=".urlencode($this->config['scopes']).
   57         
"&approval_prompt=force".
   58         
"&access_type=offline";
   59     }
   60 
   61     public function 
getUserInfo($code) {
   62 
   63         
// Exchange token
   64         
$result Lib::doPostForm(
   65             
'https://accounts.google.com/o/oauth2/token',
   66             array(),
   67             array(
   68                 
'code' => $_GET['code'],
   69                 
'redirect_uri' => $this->config['redirect_uri'],
   70                 
'client_id' => $this->config['client_id'],
   71                 
'scope' => $scopes,
   72                 
'client_secret' => $this->config['client_secret'],
   73                 
'grant_type' => 'authorization_code',
   74             )
   75         );
   76 
   77         if (
null == $result) {
   78             return 
null;
   79         }
   80 
   81         
$tokens json_decode($resulttrue);
   82 
   83         
// Get User Info
   84         
$result Lib::doRequest(
   85             
'GET',
   86             
'https://www.googleapis.com/oauth2/v2/userinfo',
   87             array(
   88                 
'Authorization: '.$tokens['token_type'].' '.$tokens['access_token'],
   89             ),
   90             array()
   91         );
   92 
   93         if (
null == $result) {
   94             return 
null;
   95         }
   96 
   97         return 
json_decode($resulttrue);
   98 
   99     }
  100 
  101 }
  102 
  103 
$auth = new GoogleAuth($client_id$client_secret$redirect_uri$scopes);
  104 
  105 
?>
  106 

  107 <h1>Login with Google</h1>
  108 
  109 <?php if (array_key_exists('code'$_GET)) { ?>
  110 

  111     <pre>
  112         <?php print_r($auth->getUserInfo($_GET['code'])); ?>
  113 
    </pre>
  114 
  115 <?php } else { ?>
  116 

  117     <a href="<? echo $auth->getAuthLink(); ?>">Login with Google</a>
  118 
  119 <?php ?>
  120 

  121 

  122 
Powered by TreeWeb
© TreeWeb 2010. Todos los derechos reservados