OAuth認証アプリのCloudFront設定でハマった話

# はじめに
独自ドメインの小さなWebアプリを、AWSで以下のお安い構成でセットアップしてみました。

- Route53 (独自ドメインを管理)
- AWS Certificate Manager (ACM。SSL証明書を管理)
- CloudFront
- EC2

CloudFrontはキャッシュ用のエッジサーバとしてではなく、ACMと紐付けてSSLを利用したいため配置しています。
しかし、この構成にしたところアプリに実装していたOAuth認証が急に動かなくなりました。。。

最終的にCloudFrontの設定を見直して解決できたので、忘れないようにメモとして残しておきます。
同じように悩んだ人の参考になれば幸いです。

最終的な設定だけ見たい方は、[まとめをどうぞ](#まとめ)。

# GoogleのOAuth認証が通らなくなった
もともと、静的なWebコンテンツを `CloudFront+S3` の構成でSSLでホストしていたため、それに倣って` CloudFront+EC2` もセットアップしてみました。
**CloudFrontのキャッシュ設定はほぼデフォルトのまま**です。

いざアプリケーションでGoogleのOAuth認証してみると・・・
![スクリーンショット 2020-08-01 18.08.34.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/430093/28dfbdab-b594-89fc-b31a-aec2ea2181d6.png)

> エラー 400: redirect_uri_mismatch
> The redirect URI in the request, http://***.ap-northeast-1.compute.amazonaws.com/login/oauth2/code/google, does not match the ones authorized for the OAuth client.

**GoogleのOAuthで設定したリダイレクトURIにマッチしないエラーとなりましたorz**

上記のエラーメッセージを見ると、認証後のリダイレクト先がEC2のパブリックDNS

`http://*****.ap-northeast-1.compute.amazonaws.com/...`
 
になってしまっているようです。

確かにGoogleのOAuth設定で、リダイレクト先に独自ドメインのみを許可しているので、 **それ以外のURIにリダイレクトできないのは正しい制御ではある**のですが・・・


## なぜリダイレクト先がEC2のパブリックDNSになってしまう？

リダイレクト用のURIはアプリ側でホスト名を設定して生成しています。
つまり、 **EC2に独自ドメインのホスト名が渡ってきていないことが問題**のようです。

ホスト名はリクエストヘッダで指定するので、改めてCloudFrontの設定をみてみると・・・

<img width="701" alt="スクリーンショット 2020-08-01 22.04.01.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/430093/35a42cf8-3d2b-04cb-184b-b622c84dcb5d.png">

**怪しい設定がありました。**

## Cache Based on Selected Request Headers とは

この項目を調べるにあたり、以下の記事がとても参考になりました。
https://michimani.net/post/aws-about-cache-spec-of-cloudfront/#cache-based-on-selected-request-headers-1

どうやらCloudFrontはエッジサーバとしてコンテンツをキャッシュするために、デフォルトではリクエストヘッダの値を考慮しないだけでなく、 **オリジンにリクエストヘッダを転送しない**ようです。

[公式の説明文](https://docs.aws.amazon.com/ja_jp/AmazonCloudFront/latest/DeveloperGuide/distribution-web-values-specify.html#DownloadDistValuesForwardHeaders) を読むと、`[None(Improves Caching)]` の説明には特に記載がないですが、 `[All(すべて)]` には 

> 代わりに、CloudFront はすべてのリクエストをオリジンに送信します。

とありました。

なるほど、**~~こいつが元凶~~ この設定を見直す必要があったようです。**

## オリジンにリクエストヘッダを転送する設定

今回、オリジンはEC2のためキャッシュは不要、かつリクエストヘッダも転送して欲しいため **`[All]`** に変更しました。
<img width="691" alt="スクリーンショット 2020-08-01 22.34.30.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/430093/90b8331e-0e2c-d0c1-305e-8454bf14ca2a.png">
これで無事にEC2にホスト名を含むリクエストヘッダが転送されるはずです。

設定変更後にアプリのログインボタンを押下すると・・・
![スクリーンショット 2020-08-01 22.59.16.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/430093/fff2444e-7965-2d8a-8bd7-691b982817d7.png)

無事にアカウント選択画面まで進みました！

アカウントを選択してみると、、、

**まさかのエラーorz**
![スクリーンショット 2020-08-01 18.40.07.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/430093/610ccde0-3bce-3f25-b52b-12705a4e00f6.png)

これはアプリ側で用意しているエラーレスポンスですが、つまり
> Status Code: 401 Unauthorized

のようです。
GoogleのOAuthからアプリへのリダイレクトは成功していますが、**アプリ側の処理で問題がある**ようです。

## Forward Cookies と Query String Forwarding and Caching
意気消沈しながらも、改めてCloudFrontの設定をみてみると・・・
<img width="689" alt="スクリーンショット 2020-08-01 23.36.21.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/430093/f72a2508-385b-c69e-ff3c-9b51f6d15c85.png">

**CookieやURIのクエリパラメータに関する設定がありました。**
この項目も以下の記事に詳しく説明されていました。

- [Forward Cookies](https://michimani.net/post/aws-about-cache-spec-of-cloudfront/#forward-cookies-1)
- [Query String Forwarding and Caching](https://michimani.net/post/aws-about-cache-spec-of-cloudfront/#query-string-forwarding-and-caching-1)

これらの設定も、デフォルトではコンテンツをキャッシュするために値の考慮をしないだけでなく、
**Cookieやクエリパラメータがオリジンに転送されない**ようです。

つまりこの設定も`None (Improves Caching)`だとオリジンに転送しないんですね。

## オリジンにCookieとクエリパラメータを転送する設定
すべてのCookie、クエリパラメータをオリジンに転送して欲しいため**`[All]`**に変更しました。
<img width="684" alt="スクリーンショット 2020-08-02 0.11.54.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/430093/795753d0-4034-9c95-4a67-de04b91ce928.png">

以上の設定で、無事に`Cookie`およびURIの`クエリパラメータ`がオリジンに転送されるようになりました。

画面ショットは省きますが、これで`CloudFront+EC2`の構成で無事にOAuth認証でログインが行えるようになりました:clap_tone1:

# まとめ
CloudFrontをEC2などAppサーバの手前に配置する場合、**以下の項目でオリジンへの転送の設定を行う必要があります。**

- **Cache Based on Selected Request Headers**
リクエストヘッダをオリジンに転送するため`[All]`を指定

- **Forward Cookies**
Cookieをオリジンに転送するため`[All]`を指定

- **Query String Forwarding and Caching**
クエリパラメータをオリジンに転送するため`[Forward all, cache based on all]`を指定

# 参考
https://michimani.net/post/aws-about-cache-spec-of-cloudfront/