Archivo de la etiqueta: RESTful

RESTful APIs with Node.js and Express.js, and Backbone.js client side

Una de las cosas más divertidas que me encontré para hacer durante el desarrollo de ttTodoMVC fue sin duda pensar en un esquema de autenticación stateless contra el servidor, pese a que mi primer idea fue enviar continuamente el nombre de usuario y contraseña por cada petición, luego de muy poca investigación entendí que no era lo optimo en lo más minimo, por lo que comencé a buscar alternativas hasta que me encontré con:

http://www.thebuzzmedia.com/designing-a-secure-rest-api-without-oauth-authentication/

que explica como funciona la autenticación en servicios tan utilizados como AWS (Amazon Web Services) como tal me resultó más que interesante y decidí implementar este método.

El Método

La forma de autenticación no es más que el viejo y conocido esquema de publicKey/privateKey donde existe una clave publica que puede ser conocida por cualquiera (y por tanto enviada en todas las peticiones, resistiendo spoofing) y una clave privada que una vez creada solo es conocida por los dos puntos de la comunicación y JAMAS es enviada durante la comunicación entre ambos puntos, ni siquiera aún cifrada. Entonces para confirmar la autenticidad de cada petición solo es necesario que las peticiones sean «firmadas» digitalmente. De que manera firmamos la petición? Simple tomamos todos los parámetros (o incluso todos los nombres de los parámetros) utilizando nuestra clave privada, para esto utilizamos el protocolo de cifrado SHA1 HMAC que nos permite cifrar un conjunto de datos con una clave privada.

Por lo que construimos un conjunto de datos a partir de nuestros parámetros y los ciframos utilizando nuestra clave privada para obtener nuestra firma electronica y adjuntamos tanto nuestra clave pública como nuestra firma al conjunto de parámetros enviados en cada petición, y lo enviamos todo.

Luego, del lado del servidor simplemente tomamos el listado de parámetros construimos nuevamente nuestro conjunto de datos y buscamos en la base de datos la clave privada a partir de la clave pública, y generamos nuevamente la firma para el conjunto de datos enviados, finalmente comprobamos la igualdad entre ambas firmas y si hay coincidencia, entonces la petición es autentica!.

Para resumir

Del lado del cliente:

  • Tomamos todos los parámetros (y nombres) a enviar, y construimos un conjunto de datos
  • Firmamos el conjunto de datos utilizando una encriptación SHA1 HMAC y la clave privada
  • Adjuntamos la clave pública y la firma a los parámetros de la petición
  • Enviamos la petición

Del lado del Servidor:

  • Tomamos todos los parámetros (menos la public key y la signature (o firma)) y construimos un conjunto de datos
  • Con la Public Key buscamos la Private Key en la base de datos
  • Generamos nuevamente la firma (signature) utilizando SHA1 HMAC y la private Key
  • Comparamos ambas firmas, si coinciden, la petición ha sido autenticada!

al describirla así puede sonar simple pero a la hora de la implementación, a veces son necesarias algunas estrategias a tener en cuenta.

veamos como implementé esto dentro de ttTodoMVC.

The SHA1 algorithm

Si hablamos de una implementación SHA1 sobre JavaScript no hablamos de otra librería más que de:

http://caligatio.github.com/jsSHA/

que implementa toda la familia de protocolos SHA, incluyendo SHA1 en

https://github.com/Caligatio/jsSHA/blob/master/src/sha1.js

aunque no es AMD compatible, pero facilmente puede hacerse compatible, simplemente agregando al final:

root = this;

if (typeof exports !== ‘undefined’) {
exports = jsSHA;
} else {
root.jsSHA = jsSHA;
}

y encerrandolo en una función wrapper para que this esté asociado a la window del browser.

del lado del servidor simplemente agregamos

module.exports= jsSHA;

y esto lo hace 100% compatible con Common.js

Luego, para usarlo es simplemente necesario crear un Objeto jsSHA de la siguiente manera

