Open2

nest: service workerを使用したトークンの保護

okuokuokuoku

nestはGitHubのような外部サービスから実際のアプリケーションコードを読み込むことになるので、外部サービスのトークンをJavaScript的に処理する必要がある。で、nestならではの特殊な事情として、WebRTCを張りっぱなしにする必要から、 認証のためにアプリの画面遷移を行うことはできない という制約がある。

そこで、

  • アプリのメイン画面
  • 認証フローの画面

のタブを分け、それを Service workerを間に挟んで通信させることにした。認証フローが完了したらService workerにメッセージをpostしてトークンを渡す。

更に、この構成にすることでトークンの実値をメイン画面に渡さないデザインを実現できる。Service workerは全ての外部アクセスを処理できるので、トークンの付与もそこで行えば良い。ただし、Amazon S3のようにリクエスト自体の署名が必要なケースではService worker側もそのアルゴリズムを知っている必要がある。

okuokuokuoku

ChatGPTに書かせたコード

正しそう。誤送信防止のためのURLフィルタリングが追加で必要などアドバイスもかなり適切だった。

コードはIndexedDBからtokenを取る仕様になっているが、これは当然on memoryでよい。

// sw.js
self.addEventListener('fetch', (event) => {
  const req = event.request;

  // 例: ページが付けた独自ヘッダが "Apply-Bearer-Token: true" のときだけ適用
  const wantsBearer = req.headers.get('Apply-Bearer-Token') === 'true';

  // no-cors はブラウザ仕様で「非シンプル」ヘッダを追加できないので除外
  const canModifyHeaders = req.mode !== 'no-cors';

  // 例: 同一オリジンの API にだけ付ける(漏えい防止)
  const sameOrigin = new URL(req.url).origin === self.location.origin;

  if (wantsBearer && canModifyHeaders && sameOrigin) {
    event.respondWith(addAuthAndForward(req));
  }
});

async function addAuthAndForward(originalRequest) {
  // ここでトークンを取得(IndexedDB、Memory、postMessage で受け取る等)
  const token = await getTokenFromIndexedDB(); // 実装は適宜

  // Request のヘッダはイミュータブルなので一度コピーしてから編集
  const headers = new Headers(originalRequest.headers);
  headers.delete('Apply-Bearer-Token');
  headers.set('Authorization', `Bearer ${token}`);

  // 既存の Request を入力にして新しい Request を作ると、
  // method/body/各種フラグが引き継がれる
  const newRequest = new Request(originalRequest, { headers });

  // そのままネットワークへ
  return fetch(newRequest);
}