Accesso asincrono ai cookie HTTP

Victor Costan

Che cos'è l'API Cookie Store?

L'API Cookie Store espone i cookie HTTP ai service worker e offre un'alternativa asincrona a document.cookie. L'API semplifica le operazioni:

  • Evita di danneggiare il thread principale accedendo ai cookie in modo asincrono.
  • Evita di eseguire il polling per i cookie, perché è possibile che si verifichino modifiche ai cookie.
  • Accedi ai cookie dei Service worker.

Leggi il messaggio esplicativo

Stato attuale

Passaggio Stato
1. Crea messaggio esplicativo Completato
2. Crea bozza iniziale della specifica Completato
**3. Raccogli feedback e ottimizza le specifiche** **In corso**
4. Prova dell'origine In pausa
5. Avvia Not started

Come faccio a utilizzare l'archivio dei cookie asincroni?

Abilita la prova dell'origine

Per provarla localmente, l'API può essere abilitata dalla riga di comando:

chrome --enable-blink-features=CookieStore

La trasmissione di questo flag nella riga di comando abilita l'API a livello globale in Chrome per la sessione corrente.

In alternativa, puoi attivare il flag #enable-experimental-web-platform-features in chrome://flags.

Probabilmente i cookie non sono necessari

Prima di entrare nel vivo della nuova API, vorrei affermare che i cookie sono ancora la peggiore primitiva di archiviazione lato client della piattaforma web e devono comunque essere utilizzati come ultima risorsa. Non è un incidente: i cookie sono stati il primo meccanismo di archiviazione lato client del web e da allora abbiamo imparato molto.

Di seguito sono riportati i motivi principali per evitare i cookie:

  • I cookie portano lo schema di archiviazione nell'API di backend. Ogni richiesta HTTP trasporta uno snapshot dell'archivio cookie. In questo modo, per gli ingegneri di backend è più semplice introdurre dipendenze nel formato dei cookie attuale. Una volta eseguita questa operazione, il front-end non può modificare il proprio schema di archiviazione senza eseguire il deployment di una modifica corrispondente al backend.

  • I cookie hanno un modello di sicurezza complesso. Le moderne funzionalità della piattaforma web seguono gli stessi criteri di origine, ovvero ogni applicazione ha la propria sandbox ed è completamente indipendente da altre applicazioni che l'utente potrebbe eseguire. Gli ambiti dei cookie rendono la storia di sicurezza molto più complessa e il solo tentativo di riassumere, raddoppia le dimensioni di questo articolo.

  • I cookie hanno costi ad alte prestazioni. I browser devono includere uno snapshot dei cookie in ogni richiesta HTTP, pertanto ogni modifica ai cookie deve essere propagata negli stack di archiviazione e di rete. I browser moderni utilizzano implementazioni di archivi di cookie altamente ottimizzate, ma non saremo mai in grado di rendere i cookie efficienti come gli altri meccanismi di archiviazione, che non hanno bisogno di comunicare con lo stack di rete.

Per tutti i motivi di cui sopra, le applicazioni web moderne dovrebbero evitare i cookie e archiviare invece un identificatore di sessione in IndexedDB e aggiungerlo esplicitamente all'intestazione o al corpo di richieste HTTP specifiche, tramite l'API fetch.

Detto questo, stai ancora leggendo questo articolo perché hai un buon motivo per usare i cookie...

La venerabile API document.cookie è una fonte abbastanza garantita di jank per la tua applicazione. Ad esempio, ogni volta che utilizzi il getter document.cookie, il browser deve interrompere l'esecuzione di JavaScript finché non riceve le informazioni sui cookie richieste. Questa operazione può richiedere un hop di processo o una lettura del disco e causa il jank della tua UI.

Una soluzione semplice per questo problema consiste nel passare dal getter document.cookie all'API asincrona di Cookie Store.

await cookieStore.get('session_id');

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

Il setter document.cookie può essere sostituito in modo simile. Tieni presente che l'applicazione della modifica è garantita solo dopo la risoluzione della promessa restituita da cookieStore.set.

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

// undefined

Osserva, non sondaggi

Un'applicazione molto diffusa per l'accesso ai cookie da JavaScript rileva quando l'utente si disconnette e aggiorna l'interfaccia utente. Al momento, questo viene eseguito mediante il polling document.cookie, che introduce jank e ha un impatto negativo sulla durata della batteria.

L'API Cookie Store offre un metodo alternativo per osservare le modifiche ai cookie, che non richiede il polling.

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);
  }
});

Accogli i service worker

A causa della progettazione sincrona, l'API document.cookie non è stata resa disponibile per i lavoratori dei servizi. L'API Cookie Store è asincrona e, di conseguenza, è consentita nei service worker.

L'interazione con i cookie funziona allo stesso modo nei contesti dei documenti e nei service worker.

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

Tuttavia, l'osservazione delle modifiche ai cookie è leggermente diversa nei service worker. Riattivare un service worker può essere piuttosto costoso, quindi dobbiamo descrivere esplicitamente le modifiche ai cookie a cui il worker è interessato.

Nell'esempio seguente, un'applicazione che utilizza IndexedDB per memorizzare nella cache i dati utente monitora le modifiche al cookie di sessione e ignora i dati memorizzati nella cache quando l'utente si disconnette.

// 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;
    }
  }
});

best practice

Disponibile a breve.

Feedback

Se provi questa API, facci sapere cosa ne pensi. Invia il feedback sulla forma dell'API al repository delle specifiche e segnala i bug di implementazione al componente Blink Blink>Storage>CookiesAPI.

Siamo particolarmente interessati a conoscere le misurazioni delle prestazioni e i casi d'uso oltre a quelli descritti nell'explainer.

Risorse aggiuntive