Web Storage API

<< Vite IndexedDB >>

Para almacenar datos de manera persistente en el cliente, tradicionalmente se han utilizado cookies. Sin embargo, tienen bastantes limitaciones, como un tamaño máximo de 4KB por dominio, o que no se envían al servidor cuando este está ejecutándose en un dominio (o puerto) diferente al de la aplicación cliente. Por lo tanto no son una opción viable cuando el servidor es una API Rest por ejemplo.

Desde hace bastantes años, existe la API Web Storage, que permite una cantidad de almacenamiento de 5MB por dominio. En este caso, es la aplicación cliente la responsable de gestionar este almacenamiento y enviar al servidor los datos que necesite.

Este sistema de almacenamiento es muy sencillo de usar. Existe un objeto global (dentro de window) llamado localStorage. A partir de dicho objeto podremos almacenar datos de forma persistente. Estos datos están relacionados con el dominio donde se está ejecutando la aplicación web cliente.

La funcionalidad de esta API es muy sencilla. Algunos ejemplos:

// Creamos la variable usuario con el valor "Pepe"
localStorage.setItem("usuario", "Pepe");
// También se puede crear así:
localStorage.usuario = "Pepe";

//Obtener el valor guardado
console.log(localStorage.getItem("usuario")); // Imprime "Pepe"
// O así
console.log(localStorage.usuario); // Imprime "Pepe"

// Borrar una variable/entrada
localStorage.removeItem("usuario");

// Borramos todos los datos almacenados
localStorage.clear();

Como has visto, los datos se guardan el parejas de clave-valor. Ambos valores se guardan como string, por lo que si queremos guardar un objeto o array (JSON), o una imagen, debemos serializarlos primero.

A no ser que borremos los datos del navegador a propósito, la próxima vez que accedamos a nuestra página, los datos guardados seguirán ahí. Si queremos datos que se borren al cerrar la página (sólo para la sesión actual), podemos usar sessionStorage (se usa de forma idéntica) en su lugar.

Usar LocalStorage para guardar credenciales

La aplicaciones web que tienen la parte cliente separada del servidor (se ejecutan en dominios o servidores web diferentes), no pueden usar cookies para que el cliente almacene información que se envía al servidor en cada petición, y por tanto no se puede utilizar una autenticación basada en sesión.

Es por ello que las API de servicios web en el servidor suelen usar autenticación por token. Se dice que una API de servicios web no maneja estados (stateless), ya que no almacena ningún tipo de información relacionada con los clientes que se conectan, es decir, que cada petición al servidor es totalmente independiente se la anterior y el cliente es el responsible de proveer al servidor con la información necesaria para completar dicha petición. Por ejemplo, con las credenciales del usuario logueado en el sistema.

Las credenciales de autenticación se suelen mandar al servidor en una cabecera HTTP. Lo estándar sería usar la cabecera Authorization, aunque esto depende de cómo esté configurado el servidor. Suelen utilizarse 3 métodos para enviar las credenciales:

  • Autenticación básica: Se envía en cada petición al servidor las credenciales del usuario (usuario y contraseña) con este formato: 'usuario:contraseña' codificadas en base64 y con el prefijo 'Basic'. Ejemplo → Authorization: "Basic YWxhZGRpbjpvcGVuc2VzYW1l".
  • Autenticación digest : Se basa en enviar varios datos con el prefijo Digest. Entre otros, un hash MD5 del usuario, la contraseña y un número aleatorio que devolvió el servidor en la última petición que se hizo.
  • Autenticación por token JWT: Es la más común. Cuando el usuario envía (mediante un formulario de login) su usuario y contraseña, el servidor genera un token, por ejemplo en formato JWT (JSON Web Token), y se lo envía como respuesta al cliente. A partir de ahora estas serán las credenciales del cliente, que deberá enviar al servidor en cada petición a partir de ese momento, precedido normalmente por el prefijo Bearer: Authorization: "Bearer TOKEN_JWT".

La versión mejorada del token JWT incluye el uso de un token adicional de refresco. Este se almacena también en el servidor y se puede invalidar en cualquier momento (cuando el usuario cierra sesión por ejemplo). En estos casos pondremos una caducidad muy corta del token JWT y podrá renovarse automáticamente a partir del token de refresco, que tendrá una duración bastante mayor antes de caducar.

Para gestionar el token desde JavaScript, lo que haremos será almacenarlo (cuando recibamos la respuesta del login) en un sistema de almacenamiento local como localStorage. A partir de ese momento, lo más sencillo será, cada vez que enviemos cualquier petición Http al servidor, comprobar si tenemos un token almacenado, y en caso afirmativo, enviarlo.

export class Http {
  async ajax(method, url, body = null) {
    const json = body && ! body instanceof FormData;
    const headers = body && json ? { "Content-Type": "application/json" } : {};
    const token = localStorage.getItem('token');
    if(token) headers.Authorization = 'Bearer ' + token;

    const resp = await fetch(url, { method, headers, json ? JSON.stringify(body) : body });

    // Procesar respuesta
  }

  // Resto de métodos
}

<< Vite IndexedDB >>