HTTP Cookie への非同期アクセス

Victor Costan

Cookie Store API とは何ですか?

Cookie Store API は、HTTP Cookie をサービス ワーカーに公開し、document.cookie の非同期代替手段を提供します。この API を使用すると、次のことが容易になります。

  • Cookie に非同期でアクセスして、メインスレッドでのジャンクを回避します。
  • Cookie の変更を検出できるため、Cookie のポーリングは避けてください。
  • Service Worker から Cookie にアクセスする。

説明を読む

現在のステータス

ステップ ステータス
1. 説明を作成する 完了
2. 仕様の最初の下書きを作成する 完了
**3. フィードバックを収集し、仕様を反復処理する** **進行中**
4. オリジン トライアル 一時停止
5. リリース 開始していません

非同期 Cookie ストアを使用するにはどうすればよいですか?

オリジン トライアルを有効にする

ローカルで試すには、コマンドラインで API を有効にします。

chrome --enable-blink-features=CookieStore

コマンドラインでこのフラグを渡すと、現在のセッションの Chrome で API がグローバルに有効になります。

また、chrome://flags#enable-experimental-web-platform-features フラグを有効にすることもできます。

Cookie は不要(と思われる)

新しい API について説明する前に、Cookie は引き続きウェブ プラットフォームで最も不適切なクライアントサイド ストレージ プリミティブであり、最後の手段として使用すべきであることを明記しておきます。これは偶然ではありません。Cookie はウェブ初のクライアントサイド ストレージ メカニズムであり、それ以降、多くのことを学んできました。

クッキーを避ける主な理由は次のとおりです。

  • Cookie を使用すると、ストレージ スキーマをバックエンド API に持ち込むことができます。各 HTTP リクエストは、Cookie ジャーのスナップショットを運びます。これにより、バックエンド エンジニアは現在の Cookie 形式に依存関係を簡単に導入できます。このような状態になると、フロントエンドは、バックエンドに一致する変更をデプロイせずにストレージ スキーマを変更できなくなります。

  • Cookie のセキュリティ モデルは複雑です。最新のウェブ プラットフォーム機能は同じオリジン ポリシーに従います。つまり、各アプリケーションは独自のサンドボックスを取得し、ユーザーが実行している他のアプリケーションとは完全に独立しています。Cookie スコープはセキュリティの観点から非常に複雑であり、その概要を説明するだけでもこの記事の 2 倍の長さになります。

  • Cookie はパフォーマンス コストが高いため、ブラウザはすべての HTTP リクエストに Cookie のスナップショットを含める必要があるため、Cookie に対するすべての変更をストレージ スタックとネットワーク スタックに伝播する必要があります。最新のブラウザでは Cookie ストアの実装が高度に最適化されていますが、ネットワーク スタックと通信する必要がない他のストレージ メカニズムほど Cookie を効率的にすることはできません。

上記の理由から、最新のウェブ アプリケーションでは、Cookie の使用を避け、代わりにセッション ID を IndexedDB に保存し、fetch API を介して特定の HTTP リクエストのヘッダーまたは本文に ID を明示的に追加する必要があります。

とはいえ、Cookie を使用する正当な理由があるため、この記事を読み続けているのでしょう。

古くからある document.cookie API は、アプリのジャンクの原因になりやすいものです。たとえば、document.cookie ゲッターを使用するたびに、ブラウザはリクエストした Cookie 情報を取得するまで JavaScript の実行を停止する必要があります。これにより、プロセスのホップやディスクの読み取りが発生し、UI がジャンクする可能性があります。

この問題を簡単に解決するには、document.cookie ゲッターから非同期 Cookie Store API に切り替えます。

await cookieStore.get('session_id');

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

document.cookie セッターも同様に置き換えることができます。変更が適用されるのは、cookieStore.set によって返された Promise が解決された後のみです。

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

// undefined

ポーリングではなくモニタリング

JavaScript から Cookie にアクセスする一般的なアプリケーションは、ユーザーがログアウトしたときに検出して UI を更新するものです。現在、これは document.cookie のポーリングによって行われていますが、ジャンクが発生し、バッテリー駆動時間に悪影響を及ぼします。

Cookie Store API は、ポーリングを必要としない、Cookie の変更を検出する代替方法を提供します。

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

サービス ワーカーの歓迎

同期設計のため、document.cookie API はサービス ワーカーで使用できません。Cookie Store API は非同期であるため、サービス ワーカーで使用できます。

ドキュメント コンテキストとサービス ワーカーで、Cookie の操作は同じ方法で行われます。

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

ただし、Service Worker での Cookie の変更の監視は少し異なります。Service Worker の起動は非常にコストがかかるため、ワーカーが関心を持っている Cookie の変更を明示的に記述する必要があります。

次の例では、IndexedDB を使用してユーザーデータをキャッシュに保存するアプリケーションが、セッション Cookie の変更をモニタリングし、ユーザーがログオフするとキャッシュに保存されたデータを破棄します。

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

ベスト プラクティス

近日提供予定。

フィードバック

この API をお試しいただいた場合は、ぜひご感想をお聞かせください。API の形状に関するフィードバックは 仕様リポジトリに送信してください。実装バグは Blink>Storage>CookiesAPI Blink コンポーネントに報告してください。

特に、説明に記載されているもの以外のパフォーマンス測定とユースケースについて、ご意見をお聞かせください。

参考情報