Cloudflare Worker で Google IAP の jwt ペイロードを検証する
GCP 側で設定して Cloudflare Worker にリクエストを流すようにすると request の headers に
x-goog-iap-jwt-assertion
として jwt のペイロードが含まって送られてくる
Node.js のコードでは
google-auth-library
に実際の検証コードがあるので node コンパチ環境ではこれを使うでいけるのだが、Worker には当然
WebCrypto 相当の API しか生えてないし、fs とかもないのでそのまま使うことはできない
仕方ないので google-auth-library
を読み下してそれっぽいコードを実装した
JWT はヘッダとペイロードとシグネチャの3セグあるかをバリデートする
ヘッダとペイロードは JSON.parse(atob(seg[0]))
みたいな感じでデコードする
ヘッダに kid
が入ってるので、jwk の keys
から該当する cert を探してくる
無知なのでアレだけど、importKey
は { name: 'ECDSA', namedCurve: 'P-256' }
、verify
は { name: 'ECDSA', hash: { name: 'SHA-256' } }
をそれぞれ指定する
importKey
は何もしなくても jwk で取得できるので問題ないが、
verify
の方は引数が
ArrayBuffer なので base64エンコード の文字列から起こすのが地味に面倒くさい
とりあえずこれで検証自体はできるので、あとは iat と exp と iss と aud をそれぞれ検証すればよい
worker => worker は service bindings で呼び出すにして、
workers.dev を塞いでやれば IAP を通したリクエストだけが子 worker にやってくる状態がつくれる
worker => pages は service bindigs ではつながらないので、pages.dev を fetch でつないでやるなどしないといけない
ここのリクエストに事前共有の値をヘッダにくっつけてリクエストを送って、リクエストを受け取る pages function でリクエストのヘッダを検証して、ヘッダがついていれば200を返して、なければ401とすることで擬似的な制限が可能になる
ただ、pages function を通らないアセット類に関してはガバガバなのでフルオープンになっちゃう
Access だとアセット類もちゃんと塞いでくれるので偉い
とはいえ現代のツールチェインを使ってるとだいたいアセットにはダイジェストがくっついてるので、攻撃したい人がドンズバで URL 当てないと引っ張ってこれないのでまあ妥協ラインとしてはそんなもんかなあと
あと worker => pages でつなぐ時は
POST
の body が
ReadableStream として入ってきてはいるけど、pages の fetch の body としては使えないってことになってるっぽくて、一旦 body を worker の中で json なり formData なり確定させないといけない
この時、formData だと headers の Content-Type に boundary が入ってて、headers をそのままパスしてると pages 側で formData として受け取れないので取り除いてやらないといけなかった