shaObj = new jsSHA(data,»ASCII»);
signature = shaObj.getHMAC($(privateKey, «ASCII»,»HEX»)

y esto nos da la firma digital SHA1 HMAC por private Key,

Con SHA1 tanto del lado del servidor como del cliente, estamos listos para la implementación

Client Side

Backbone.js ofrece muy pocos puntos para definir estrategias de acceso a un servicio RESTful, en casos simples, totalmente fuera del MVC propuesto por Backbone.js, como el login solo tratamos contra una url enviando una petición ajax con jQuery como en:

https://github.com/picanteverde/ttTodoMVC/blob/RESTFulAuth/public/app/modules/login.js

donde podemos ver simplemente generamos un conjunto de datos a firmar utilizando la publicKey como parte de los parametros y generamos un parámetro randomico para simplemente generar ruido en cualquier intento de ubicar nuestra private key

var rnd = Math.random()*1000,
sign = «publicKey=» + $(«#login-username»).val() + «rnd=»+rnd,
shaObj = new jsSHA(sign,»ASCII»);

en este caso utilizo por cuestiones de simplificación solo el nombre de usuario como publicKey, aunque tambien podría haber sido utilizado un Hash SHA1 del mismo nombre de usuario

Acto seguido generamos nuestra firma electronica SHA1 HMAC y la adjuntamos al conjunto de parámetros enviados a la API

$.ajax({
url: «/api/auth»,
type: «POST»,
dataType: «json»,
data: {
publicKey: $(«#login-username»).val(),
rnd: rnd,
signature: shaObj.getHMAC($(«#login-password»).val(), «ASCII»,»HEX»)
},

y enviamos la petición.

ahora a la hora de trabajar con Modelos y Colleciones de Backbone.js es necesario sobre escribir Backbone.Sync, aunque el mismo podría ser escrito de mejor manera esto es lo que tengo hasta ahora:

// Ensure that we have the appropriate request data.
if (!options.data && model && (method == ‘create’ || method == ‘update’ || method == ‘delete’)) {
params.contentType = ‘application/json’;
var data = model.toJSON();
if(publicKey && privateKey){
var key, sign =»», shaObj;
data.publicKey = publicKey;
for(key in data){
if(data.hasOwnProperty(key)){
sign += key + «=» + data[key];
}
}
shaObj = new jsSHA(sign, «ASCII»);
sign = shaObj.getHMAC(privateKey,»ASCII»,»HEX»);
data.signature = sign;
}
params.data = JSON.stringify(data);
}

agregamos delete para enviar un payload en el verbo «delete» de http y de estar definidos public y private key simplemente generamos la signature y agregamos ambos parámetros a la petición

tambien como pueden ver mas adelante hacemos lo mismo con al verbo «read»

if(method==’read’ && publicKey && privateKey){
var rnd = Math.random()*1000,
sign = «publicKey=» + publicKey + «rnd=»+rnd,
shaObj = new jsSHA(sign,»ASCII»);
if(!params.data){
params.data ={};
}
params.data.publicKey = publicKey;
params.data.rnd = rnd;
params.data.signature = shaObj.getHMAC(privateKey, «ASCII», «HEX»);
}

de esta forma firmamos todos los verbos.

Server Side

en el server side es mucho, mucho, más fácil gracias a la idea de middleware traída a expresss.js por connect.js

de esta forma simplemente podemos deifinir una función que se encargue de autenticar nuestra petición

var authorize = function(req, res, next){
var container = null, key, sign,shaObj;
if(req.body.publicKey && req.body.signature){
container = req.body;
}
if(req.query.publicKey && req.query.signature){
container = req.query;
}
if(!container){
res.status(401);
res.json({«error»:»Authentication required!»});
}else{
db.getUser(container.publicKey,function(err, user){
if(!user){
res.status(401);
res.json({«error»:»Authentication required, invalid publicKey»});
}else{
sign = «»;
for(key in container){
if(container.hasOwnProperty(key) && key !== «signature»){
sign += key + «=» +container[key];
}
}
shaObj = new jsSHA(sign, «ASCII»);
sign = shaObj.getHMAC(user.password,»ASCII»,»HEX»);
if(sign === container.signature){
next();
}else{
res.status(401);
res.json({«error»:»Authentication required, Authentication failed!»});
};
}
});

de esta forma via middle ware podemos autenticar cada petición simplemente añadiendo la función como middleware para cada petición que sea necesario autenticar

app.post(«/api/auth»,[authorize], function(req, res, next){
res.json({«auth»:true, «username»: req.body.username});
});

de esta forma podemos autenticar cada petición del lado del servidor sin tener que requerir el password por cada petición solo una signature.

Peróooooooooooooooooooooon por el largo post pero no había forma de explicarlo todo sin ser tan extenso.

Saludoooos

Etiquetado , , , , ,

Backbone.js, Backbone boilerplate, and RESTful Apis

Estuve jugando/peleando con Backbone.js, Backbone.js Boilerplate, el nunca bien ponderado Backbone Layout y RESTful Services ultimamente, así que decidí darles un paseo por estas tecnologías en una aplicación de prueba que hice en mi repositorio:

https://github.com/picanteverde/ttTodoMVC

Donde intenté dejar plasmados los siguientes requerimientos:

  • One Page App: una aplicación cliente 100% contra un RESTful service
  • Todo List: simple administración de «Tareas para hacer»
  • Backbone.js: usando Backbone.js como framework principal de la aplicación
  • Boilerplate: haciendola profesional con boilerplate, incluyendo Backbone Layout, Require.js, and Async Handlebars templates
  • RESTful: del lado del servidor solo una API stateless (sin estado)
  • MongoDB: almacenando todo en MongoDB
  • Login & SignIn: multiples perfiles

Con estas ideas en mente intenté hacerlo lo mejor que pude. y el resultado es la aplicación en el repositorio.

Como podrán ver hay 2 branchs

master branch: donde trabajé el servidor en un sentido stateful, aunque debatible porque almacené la información en un hash dentro de la session que es enviada junto con cada petición al servidor

RESTFulAuth: donde utilicé una encriptado  SHA1 para firmar cada una de las peticiones a la API, la pueden encontrar en:

https://github.com/picanteverde/ttTodoMVC/tree/RESTFulAuth

que fue sin duda una de las cosas más divertidas y desafiantes que encontré durante el desarrollo.

Más adelante le voy a dedicar una serie de post a cada parte de la aplicación para contar mi experiencia con cada reto que me encontré por ahora los dejo revisando el código e intentando hacerlo andar.

Saludos

Etiquetado , , , , , , ,