というアプローチを紹介してる記事があって、なるほど?と思ったのでまとめてみる。
元記事はこちら。
Leveraging Web Workers to Safely Store Access Tokens – The New Stack
毎度のことながら、今にはじまったことではない。
元記事いわく
WebWorkerであれば、メインスレッドで実行されるであろうXSSや3rdのコードから触れないので安全!
設計としては、
- メイン: まず`Worker`をロード
- メイン: 初期化のメッセージを`postMessage()`
- クレデンシャルがあるならそれを渡す
- ワーカー: アクセストークンの準備
- 受け取ったやつ or そこで`fetch()`して、オンメモリに保存
- (これで準備OK)
- メイン: APIにリクエストしてほしいと`postMessage()`
- ワーカー: APIに向けてアクセストークンつけて`fetch()`
- ワーカー: レスポンスをメインスレッドに`postMessage()`
- メイン: それを受け取って使う!
大事なのは、アクセスできるAPIのリストを、ワーカー内にホワイトリストとして定義しておくこと。
そうすることで、メインスレッドから悪意のあるコードによって、意図しないドメインに送信されることがない。
ただしWebWorker自体のライフサイクルはそのセッションと同じで、永続化ができるわけではない。
とはいえ、最近のAuthベンダーであれば、セキュアなセッションCookieを使ってユーザー入力なくアクセストークンを更新できるはずなので、UXに影響はないであろう。
とのこと。
まとめ
この記事を読み始めたときは、「LocalStorageを使わなくても、WebWorkerならセキュアにトークンを永続化できる・・?」って思ってたけど、まったく関係のない話だった。言われてみれば当然なんやけど、なぜか永続化の話だと思いこんでしまってた。
「大事なトークンを一瞬たりともメインスレッドに置きたくない!」というのを実現する方法としては、実装コスパとしては妥当なのかな〜と思った。(ワーカー内にスコープが狭まっただけではあるけど)
あとはトークンそれ自体のIOだけを隠蔽するのでは不十分で、結局それがリクエストに載っていくならば、`fetch()`なんかもXSSの対象になることを忘れるなということかね。
Auth0のSPA用のSDKでも、オンメモリのStorage + FetcherとしてWebWorkerを使うようになってた。
あとWebWorker使えっていう話は、Auth0のドキュメントにも書かれてた。
ちなみに先行実装とかないかなーと思って探したけど、見つけられたのはこれだけだった。
まあやるからには自作するか・・。
永続化は結局どうする
元記事は大丈夫って言ってるけど、実際には3rdのCookieに対する風向きも微妙な昨今では、Cookieに頼る方法はぜんぜん大丈夫じゃないと思う。
となるとやはりリフレッシュトークン + ローテーションを必須にしつつ、LocalStorageに入れるしかないんやろうけど、なんかもっと画期的なソリューションは出てこないもんかね〜。
const auth0 = await createAuth0Client({ domain: '<your Auth0 domain>', client_id: '<your Auth0 client ID>', cacheLocation: 'localstorage', useRefreshTokens: true, }); // Logging-in will automatically request the offline_access scope // and store the resulting refresh token auth0.loginWithRedirect(); // Silently refreshing the access token will use the /token endpoint // with ‘refresh_token’ grant and the refresh token from the cache await auth0.getTokenSilently();
`auth0-spa.js`だとこんな感じのコードになるらしい。
こっちの記事もブラウザのストレージまわりについてよくまとまってたのでおすすめ。