このエントリは2020/07/13現在の情報に基づいています。将来の機能追加や変更に伴い、記載内容との乖離が発生する可能性があります。
CORSとは?
CORSの定義などは種々のコンテンツに記載があるので、そちらを参照のこと。以下はその一例。
オリジン間リソース共有 (CORS) / Cross-Origin Resource Sharing (CORS)
https://developer.mozilla.org/docs/Web/HTTP/CORS
CORSの設定をしたからといって、CSRF(Cross site request forgery)攻撃に対する防御としては有効ではない点は留意しておかねばならない(ちゃんとセキュリティ設計はしましょ、ということ)。
Origin
Originは、以下を組み合わせたもの。
<protocol>://<FQDN>:<port>
同一Originとは、コンテキストパス以外が同じ、という意味。なので、プロトコルが違えば異なるOriginだし、ポート番号(例えば443と8443)が違っても異なるOrigin。
GatewayでのCORS許可の挙動
APIMのGatewayは通常、外部サービスとのアクセス、つまりドメイン外のリソースにアクセスするので、CORSが許可されていることを確認するためのPre-flightリクエストの後に実際のバックエンドサービスへのリクエストを投げている。
説明のため、以下のような構成を考える。
| testclient.azure-api.net | APIMと同一ドメインに存在するWebアプリケーション (テストクライアント) |
| apim.azure-api.net | APIMのGateway |
| backend.azure-api.net | APIMと同一ドメインに存在するバックエンドサービス |
| testclient.foo.com | APIMのドメイン外に存在するWebアプリケーション (テストクライアント) |
| api.contoso.net | APIMのドメイン外に存在するバックエンドサービス |
なお、この例ではGatewayにのみフォーカスするため、testclient.azure-api.netからの呼び出しを想定する。その後外部ドメインのクライアントからのアクセスのためのCORSポリシー設定について考える。
GatewayがApplication Request(Gatewayへのincoming request)を受け取ると、Gateway内での処理後、バックエンドサービスにリクエストを投げ込む。このとき、同一Originであれば特に気にする必要はないが、ほとんどの場合は異なるOriginであるため、Pre-flight requestをバックエンドサービスに投げ込む(厳密には、以下のドキュメントに記載の”単純リクエスト (simple request)” と呼ばれるものであればpre-flight requestを投げる必要はない)。
単純リクエスト / Simple request
https://developer.mozilla.org/en-US/docs/Web/HTTP/CORS#simple_requests

1. クライアントからGatewayに向けてリクエストを送信
ひとまず、testclient.azure-api.netからリクエストを投げる。ここは通常通り。

2-1 GatewayからバックエンドサービスへのPre-flightリクエスト
今回、api.contoso.netへリクエストを渡すので、Gatewayはapi.contoso.netへPre-flight requestを投げ込む。

2-2 Pre-flight レスポンス
api.contoso.netからのレスポンスでCORSを許可されていれば、Access-Control-*でその許可内容が渡される。以下の例で許可されたものは…。
- HTTP verbs:
POST、GET、OPTIONS - Header:
X-Custom-Header、Content-Type - 有効期間:
86400秒(24時間)

2-3 実際のバックエンドサービスへのリクエストを投げる
許可された後に、ようやくバックエンドサービスへ本来のリクエストを投げることができる。

2-4 バックエンドサービスからのレスポンスをGatewayで受け取る
api.contoso.netから返ってきたレスポンスをGatewayが受け取る。

3. レスポンスをクライアントへ返す
Gatewayはapi.contoso.netから返ってきたレスポンスを必要に応じて加工したのちに、クライアントtestclient.azure-api.netへ返す。

Azure API Management(以下、APIM)でのCORS(Cross-Origin Resource Sharing)ポリシー
GatewayでのCORSの許可を得るフローが理解できれば、このポリシーで何を設定しようとしているかが分かる(はず、知らんけど)。
このポリシーで実現しようとしているのは、APIM(ここではGateway)にアクセスしてくるクライアント(PostmanやWebアプリケーションなど)が、Gatewayと同一Originではない場合、Gatewayを呼び出す際にPre-flightリクエストを投げ込む必要がある。そのPre-flightリクエストに対するGatewayでの振る舞いを設定しよう、というもの(下図の赤矢印で届くリクエストに対する振る舞い)。それゆえ、CORSポリシーはInbound-processingのパイプライン内に配置される。

ドキュメントは以下の通り。
CORS
https://learn.microsoft.com/azure/api-management/api-management-cross-domain-policies#CORS
<allowed-origins>要素には複数のオリジンを指定できる。このあたりはAzure FunctionsのCORS設定と同じ。以下のポリシーの例では、以下の2個のオリジンからのアクセスも許可している。
<cors allow-credentials="true">
<allowed-origins>
<!-- Localhost useful for development -->
<origin>http://localhost:8080/</origin>
<origin>http://example.com/</origin>
</allowed-origins>
<allowed-methods preflight-result-max-age="300">
<method>GET</method>
<method>POST</method>
<method>PATCH</method>
<method>DELETE</method>
</allowed-methods>
<allowed-headers>
<!-- Examples below show Azure Mobile Services headers -->
<header>x-zumo-installation-id</header>
<header>x-zumo-application</header>
<header>x-zumo-version</header>
<header>x-zumo-auth</header>
<header>content-type</header>
<header>accept</header>
</allowed-headers>
<expose-headers>
<!-- Examples below show Azure Mobile Services headers -->
<header>x-zumo-installation-id</header>
<header>x-zumo-application</header>
</expose-headers>
</cors>
開発者ポータルの対話コンソールで設定したCORSポリシー
以前開発者ポータルのエントリで、対話コンソールからのAPIテスト呼び出しのためのCORSポリシー適用が可能と書いた。このCORSポリシーはグローバルレベルで設定されている。

対話コンソールからのアクセスを許可、つまり開発者ポータルからのアクセスを許可したいので、このポリシーの設定では、オリジンとしてAPIM開発者ポータルのURLが入っている。
<cors allow-credentials="true">
<allowed-origins>
<origin>https://(APIMインスタンス名).developer.azure-api.net</origin>
</allowed-origins>
<allowed-methods preflight-result-max-age="300">
<method>*</method>
</allowed-methods>
<allowed-headers>
<header>*</header>
</allowed-headers>
<expose-headers>
<header>*</header>
</expose-headers>
</cors>