Last active
June 17, 2025 00:57
-
-
Save fsubal/8280365f54f5b262aef9d4bedf3ed775 to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| /// <reference lib="webworker" /> | |
| /** | |
| * Rails の CSRF トークンを取得し、それを使ってリクエストを送信する | |
| * | |
| * ワーカースクリプトで動作する | |
| * | |
| * ワーカースクリプトの場合、メインスクリプトからメッセージを送信して CSRF トークンを更新する | |
| * メインスクリプトからメッセージを送信して CSRF トークンを更新する | |
| * メインスクリプトからメッセージを送信して CSRF トークンを更新する | |
| */ | |
| class RailsCsrf { | |
| #csrfToken?: string | |
| #tokenSourceUrl: URL | |
| constructor(tokenSourceUrl: URL) { | |
| this.#tokenSourceUrl = tokenSourceUrl; | |
| } | |
| async refresh() { | |
| const response = await fetch(this.#tokenSourceUrl, { | |
| credentials: 'include', | |
| }); | |
| const token = response.headers.get('X-CSRF-TOKEN'); | |
| if (token) { | |
| this.#csrfToken = token; | |
| } | |
| } | |
| async onFetch(request: Request) { | |
| const response = await this.#proxyFetch(request); | |
| if (response.status === 403) { | |
| return this.refresh().then(() => this.#proxyFetch(request.clone())); | |
| } | |
| return response; | |
| } | |
| async #proxyFetch(request: Request) { | |
| if (this.#csrfToken == null) { | |
| throw new TypeError('CSRF token not loaded'); | |
| } | |
| request.headers.set('X-CSRF-Token', this.#csrfToken); | |
| return fetch(request); | |
| } | |
| } | |
| const { origin, searchParams } = self.location; | |
| const tokenSourceUrl = new URL(searchParams.get('token_source_url') ?? '', origin); | |
| const railsCsrf = new RailsCsrf(tokenSourceUrl); | |
| // 初回トークン取得 | |
| self.addEventListener('activate', (event: ExtendableEvent) => { | |
| event.waitUntil(railsCsrf.refresh()); | |
| }); | |
| self.addEventListener('message', (event: MessageEvent) => { | |
| if (event.data === 'refresh-csrf-token') { | |
| railsCsrf.refresh(); | |
| } | |
| }); | |
| self.addEventListener('fetch', (event: FetchEvent) => { | |
| const { request } = event; | |
| const { method } = request; | |
| if (method === 'GET' || method === 'HEAD') { | |
| return; | |
| } | |
| event.respondWith(railsCsrf.onFetch(request)); | |
| }); |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment