XHR中にクロスドメインのリダイレクトが発生した時に、リダイレクト先のページに遷移できない
上記についてハマった時のメモを残す。
状況
認証サーバ(OpenID Connect 準拠)を作っている。この時、以下のユースケースで認証が成立する。
- アプリケーションから認証サーバにログイン処理が委譲される
- 認証サーバで認証がすみ、ユーザにアプリケーションに対する同意を求める
- 認証サーバはユーザ(UA)をアプリケーションのURIにリダイレクトさせる
- アプリケーションは認証サーバから取得した情報を元に、ユーザを認証済みまたは拒否する
上記の3のプロセスで、リダイレクト先のページに遷移できなかった。
XMLHttpRequest cannot load <DESTINATION_HOSTNAME>. No 'Access-Control-Allow-Origin' header is present on the requested resource. Origin <SENDER_HOSTNAME> is therefore not allowed access.
以下、問題の焦点を分割し、それぞれについての対策を記述している。
XHRでGETできない
プリフライトリクエストを受信できるようにサーバを設定
サンプルとなるアプリケーションのサーバサイドは、S3のWebホスティングを使っている。
ここでは当該バケットの、CORS設定を下記のように変更した(See here)。
<CORSConfiguration>
<CORSRule>
<AllowedOrigin>*</AllowedOrigin>
<AllowedMethod>GET</AllowedMethod>
<MaxAgeSeconds>3000</MaxAgeSeconds>
<AllowedHeader>*</AllowedHeader>
</CORSRule>
</CORSConfiguration>
プリフライトリクエストを送信できるようにリクエストヘッダを追加
リクエストヘッダに以下を追記(See here)した。
これにより、ブラウザからプリフライトリクエストが飛ぶことになる。
Requets
.post('http://localhost:4000/authorization')
.withCredentials()
.set('Content-type', 'application/x-www-form-urlencoded')
.set('Access-Control-Allow-Origin', '*')
.set('Access-Control-Allow-Methods', 'GET, PUT, POST, DELETE, OPTIONS')
. set('Access-Control-Allow-Headers', 'Content-Type, Content-Range, Content-Disposition, Content-Description')
XHRでリダイレクトできない
withCredentialsへの対応
リダイレクト先へのXHRではCookieを送信する必要はないのだが、大元のエンドポイントに対するリクエストには送信する必要があった。
このため、最終的なリダイレクト先に対してもwithCredentials = true
の状態でGETを行なっているようだ。
ゆえに、サーバのレスポンスヘッダAccess-Control-Allow-Origin
が*
だとNGになる(See here)。この場合はサーバ設定にて送信元ホストを直接指定することで回避した。
<CORSConfiguration>
<CORSRule>
<AllowedOrigin>http://localhost:4000</AllowedOrigin>
<AllowedMethod>GET</AllowedMethod>
<MaxAgeSeconds>3000</MaxAgeSeconds>
<AllowedHeader>*</AllowedHeader>
</CORSRule>
</CORSConfiguration>
終わりに
やったこと | 必要性 |
---|---|
アプリケーション側のCORS対応 | やるべき |
認証サーバ側のプリフライトリクエスト対応 | やるべき |
アプリケーション側のwithCredentials対応 | やりたくはなかったが、仕方なく |
3つめについて、リダイレクト先(アプリケーション)には単純にGETリクエスト送信したかっただけだが、withCredentials
対応をする必要があった。このためアプリケーションのサーバ設定で、Access-Control-Allow-Origin
に送信元ホストを個別に指定することになってしまった。
なお、CORS関連の予備知識としてまとめた投稿はこちらにある。