Creando un api rest con node.js – Login con Google

Creando un api rest con node.js y mongodb – Login con Google

En una entrada anterior, os explicaba, cómo proteger vuestra API en node.js mediante tokens JWT. Hoy pasaré a comentaros la lógica necesaria para hacer el login y registro de usuarios mediante una cuenta de Google.

Podríamos utilizar cualquier otro servicio de terceros, como Facebook o Twitter, para ello solo habría que consultar su documentación correspondiente. Lo primero que tenemos que hacer, es crearnos un proyecto en la consola de apis de Google, una vez hecho esto, habilitaremos el api de Google +.

Google Api Console

Posteriormente, tendremos que ir al apartado de credenciales –> crear credenciales –> id de cliente Oauth y en este caso, seleccionaremos la opción “web”. Una vez aquí, tendremos que configurar un nombre de producto, y dos apartados más: Orígenes de javascript autorizados:

URI de origen de la aplicación cliente.

URIs de redireccionamiento autorizados:

Es la ruta de la aplicación a la que se redirecciona a los usuarios después de autenticarse en Google. A dicha ruta se añadirá el código de autorización de acceso.

Crear credenciales API Google

 

Cuando tengamos todo esto hecho, ya solo tendremos que ir a nuestro backend e implementar la lógica para consultar al api de Google + y obtener y guardar los datos de ese usuario:

 



router.post('/auth/google', function(req, res) 
{
  var accessTokenUrl = 'https://accounts.google.com/o/oauth2/token';
  var peopleApiUrl = 'https://www.googleapis.com/plus/v1/people/me/openIdConnect';
  var params = 
  {

    code: req.body.code,
    client_id: req.body.clientId,
    client_secret: config.google.clientSecret,
    redirect_uri: req.body.redirectUri,
    grant_type: 'authorization_code'
  };

  // Intercambiamos el codigo de autorizacion 
  //por el accestoken para el api de Google

  request.post(accessTokenUrl, { json: true, form: params },
  function(err, response, token) 
  {
    var accessToken = token.access_token;
    var headers = { Authorization: 'Bearer ' + accessToken };

    //Con el accestoken accedemos al api de Google +
    //y consultamos la información del perfil

    request.get({ url: peopleApiUrl, headers: headers, json: true },
    function(err, response, profile) 
    {
      if (profile.error)
      {
        return res.status(500).send({message: profile.error.message});
      }

          User.findOne({providerId:profile.sub},
          function(err,existingUser)
          {
              //si el usuario ya existe devolvemos un token
              if(existingUser)
              {                
                  tokenService.createJWT(existingUser,function(token)
                  {
                    res.json({ token: token});
                  });
              }
              else
              {
                   //el usuario no existe, lo creamos
                    var user = new User(
                    {
                      providerId : profile.sub, 
                      provider : 'google', 
                      displayName:profile.name,
                      email:profile.email,
                      pic:profile.picture,
                                      
                  });

                  user.save(function (err, saveUser, numAffected) 
                  {
                    if(err)
                      return res.status(500).send({message:err});

                      tokenService.createJWT(user,function(token)
                      {
                        res.json({ token: token});
                      });

                  });
                }
           }); 
    });
  });
});

Ahora explicaré las piezas más importantes de este código:

 

OBTENCIÓN DE TOKEN DE ACCESO A API GOOGLE +


  var accessTokenUrl = 'https://accounts.google.com/o/oauth2/token';
  var peopleApiUrl = 'https://www.googleapis.com/plus/v1/people/me/openIdConnect';
  var params = 
  {

    code: req.body.code,
    client_id: req.body.clientId,
    client_secret: config.google.clientSecret,
    redirect_uri: req.body.redirectUri,
    grant_type: 'authorization_code'
  };

  //Intercambiamos el código de autorización 
  //por el accestoken para el API de Google

  request.post(accessTokenUrl, { json: true, form: params },
  function(err, response, token) 
  {
     //aqui ya podríamos consultar al API de google + 

  });

