CORSについてざっくり理解する

# **はじめに**
- API開発をしていると以下のようなエラーに遭遇することがあります

```
Cross-Origin Request Blocked: The Same Origin Policy disallows
reading the remote resource at https://some-url-here. (Reason:
additional information here).
```
- なにが原因でこのエラーが起こり、どう解消したらいいのかをざっくりまとめてみる
    - 結論を言ってしまうと、同一オリジンポリシーに引っかかっているのでCORSの設定をする必要があります

# **CORSとは**
- `Cross-Origin Resource Sharing`の略で、ざっくり言うと違うオリジンからのアクセスを許可する仕組みのこと
    - 同一オリジンポリシー（後述）にという仕組みによって、異なるオリジンのリソースへのアクセスは制限されている
- オリジンとはURL中のスキーム、ホスト、ポートの組み合わせのこと
    - `http://example.com`だと
        - スキーム：`http`
        - ホスト：`example.com`
        - ポート: `80`（省略可能なので明示されているケースは少ない）
- CORSやオリジンについて詳しくは以下のサイトを参照してください
    - [オリジン間リソース共有 (CORS)](https://developer.mozilla.org/ja/docs/Web/HTTP/CORS)
    - [Origin (オリジン)](https://developer.mozilla.org/ja/docs/Glossary/Origin)


# **どのような場面でCORSの設定が必要**か
- 現在のブラウザは[同一オリジンポリシー](https://developer.mozilla.org/ja/docs/Web/Security/Same-origin_policy)が導入されているため、デフォルトでは違うオリジンからのアクセスは遮断されてしまう。

- WebクライアントとAPIを分ける場合などで、異なるオリジン間の通信を行うためにはCORSの設定をする必要がある。

# **具体的な設定方法**

## (i) 単純リクエストの場合
- 単純リクエストとは、以下のすべての条件を満たすもの
（[オリジン間リソース共有 (CORS)](https://developer.mozilla.org/ja/docs/Web/HTTP/CORS)から引用）

```
- 許可されているのは以下のメソッドのみです。
  - GET
  - HEAD
  - POST
 - ユーザーエージェントによって自動的に設定されたヘッダー (たとえば  `Connection`、 `User-Agent`、 または Fetch 仕様書で "forbidden header name" として定義されている名前のヘッダー)を除いて、手動で設定できるヘッダーは、 Fetch 仕様書で "CORS-safelisted request-header" として定義されている以下のヘッダーだけです。
   - Accept
   - Accept-Language
   - Content-Language
   - Content-Type (但し、下記の要件を満たすもの)
   - DPR
   - Downlink
   - Save-Data
   - Viewport-Width
   - Width
 - Content-Type ヘッダーで許可されているのは以下の値のみ
   - application/x-www-form-urlencoded
   - multipart/form-data
   - text/plain
- リクエストに使用されるどの XMLHttpRequestUpload にもイベントリスナーが登録されていないこと。これらは正しく XMLHttpRequest.upload を使用してアクセスされます。
- リクエストに ReadableStream オブジェクトが使用されていないこと。
```

### 設定すべきこと

- サーバー側のResponse Headerに以下を追加する。

```
Access-Control-Allow-Origin: <オリジン名>
```
- Access-Control-Allow-Originの値でアクセスを許可するオリジンを指定する。
値に`*`を指定するとすべてのアクセスを許可できる。

***

## (ii) 単純リクエストではない場合
- 実際のリクエストを送る前に安全かを確かめるためにプリフライトリクエストが送られる。

- プリフライトリクエストは以下の特徴を持っている
    - OPTIONSメソッドを使用
    - 以下の情報がリクエストヘッダに含まれる
      - 実際のリクエストのHTTPメソッド情報
        - `Access-Control-Request-Method: <実際のリクエストのHTTPメソッド>`
      - 実際のリクエストのヘッダー情報（単純リクエストのヘッダの条件から外れる場合）
        - `Access-Control-Request-Headers: <実際のリクエストのカスタムヘッダ>`

### 設定すべきこと
- サーバー側でOPTIONSメソッドを受け取れるようにする
- OPTIONSメソッドを受けとったら、以下のヘッダ・値をレスポンスヘッダに追加して返す
  - `Access-Control-Allow-Origin: <許可するオリジン名>`
  - `Access-Control-Allow-Methods: <許可するHTTPメソッド名>`
  - `Access-Control-Request-Headers: <許可するカスタムヘッダ名>`
- プリフライトリクエストのレスポンスで許可されれば実際のリクエストが送信される。実際のリクエストに対する設定は単純リクエストの場合と同じ。


- プリフライトリクエスト
![スクリーンショット 2019-03-22 16.58.03.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/288334/02fa51f0-8381-9fbe-819b-9c3235e2842e.png)


- 実際のリクエスト
![スクリーンショット 2019-03-22 16.58.14.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/288334/26d49ce9-47df-f5f8-cd64-ab9a68d087c8.png)




***

## ※  ユーザー認証情報（CookieやBASIC認証）を送信する場合
### 設定すべきこと
- 上記に加えて以下のことを設定する必要がある
- サーバー側
  - `Access-Control-Allow-Origin`の値を`*`以外にする
  - `Access-Control-Allow-Credentials: true`を指定する
- クライアント側
  - クレデンシャルを扱うことを明示する

- 詳しくは[CORSリクエストでクレデンシャル(≒クッキー)を必要とする場合の注意点](https://qiita.com/kawaz/items/1e51c374b7a13c21b7e2)を参照してください

# **補足**
- [rack-cors](https://github.com/cyu/rack-cors)や[cors](https://www.npmjs.com/package/cors)のようにCORSの設定をサポートするライブラリを利用するのが楽そう

# **参考URL**
- https://developer.mozilla.org/ja/
- [CORS関連、これだけ知っとけばまぁ大丈夫](https://qiita.com/rooooomania/items/4d0f6275372f413765de#cors%E3%81%A8%E3%83%A6%E3%83%BC%E3%82%B6%E8%AA%8D%E8%A8%BC%E6%83%85%E5%A0%B1)
- [CORSリクエストでクレデンシャル(≒クッキー)を必要とする場合の注意点](https://qiita.com/kawaz/items/1e51c374b7a13c21b7e2)
- http://takapi86.hatenablog.com/entry/2017/09/18/172846