Introducción
En este articulo explicamos el uso de Bearer token plugin para HapiJS.API publico o privado?
Un REST API recurso puede ser privado si por medio de routing lo podemos crear de uso exclusivo para una aplicacion de server. En este caso cada llamado debe ser de HapiJS UI server page a REST API. Dado que Rutha utiliza ambos, y nuestra aplicacion es un SPA (Single Page Application), la opcion de API privado no es factible. En este caso optamos por utilizar autenticacion a nivel de REST.Modelo ideal segun Twitter de autenticacion 'Application Only'
Segun Twitter, son tres pasos que se requieren:- Una aplicacion codifica la llave y secreto del usuario (consumer key and secret) en un set de credenciales. Esto es lo que adquieres en la mayoria de los OAuth APIs y el cual debes incluir en tus aplicaciones.
- Una aplicacion crea una llamada POST a un recurso o 'endpoint' OAuth2/token para intercambio de token (bearer token o token al portador).
- Para acceder al API REST, la aplicacion utiliza el token al portador para autenticar.
Usando HapiJS
En este ejemplo no explicaremos completame el flujo, solo nos enfocamos en el punto #3, en donde autenticamos llamados al API REST. El punto 1 y 2 para aplicaciones no publicas como Twitter, es posible con la autenticacion del usuario generar un token al portador permitiendo al usuario acceder el API REST que requiere ser autenticado.
En el bootloader de HapiJS
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
server.pack.register(require('hapi-auth-bearer-token'), function(err) {
server.auth.strategy('token', 'bearer-access-token', {
validateFunc: function(token, callback) {
// read from db or some place
var matched = false;
var tokenResult = { token: token };
var err = null;
if (token === 'a1b2c3') {
matched = true;
} else {
tokenResult = null;
err = { error: 'Unauthorized' };
}
return callback(err, matched, tokenResult);
}
});
// health check
server.route({
method: 'GET',
path: '/api/health',
config: {
handler: function(req, reply) {
reply('OK');
}
}
});
server.pack.register(controllers,
{
route: {
prefix: '/api'
}
}, function() {
if (!module.parent) {
server.start(function () {
console.log('Server started at port ' + server.info.port);
});
}
});
});
Registramos el plugin de 'hapi-auth-bearer-token' y la estrategia con validateFunc, que contiene la logica del token. Esto se puede reemplazar por un datastore en Redis o MongoDB que almacene la sesion, o algun otro metodo criptografico.Rutas
En las rutas, toda ruta que requiera previa autenticacion, le asignamos en config.auth el nombre de la estrategia a utilizar.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
var debug = require('debug')('users');
var createHandler = require('./v1/create.js');
exports.register = function(plugin, options, next) {
plugin.route({
method: 'POST',
path: '/v1/users',
handler: createHandler,
config: {
auth: 'token'
}
});
next();
};
exports.register.attributes = {
pkg: require('./package.json')
};
Consumidor del API
Finalmente, en el cliente nos aseguramos de enviar el header de autorizacion con el token al portador.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/*globals expect:true*/
var Hapi = require('hapi');
var server = require('./../../../lib/hapi');
describe("Users controller", function() {
it("should return HTTP 401 when no bearer header auth is found", function(done) {
server.inject({ method: 'POST', url: '/api/v1/users' }, function (res) {
expect(res.statusCode).toBe(401);
done();
});
});
it("should return HTTP 500 when bearer code is a mismatch", function(done) {
server.inject({ method: 'POST', url: '/api/v1/users', headers: { authorization: "Bearer abc" } }, function (res) {
expect(res.statusCode).toBe(500);
done();
});
});
it("should return HTTP 200 for /api/v1/users", function(done) {
server.inject({ method: 'POST', url: '/api/v1/users', headers: { authorization: "Bearer a1b2c3" } }, function (res) {
expect(res.statusCode).toBe(201);
done();
});
});
});
Hasta la siguiente entrega
Rogelio Morrell
No comments:
Post a Comment