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 trabajadores del servicio y ofrece una alternativa asíncrona a document.cookie. La API facilita lo siguiente:

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

Lee la explicación

Estado actual

Paso Estado
1. Crea una explicación Completar
2. Crea un borrador inicial de la especificación Completo
**3. Recopilar comentarios y iterar en las especificaciones** **En curso**
4. Prueba de origen Pausada
5. Lanzamiento Sin iniciar

¿Cómo uso el almacén de cookies asíncrono?

Habilita la prueba de origen

Para probarla de forma local, la API se puede habilitar en la línea de comandos:

chrome --enable-blink-features=CookieStore

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

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

Probablemente no necesites cookies

Antes de analizar la nueva API, me gustaría afirmar que las cookies siguen siendo la peor primitiva de almacenamiento del cliente de la plataforma web y deben 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.

Estos son los principales motivos para evitar las cookies:

  • Las cookies llevan tu esquema de almacenamiento a tu API de backend. Cada solicitud HTTP lleva una instantánea del archivo jar de cookies. Esto facilita que los ingenieros de backend introduzcan dependencias en el formato de cookie actual. Una vez que esto suceda, tu frontend no podrá cambiar su esquema de almacenamiento sin implementar un cambio coincidente en el backend.

  • Las cookies tienen un modelo de seguridad complejo. Las funciones 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 alcances de las cookies hacen que la historia de seguridad sea mucho más compleja, y solo intentar resumirla duplicaría el tamaño de este artículo.

  • Las cookies tienen costos de rendimiento altos. Los navegadores deben incluir un resumen de tus cookies en cada solicitud HTTP, por lo que cada cambio en las cookies debe propagarse en las pilas de almacenamiento y red. Los navegadores modernos tienen implementaciones de almacenamiento de cookies muy 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 solicitudes HTTP específicas a través de la API de fetch.

Dicho esto, sigues leyendo este artículo porque tienes un buen motivo para usar las cookies…

La API de document.cookie es una fuente bastante garantizada de bloqueos 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 de disco, y hará que tu IU se bloquee.

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

await cookieStore.get('session_id');

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

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

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

// undefined

Observa, no sondees

Una aplicación popular para acceder a las cookies desde JavaScript es detectar cuándo el usuario sale y actualizar la IU. Actualmente, esto se realiza mediante el sondeo de document.cookie, que introduce bloqueos y tiene un impacto negativo en la duración de batería.

La API de Cookie Store ofrece un método alternativo para observar los 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);
  }
});

Te damos la bienvenida a los service workers

Debido al diseño síncrono, la API de document.cookie no está disponible para los trabajadores en segundo plano. La API de Cookie Store es asíncrona y, por lo tanto, se permite en los trabajadores de servicio.

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

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

Sin embargo, observar los cambios de las cookies es un poco diferente en los service workers. Activar un trabajador de servicio puede ser bastante costoso, por lo que debemos describir de forma explícita los cambios de 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 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

Próximamente.

Comentarios

Si pruebas esta API, envíanos tus comentarios. Envía los comentarios sobre la forma de la API al repositorio de especificaciones y, luego, informa los errores de implementación al componente de Blink Blink>Storage>CookiesAPI.

Nos interesa especialmente conocer las mediciones de rendimiento y los casos de uso más allá de los que se describen en la explicación.

Recursos adicionales