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 +.
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.
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