En este bloque, llamamos al endpoint de Google + identificado por la variable “accessTokenUrl”,que recibirá varios parámetros:

  • un código de autorización (que nos enviará la aplicación cliente)
  • un id y secreto de cliente, obtenidos en la consola de apis de Google,
  • una uri de redireccionamiento, donde devolveremos nuestro token JWT
  • el parámetro "gran_type", con valor "authorization_code", que indica que queremos obtener los tokens de acceso a su API.

 

CONSULTA AL API DE GOOGLE + SOBRE EL USUARIO



    var accessToken = token.access_token;
    var headers = { Authorization: 'Bearer ' + accessToken };

    request.get({ url: peopleApiUrl, headers: headers, json: true },
    function(err, response, profile) 
    {
      if (profile.error)
      {
        return res.status(500).send({message: profile.error.message});
      }

      //en este momento ya tenemos los datos 
      //nuestro usuario logueado en la variable profile

    
    });

 

En este código podemos ver cómo hacemos una petición al API de Google +, mediante el método request.get(). En la petición de esta cabecera, enviaremos el token obtenido anteriormente que nos autorizará al uso de este API.

LÓGICA DE REGISTRO Y COMPROBACIÓN DE USUARIOS

   //buscamos el usuario en nuestra base de datos
   User.findOne({providerId:profile.sub},function(err,existingUser)
   {
              if(existingUser)
              {
                  //si el usuario ya existe devolvemos un token                  
                  tokenService.createJWT(existingUser,function(token)
                  {
                    res.json({ token: token});
                  });

              }
              else
              {
                 //el usuario no existe, lo creamos
                 var user = new User(
                 {
                      providerId : profile.sub, 
                      provider : 'google', 
                      displayName:profile.name,
                      email:profile.email,
                      pic:profile.picture,           
                     
                  });
                  //el usuario registrado no existe
                  //lo añadimos a nuestra BBDD 
                  user.save(function (err, saveUser, numAffected) 
                  {
                    if(err)
                      return res.status(500).send({message:err});

                      tokenService.createJWT(user,function(token)
                      {
                        res.json({ token: token});

                      });

                  });
               }
   }); 

En estas líneas, tenemos la lógica para buscar el usuario en nuestro base de datos, usando el método findOne() de nuestro modelo User, si el usuario no existe, lo insertamos, si no, simplemente devolvemos el token. Para crear nuestro token JWT , hemos hecho un servicio llamado tokenService.

 

GENERACIÓN DE TOKEN JWT

var jwt = require('jsonwebtoken');
var config = require('../config/config');
var moment = require('moment');


module.exports=
{
    /*creamos el token JSON*/   
    createJWT:function(user,callback) 
    {
      var payload = 
      {
        sub: user._id,
        iat: moment().unix(),
        exp: moment().add(14, 'days').unix(),
        pic:user.pic,
        email:user.email,
        displayName:user.displayName,       
      };
      
      jwt.sign(payload,'secret',{ algorithm: 'HS256' },function(token)
      {     
         callback(token);
      });
    },

};

En este servicio que hemos creado, generaremos el contenido del token JWT en función de la especificación para el mismo. En él, incluimos el campo sub (identificador de usuario), el campo iat (fecha de creación) y el campo exp (fecha de expiración del token). Además cómo se puede ver, en nuestro token podemos incluir otros campos que consideremos necesarios. Una vez que tenemos definido el contenido del mismo, lo firmamos junto con una clave privada, en este caso ‘secret’ que tendremos que configurar en nuestra aplicación.

Con este último artículo ya tenemos todas las partes necesarias para construir un API en node.js y mongodb de forma completa. Podéis revisar las anteriores partes de este tutorial aquí:

Primera parte - Creación del esqueleto del API Segunda parte - Autenticación con token

¡ Vota mi artículo !