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:

  • Evita i jank sul thread principale, accedendo ai cookie in modo asincrono.
  • Evita di eseguire il polling per i cookie, perché potrebbero essere osservate modifiche ai cookie.
  • Accedere ai cookie dai service worker.

Leggi la spiegazione

Stato attuale

Passaggio Stato
1. Creare un'animazione esplicativa Completato
2. Crea la bozza iniziale delle specifiche Completato
**3. Raccogli feedback e esegui l'iterazione sulle specifiche** **In corso**
4. Prova dell'origine In pausa
5. Lancio Non avviato

Come si utilizza l'archivio cookie asincrono?

Attiva la prova dell'origine

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

chrome --enable-blink-features=CookieStore

Il passaggio 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) non hai bisogno di cookie

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

I motivi principali per cui è consigliabile evitare i cookie sono:

  • I cookie importano lo schema di archiviazione nell'API di backend. Ogni richiesta HTTP contiene un'istantanea del cookie jar. In questo modo, gli sviluppatori di backend possono facilmente introdurre dipendenze sul formato corrente dei cookie. In questo caso, il front-end non può modificare il proprio schema di archiviazione senza implementare una modifica corrispondente nel back-end.

  • I cookie hanno un modello di sicurezza complesso. Le funzionalità delle piattaforme web moderne seguono la stessa norma di origine, il che significa che ogni applicazione riceve la propria sandbox ed è completamente indipendente dalle altre applicazioni che l'utente potrebbe eseguire. Gli ambiti dei cookie rappresentano un quadro di sicurezza molto più complesso e il semplice tentativo di riassumerlo raddoppierebbe le dimensioni di questo articolo.

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

Per tutti i motivi sopra indicati, le applicazioni web moderne dovrebbero evitare i cookie e memorizzare un identificatore di sessione in IndexedDB e aggiungere esplicitamente l'identificatore 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...

L'API vettusta document.cookie è una fonte abbastanza garantita di problemi per la tua applicazione. Ad esempio, ogni volta che utilizzi il getter document.cookie, il browser deve interrompere l'esecuzione di JavaScript finché non dispone delle informazioni sui cookie che hai richiesto. Questa operazione può richiedere un hop di processo o una lettura di disco e causerà il jank della tua UI.

Una soluzione semplice a questo problema è passare dal getter document.cookie all'API Cookie Store asincrona.

await cookieStore.get('session_id');

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

Il set document.cookie può essere sostituito in modo simile. Tieni presente che è garantito che la modifica venga applicata solo dopo la risoluzione della promessa restituita da cookieStore.set.

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

// undefined

Osserva, non fare sondaggi

Un'applicazione molto diffusa per l'accesso ai cookie da JavaScript è il rilevamento dell'uscita dell'utente e l'aggiornamento dell'interfaccia utente. Attualmente questo viene fatto tramite il polling document.cookie, che introduce un ritardo 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 l'esecuzione di 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);
  }
});

Dai il benvenuto ai service worker

A causa del design sincrono, l'API document.cookie non è stata messa a disposizione dei service worker. L'API Cookie Store è asincrona e pertanto è consentita nei worker di servizio.

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 worker di servizio. Riattivare un worker può essere piuttosto costoso, quindi dobbiamo descrivere esplicitamente le modifiche ai cookie che interessano il worker.

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

// 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 i feedback sulla forma dell'API al repository delle specifiche e segnala i bug di implementazione al componente Blink Blink>Storage>CookiesAPI.

Ci interessano in modo particolare le misurazioni del rendimento e i casi d'uso diversi da quelli descritti nella spiegazione.

Risorse aggiuntive