Azure AD B2Cカスタムポリシーを利用し、認証フローの中で独自のREST APIを呼び出す

[Azure AD B2C](https://docs.microsoft.com/ja-jp/azure/active-directory-b2c/overview)では、認証フローの一連の流れの中で独自のREST APIを呼び出すことができ、独自のビジネスロジックを認証フローの中に組み込むことができます。

また、Azure AD B2Cで認証フローを定義する方法は「[ユーザーフロー](https://docs.microsoft.com/ja-jp/azure/active-directory-b2c/user-flow-overview)」と「[カスタムポリシー](https://docs.microsoft.com/ja-jp/azure/active-directory-b2c/custom-policy-overview)」の2通りがあります。
Azure AD B2Cのドキュメントにも記載されている通り、ユーザーフローを使った認証フロー作成が推奨されていますが、REST API呼び出しに関してはカスタムポリシーでのみサポートされています。

そこでカスタムポリシーでREST APIを呼び出すにあたり、何点かハマった箇所があったのでポイントをまとめました。

### カスタムポリシーの記述方法
カスタムポリシーでのREST API呼び出しについてはこちらのドキュメントをご参照ください。
https://docs.microsoft.com/ja-jp/azure/active-directory-b2c/custom-policy-rest-api-intro

また、Azure公式で公開されているサンプルコードはこちらです。
https://github.com/azure-ad-b2c/rest-api/

### REST API呼び出しのポイント

#### URLはServiceUrlに記述する
* APIのURLは`TechnicalProfile`の`ServiceUrl`部分に記述します。
    * 呼び出すAPIは、インターネットゾーン経由でアクセスできる必要があります。
    * 動作確認は以下で良いのですが、セキュリティリスクを鑑みて「[お使いの RESTful サービスを保護する](https://docs.microsoft.com/ja-jp/azure/active-directory-b2c/secure-rest-api)」を適用することを推奨します。

```xml
<TechnicalProfile Id="TestApi">
  ...
  <Metadata>
    <Item Key="ServiceUrl">https://xxx.example.com/api/auth/user</Item>
    <Item Key="AuthenticationType">None</Item>
    <Item Key="SendClaimsIn">Body</Item>
    <Item Key="AllowInsecureAuthInProduction">true</Item>
  </Metadata>
</TechnicalProfile>
```

#### タイムアウトは30秒、リトライは2回
* REST API呼び出しのタイムアウトとリトライ回数は下記に設定されており、現状カスタムができません。
    * タイムアウト：30秒
    * リトライ：2回まで

#### HTTPメソッドは全てPOST
* Azure AD B2Cが呼び出すAPIは、HTTPメソッドがすべてPOSTとなります。

#### リクエストはInputClaimsに記述する
* リクエストの内容は`InputClaims`部分に記載します。
   * `ClaimTypeReferenceId`に記述する部分は`ClaimsSchema`に定義されている必要があります。

```xml
<TechnicalProfile Id="TestApi">
  ...
  <InputClaims>
    <InputClaim ClaimTypeReferenceId="objectId" />
    <InputClaim ClaimTypeReferenceId="email" />
  </InputClaims>
  ...
</TechnicalProfile>
```

#### レスポンスはOutputClaimsに記述する
* レスポンスの内容は`OutputClaims`部分に記載します。
   * リクエスト同様、`ClaimTypeReferenceId`に記述する部分は`ClaimsSchema`に定義されている必要があります。

```xml
<TechnicalProfile Id="TestApi">
  ...
  <OutputClaims>
    <OutputClaim ClaimTypeReferenceId="employeeId" />
  </OutputClaims>
  ...
</TechnicalProfile>
```

#### カスタムポリシーのXMLは規定の順番通りに記述する
* B2Cのカスタムポリシーは決められた順番通りにXMLを記述する必要があります。
* 例えば`TechnicalProfile`内部の順番は下記の通り記述します。
    1. DisplayName
    1. Protocol
    1. Metadata
    1. InputClaims
    1. OutputClaims

#### REST APIでリクエストを受け取る方法
* Azure AD B2Cで呼び出すREST APIは`Content-Type`の指定が無いため、HTTP Requestを直接参照する必要があります。
* 上述で紹介したサンプルコードに.NETやJava等の様々な環境でのREST APIの例が記載されているため、そちらも合わせてご参照ください。
* .NET Coreでの記述例は下記の通りです。

```csharp
var input = "";

// HTTP Requestを読み取る
using (StreamReader reader = new StreamReader(Request.Body, Encoding.UTF8))
{
    input = await reader.ReadToEndAsync();
}

// JSON形式にデシリアライズ
return JsonConvert.DeserializeObject<T>(input);
```

#### エラー時のレスポンス形式は規定の形式にする
* エラー時のステータスコードやレスポンスの形式は、規定の形式に合わせる必要があります。
    * 詳細は[こちら](https://docs.microsoft.com/ja-jp/azure/active-directory-b2c/restful-technical-profile#returning-validation-error-message)をご覧ください。

#### UserJourneysでのREST APIを呼び出す
* `UserJourneys`セクションにAPI呼び出しをしている`TechnicalProfile`を定義します。
* その際、`TechnicalProfileReferenceId`の部分は`TechnicalProfile`の`Id`と合わせる必要があります。

```xml
<UserJourneys>
  <UserJourney Id="SignUpOrSignIn">
    <OrchestrationSteps>
     ...
      <OrchestrationStep Order="7" Type="ClaimsExchange">
        <ClaimsExchanges>
          <ClaimsExchange Id="TestApi" TechnicalProfileReferenceId="TestApi" />
        </ClaimsExchanges>
      </OrchestrationStep>
       ...
    </OrchestrationSteps>
     ...
  </UserJourney>
<UserJourneys>
```

## 関連記事
* [AD B2C記事まとめ](https://qiita.com/AsukaSeki/items/46afb6846cb4057b8050)