Acceso asíncrono a cookies HTTP

Victor Costan

¿Qué es la API de Cookie Store?

La API de Cookie Store expone cookies HTTP a los service workers y ofrece una alternativa asíncrona a document.cookie. La API facilita las siguientes tareas:

  • Evita los bloqueos en el subproceso principal mediante el acceso a cookies de forma asíncrona.
  • Evita consultar las cookies, ya que se pueden observar cambios en ellas.
  • Accede a las cookies de los service workers.

Leer la explicación

Estado actual

Step Estado
1. Crear explicación Completo
2. Crea el borrador inicial de la especificación Completo
**3. Recopila comentarios y, luego, itera sobre las especificaciones.** **En curso**
4. Prueba de origen Pausada
5. Lanzamiento No iniciada

¿Cómo utilizo el almacén de cookies asíncrona?

Habilitar la prueba de origen

Para probarlo de manera local, puedes habilitar la API en la línea de comandos:

chrome --enable-blink-features=CookieStore

Pasar esta marca en la línea de comandos habilita la API globalmente en Chrome para la sesión actual.

Como alternativa, puedes habilitar la marca #enable-experimental-web-platform-features en chrome://flags.

Es probable que no necesites las cookies

Antes de hablar de la nueva API, me gustaría aclarar que las cookies siguen siendo la peor primitiva de almacenamiento del cliente de la plataforma web y deberían usarse como último recurso. Esto no es un accidente. Las cookies fueron el primer mecanismo de almacenamiento del cliente de la Web, y hemos aprendido mucho desde entonces.

Los motivos principales para evitar las cookies son los siguientes:

  • Las cookies llevan tu esquema de almacenamiento a tu API de backend. Cada solicitud HTTP transporta una instantánea del jar de cookies. Esto facilita a los ingenieros de backend la introducción de dependencias en el formato de cookie actual. Una vez que esto sucede, el frontend no puede cambiar su esquema de almacenamiento sin implementar un cambio coincidente en el backend.

  • Las cookies tienen un modelo de seguridad complejo. Las características de la plataforma web moderna siguen la misma política de origen, lo que significa que cada aplicación tiene su propia zona de pruebas y es completamente independiente de otras aplicaciones que el usuario pueda estar ejecutando. Los permisos de cookies generan una historia de seguridad mucho más compleja, en la que el simple hecho de intentar resumir eso duplicaría el tamaño de este artículo.

  • Las cookies tienen costos de alto rendimiento. Los navegadores deben incluir una instantánea de las cookies en cada solicitud HTTP, por lo que cada cambio en las cookies debe propagarse a través de las pilas de almacenamiento y red. Los navegadores modernos tienen implementaciones de almacenamiento de cookies altamente optimizadas, pero nunca podremos hacer que las cookies sean tan eficientes como los otros mecanismos de almacenamiento, que no necesitan comunicarse con la pila de red.

Por todos los motivos anteriores, las aplicaciones web modernas deben evitar las cookies y, en su lugar, almacenar un identificador de sesión en IndexedDB y agregar el identificador de forma explícita al encabezado o al cuerpo de las solicitudes HTTP específicas mediante la API de fetch.

Dicho esto, sigues leyendo este artículo porque tienes una buena razón para usar cookies...

La respetada API de document.cookie es una fuente de bloqueo bastante garantizada para tu aplicación. Por ejemplo, cada vez que usas el método get document.cookie, el navegador debe dejar de ejecutar JavaScript hasta que tenga la información de la cookie que solicitaste. Esto puede requerir un salto de proceso o una lectura en el disco, y hará que se bloquee la IU.

Una solución sencilla para este problema es cambiar del método get document.cookie a la API asíncrona de Cookie Store.

await cookieStore.get('session_id');

// {
//   domain: "example.com",
//   expires: 1593745721000,
//   name: "session_id",
//   path: "/",
//   sameSite: "unrestricted",
//   secure: true,
//   value: "yxlgco2xtqb.ly25tv3tkb8"
// }

El método set document.cookie se puede reemplazar de manera similar. Ten en cuenta que solo se garantiza que se aplique el cambio después de que se resuelva la promesa que muestra cookieStore.set.

await cookieStore.set({name: 'opt_out', value: '1'});

// undefined

Observa, no sondear

Una aplicación popular para acceder a cookies desde JavaScript es detectar cuándo el usuario sale de su cuenta y actualizar la IU. Para ello, se sondea document.cookie, que introduce un bloqueo y tiene un impacto negativo en la duración de batería.

La API de Cookie Store ofrece un método alternativo para observar cambios en las cookies, que no requiere sondeo.

cookieStore.addEventListener('change', event => {
  for (const cookie of event.changed) {
    if (cookie.name === 'session_id') sessionCookieChanged(cookie.value);
  }
  for (const cookie of event.deleted) {
    if (cookie.name === 'session_id') sessionCookieChanged(null);
  }
});

Bienvenida a los service workers

Debido al diseño síncrono, la API de document.cookie no está disponible para los service worker. La API de Cookie Store es asíncrona y, por lo tanto, está permitida en los service workers.

La interacción con las cookies funciona de la misma manera en los contextos de documentos y en los service workers.

// Works in documents and service workers.
async function logOut() {
  await cookieStore.delete('session_id');
}

Sin embargo, observar los cambios en las cookies es un poco diferente en los service workers. Activar un service worker puede ser bastante costoso, por lo que tenemos que describir explícitamente los cambios en las cookies que le interesan al trabajador.

En el siguiente ejemplo, una aplicación que usa IndexedDB para almacenar en caché los datos del usuario supervisa los cambios en la cookie de sesión y descarta los datos almacenados en caché cuando el usuario cierra la sesión.

// Specify the cookie changes we're interested in during the install event.
self.addEventListener('install', event => {
  event.waitUntil(cookieStore.subscribeToChanges([{name: 'session_id'}]));
});

// Delete cached data when the user logs out.
self.addEventListener('cookiechange', event => {
  for (const cookie of event.deleted) {
    if (cookie.name === 'session_id') {
      indexedDB.deleteDatabase('user_cache');
      break;
    }
  }
});

Prácticas recomendadas

Disponible próximamente

Comentarios

Si pruebas esta API, danos tu opinión. Dirige tus comentarios sobre la forma de la API al repositorio de especificaciones y, luego, informa los errores de implementación en el componente Blink>Storage>CookiesAPI de Blink.

En particular, nos interesa aprender sobre las mediciones de rendimiento y los casos de uso más allá de los descritos en la explicación.

Recursos adicionales