Azure AD B2C のカスタムポリシーのフロー内で API を叩いてみた

本日の備忘録。
カスタムポリシーを本格的にいじり始めました。
今回は、カスタムポリシー内でREST APIをリクエストしてみようというものです。
おまけで、レスポンスデータからオーケストレーションステップをスキップするにはどうするのかも記述してます。

## カスタムポリシーの中身を見るうえで大事そうなキーワード

読み解くうえで大事そうなキーワードを軽く説明。

- Claim: ユーザーの情報などが保管されてる。わたしは変数みたいなものとイメージしてます。
- TechnicalProfile: テクニカルプロファイル。関数みたいなもの。入力値を受け取ってなんらかの処理をして出力する。
- UserJorney: ユーザージャーニー。フローを書いていくとこ。
- OrchestrationStep: フローの中身。フローの順番なども設定してあげる。

## 準備

- テストする API を用意する
  - 今回は post リクエストすると responseValue というプロパティに true という bool 値がセットされ👇のようなJSONが返ってくるような API をたたく想定で進めます。
  - レスポンス例👇

```json
{
  "responseValue": true
}
```

- 一からカスタムポリシーを書いてくのは大変なのでどこかからサンプルをダウンロードしておく。
  - 公式が用意してる[スターターパック](https://docs.microsoft.com/ja-jp/azure/active-directory-b2c/tutorial-create-user-flows?pivots=b2c-custom-policy#get-the-starter-pack)とかあります。
  - Local Account/TrustFrameworkExtensions.xmlに手を加えてみます。

## API をたたいて出力値を受け取る クレーム を定義する

まずは クレームを定義する。

- BuildingBlocksで検索（なければBuildingBlocksを作る。）
- ClaimsSchemaで検索（なければBuildingBlocksを作る。）
- ClaimsSchema内で👇みたいにClaimTypeを作る
- Id="extension_ResponseAPItest" と指定する

```xml:TrustFrameworkExtensions.xml
<ClaimsSchema>
  <!-- APIで返ってきた値を保管する変数(Claim)を用意 -->
  <ClaimType Id="extension_ResponseAPItest">
    <DisplayName>API Response Set Value</DisplayName>
    <DataType>boolean</DataType>
    <UserInputType>Readonly</UserInputType>
  </ClaimType>
</ClaimsSchema>
```

## テクニカルプロファイルで API を叩くように定義する

APIを叩いて、レスポンスを先程設定したextension_ResponseAPItestにセットするようなテクニカルプロファイルを定義します。

- ClaimsProvidersで検索（なければ作成）
- 👇にあるコードを追加する

```xml:TrustFrameworkExtensions.xml
<ClaimsProvider>
  <DisplayName>REST API</DisplayName>
  <TechnicalProfiles>
    <TechnicalProfile Id="AppFactor-ApiTestRequestResponseValue">
      <DisplayName>{{表示名}}</DisplayName>
      <Protocol Name="Proprietary" Handler="Web.TPEngine.Providers.RestfulProvider, Web.TPEngine, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null" />
      <Metadata>
        <Item Key="ServiceUrl">{{ServiceUrl}}</Item>
        <Item Key="AuthenticationType">None</Item>
        <Item Key="AllowInsecureAuthInProduction">false</Item>
      </Metadata>
      <OutputClaims>
        <OutputClaim ClaimTypeReferenceId="extension_ResponseAPItest" PartnerClaimType="responseValue" />
      </OutputClaims>
    </TechnicalProfile>
  </TechnicalProfiles>
</ClaimsProvider>
```

- TechnicalProfile Id: テクニカルプロファイルのId。ここで指定したIdを用いてオーケストレーションステップで呼び出す。
  - DisplayName: 表示名を入力する（任意）
  - ServiceUrl: ここにAPIのエンドポイントをセットする
  - OutputClaims
    - ここに出力を記述する。
      - ClaimTypeReferenceId: セット先を指定する。先程定義したextension_ResponseAPItestを指定する
      - PartnerClaimType: どの値をセットするか。responseValueを指定してあげます

## オーケストレーションステップで定義したAPIを実行するようにセットする

- サンプルの中身をOrchestrationStepで検索すると多くヒットすると思われる。
  - ない場合はUserJourneyの中で
- APIを呼び出したい箇所を選び、👇のような感じでコードを挿入する

```xml
<OrchestrationStep Order="4" Type="ClaimsExchange">
  <Preconditions>
    <Precondition Type="ClaimsExist" ExecuteActionsIf="true">
      <Value>extension_StrongAuthenticationAppSecretKey</Value>
      <Action>SkipThisOrchestrationStep</Action>
    </Precondition>
  </Preconditions>
  <ClaimsExchanges>
    <ClaimsExchange Id="AppFactorApiTestRequestResponseValue" TechnicalProfileReferenceId="AppFactor-ApiTestRequestResponseValue" />
  </ClaimsExchanges>
</OrchestrationStep>
```

- Order: 実行される順番の制御をしてる。
  - 重複した値はダメ。飛び番もダメだったはずなので注意
- Preconditions: 前条件。条件に一致した場合はこのオーケストレーションステップをスキップするなどができる。
- ClaimsExchange:
  - TechnicalProfileReferenceId: こちらにどのテクニカルプロファイルを行うかを指定する
    - 今回は先程定義したAppFactor-ApiTestRequestResponseValueをセットする

あとはアップロードして試してみてください。

## まとめ

以上の内容をAzure AD B2Cにてアップロードすることでカスタムポリシー内でAPIを叩くことができます。　　
今回は使いませんでしたが、ClaimをAPIのBodyにセット(Input)してリクエストすることもできます。  
Claimの値をみてオーケストレーションをスキップするなどができますのでより柔軟なフローをカスタマイズできます。

## おまけ - オーケストレーションステップをスキップするには？

今回、APIからレスポンスを受け取ってextension_ResponseAPItestにはtrueが返ってくるようなフローを書いたと思います。  
extension_ResponseAPItestの値を使ってみたいと思います。  
extension_ResponseAPItestがtrueのときはオーケストレーションステップをスキップする処理は👇みたいな感じになります。

```xml
<Preconditions>
  <Precondition Type="ClaimEquals" ExecuteActionsIf="true">
    <Value>extension_ResponseAPItest</Value>
    <Value>true</Value>
    <Action>SkipThisOrchestrationStep</Action>
  </Precondition>
</Preconditions>
```

これをスキップさせたいオーケストレーションステップで記述すればOKです。
ここで注意したいのがPreconditionのTypeはClaimEqualsになってるところです。  

- Value
  - 1つ目は評価するClaim
  - 2つ目にどのような値とEqualであるか  
- Action
  - Preconditionの条件を満たしている場合、どのようなActionを行うかを記述します。