La autenticación es una de las partes más importantes de nuestra aplicación. En este tutorial vamos a ver en qué se basa la autenticación basada en tokens y las diferencias con la autenticación tradicional basada en servidor.

Problemas de la autenticación tradicional

En la autenticación tradicional, el usuario se logea con sus credenciales, el servidor las valida las y guarda información del mismo en objetos de sesión, bien en memoria o en disco.

Esto tiene los siguientes problemas:

1. Sobrecarga

Cuando un usuario se autentica, el servidor necesita recordarle de alguna manera, normalmente guardando la información en memoria. Cuando hay muchos usuarios conectados, se aumenta la sobrecarga del servidor.

2. Escalabilidad

Desde el momento que guardamos información del usuario en la sesión, tenemos problemas de escalabilidad. Imaginemos que nuestra aplicación necesita autoescalar para dar respuesta a picos de peticiones y balancear la carga entre varios servidores.

Como estamos guardando el usuario en la sesión de un servidor, si la petición se envía a otro, necesitará volver a logearse.

Esto se puede resolver utilizando la técnica conocida como Sticky Session pero no es lo óptimo. Con autenticación basada en tokens, esto se soluciona de forma natural.

3. CORS

Lo más normal es que queramos que nuestros datos puedan ser consumidos desde múltiple dispositivos móviles (tabletas, smatphones, etc…). En este escenario es importante preocuparse por lo que se denomina CORS: cross-origin resource sharing o compartición de recursos de distintos orígenes.

Cuando utilizamos AJAX para obtener datos de nuestra aplicación, nos podemos encontrar con peticiones no autorizadas.

4. CSRF

Cross-site request forgery (CSRF) es un tipo vulnerabilidad que afecta a aplicaciones web y que se basa en explotar la confianza que los sitios web tienen con sus usuarios.

Con autenticación tradicional, a no ser que utilicemos ténicas para prevenirlo, estamos expuestos a este tipo de ataques. Con la autenticación basada en tokens, evitamos este tipo de problemas.

Autenticación basada en tokens

La autenticación basada en tokens es stateless. Esto quiere decir que no guarda ningún tipo de información del usuario en el servidor ni en la sesión.

Este tipo de autenticación envía un token en cada petición a través de las cabeceras HTTP en lugar de mantener la información de autenticación en sesiones o cookies.

De esta forma, no se guarda ningún estado entre las diferentes peticiones de nuestra aplicación y nuestros datos pueden ser consumidos desde diferentes tipos de clientes.

¿Qué beneficios aporta?

Escalabilidad

Los tokens son guardados a nivel de cliente, por tanto, nuestro balanceador puede enviar la petición a cualquiera de los servidores qu tengamos disponibles en nuestra arquitectura.

Seguridad

Al usar este tipo de autenticación, no tenemos que preocuparnos por la vulnerabilidad CSRF ya que no disponemos de ninguna sesión sensible de ser suplantada.

Extensibilidad

Con esta ténica podemos proporcionar a nuestra aplicación permisos de autenticación mediante servicios de terceros, como Twitter o Facebook.

Incluso podríamos construir nuestra propia API y otorgar unos permisos especiales a nuestros usuarios para que puedan dar acceso a sus datos desde otra aplicación.

Multiplataforma y CORS

No tendremos problemas a la hora de acceder desde diferentes dominios, clientes, etc… a los datos o recursos que proporciona nuestra aplicación.

JSON Web Tokens (JWT)

JWT es un sistema de codificación que cifra un objeto JSON utilizando JSON Web Signature y que es transferido entre dos partes para establecer una comunicación segura.

Un token con JWT tiene la siguiente pinta:

eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOjEyMzQ1Njc4OTAsIm5hbWUiOiJKb2huIERvZSIsImFkbWluIjp0cnVlfQ.eoaDVGTClRdfxUZXiPs3f8FmJDkDE_VCQFXqKxpLsts

y está compuesto de 3 partes separadas por un .:

  • header

  • payload

  • signature

1. Cabecera

La cabecera (header) está compuesta de 2 partes:

  • el tipo, que en este caso es JWT

  • el algoritmo hash que se va a utilizar, como por ejemplo HS256 (HMAC SHA256)

Un ejemplo de cabecera puede ser:

{
    "typ": "JWT",
      "alg": "HS256"
}

2. Información útil

Esta segunda parte es el payload y es donde vamos a poner la información que queremos transmitir, así como otra información sobre nuestra token.

Existe múltiple información que podemos proporcionar, como el emisor, cuando expira el token, etc… A continuación se detalla la información que podemos enviar.

Esta es la información registrada y que podemos enviar:

  • iss: el emisor del token, normalmente suele ser una URL (mi-api-server.com)

  • sub: un identificador de usuario (khernandez)

  • aud: el que va a consumir el token, normalmente una URL (http://mi-aplicacion.com)

  • exp: valor numérico que define cuando va a expirar el token

  • nbf: valor numérico que determina el momento desde el que el tokken ya no puede ser aceptado

  • iat: valor numérico que define cuando se creó el token. Útil para conocer el cuanto tiempo lleva creado el token.

  • jti: identificador único del token. Permite que un token no sea reproducido. Muy útil para tokens de un sólo uso.

Además, podemos incluir información pública que podemos crear a nuestro gusto. De esta forma, el payload del token puede tener la siguiente pinta:

{
    "iss": "mi-api-server.com", 
    "sub": "khernandez",
    "exp": 1300819380, 
    "name": "Koldo Hernández", 
    "admin": true
}

En este ejemplo vemos que combinamos información registrada (iss, sub y exp) con información pública.

3. Signature

La tercera parte del token es la firma y es un valor codificado creado a partir del header, el payload y una clave secreta:

var encodedString = base64UrlEncode(header) + "." + base64UrlEncode(payload); 

HMACSHA256(encodedString, 'miclavesupersecreta');



En otro post mostraré cómo autenticar nuestra API desarrollada con ExpressJS utilizando esta técnica.