Azure ADB2Cのカスタムポリシーでパスワードリセットフローを実装してみた


これは公式ドキュメント通りにやって実装したときのメモみたいなものになります。

- [公式Docお手本](https://docs.microsoft.com/ja-jp/azure/active-directory-b2c/add-password-reset-policy?pivots=b2c-custom-policy)

## やること

### Claimの定義

```xml
<ClaimType Id="isForgotPassword">
  <DisplayName>isForgotPassword</DisplayName>
  <DataType>boolean</DataType>
  <AdminHelpText>Whether the user has selected Forgot your Password</AdminHelpText>
</ClaimType>
```

- このClaimをみてパスワードの変更を行うかどうかのステップを判断します。

### ContentDefinitionの定義

```xml
<!-- パスワードリセットフローを定義する -->
<ContentDefinition Id="api.signuporsignin">
  <DataUri>urn:com:microsoft:aad:b2c:elements:contract:unifiedssp:2.1.2</DataUri>
</ContentDefinition>
```

### TechnicalProfileの定義

```xml
        <!-- パスワードリセットのテクニカルプロファイルを定義 -->
        <!-- 呼び出されるとisForgotPasswordがtrueに設定されるテクニカルプロファイルを定義 -->
        <TechnicalProfile Id="ForgotPassword">
          <DisplayName>Forgot your password?</DisplayName>
          <Protocol Name="Proprietary" Handler="Web.TPEngine.Providers.ClaimsTransformationProtocolProvider, Web.TPEngine, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null"/>
          <OutputClaims>
            <OutputClaim ClaimTypeReferenceId="isForgotPassword" DefaultValue="true" AlwaysUseDefaultValue="true"/>
          </OutputClaims>
        </TechnicalProfile>

        <!-- ユーザージャーニーで実行されるパスワードリセットリクエストの変換を定義 -->
        <TechnicalProfile Id="SelfAsserted-LocalAccountSignin-Email">
          <Metadata>
            <Item Key="setting.forgotPasswordLinkOverride">ForgotPasswordExchange</Item>
          </Metadata>
        </TechnicalProfile>
```

- ForgotPassword
  - このテクニカルプロファイルで先程定義したClaimをtrueに変換する
- SelfAsserted-LocalAccountSignin-Email
  - パスワードリセットを行う

### OrchestrationStepの修正

- CombinedSignInAndSignUpで検索して（だいたいサインインユーザージャーニーの1個目のステップだと思われる)👇を追加する。

```xml
<ClaimsProviderSelection TargetClaimsExchangeId="ForgotPasswordExchange" />
```

- 次のStepのClaimExchangesで👇を記述する

```xml
<ClaimsExchange Id="ForgotPasswordExchange" TechnicalProfileReferenceId="ForgotPassword" />
```

- その次のStepには👇のオーケストレーションステップを挿入する

```xml
        <!-- isForgotPassword が有効かどうか -->
        <OrchestrationStep Order="3" Type="InvokeSubJourney">
          <Preconditions>
            <Precondition Type="ClaimsExist" ExecuteActionsIf="false">
              <Value>isForgotPassword</Value>
              <Action>SkipThisOrchestrationStep</Action>
            </Precondition>
          </Preconditions>
          <JourneyList>
            <Candidate SubJourneyReferenceId="PasswordResetSubJourney" />
          </JourneyList>
        </OrchestrationStep>
```

- isForgotPasswordがtrueだったらSubJourneyの内容を行う
- あとはオーケストレーションステップを１つ追加した影響でOrderに被りが生じているので修正する。

### SubJourneyの定義

- 先程、PasswordResetSubJourneyというSubJourneyを実行するようなオーケストレーションステップを追加したと思います。
  - これからそれを定義します
  - 定義する場所は、UserJourneysの末尾に追加します

```xml
<!-- パスワードリセットサブジャーニーを定義する -->
  <SubJourneys>
    <SubJourney Id="PasswordReset1" Type="Call">
      <OrchestrationSteps>
        <!-- Validate user's email address. -->
        <OrchestrationStep Order="1" Type="ClaimsExchange">
          <ClaimsExchanges>
            <ClaimsExchange Id="PasswordResetUsingEmailAddressExchange" TechnicalProfileReferenceId="LocalAccountDiscoveryUsingEmailAddress" />
          </ClaimsExchanges>
        </OrchestrationStep>
  
        <!-- Collect and persist a new password. -->
        <OrchestrationStep Order="2" Type="ClaimsExchange">
          <ClaimsExchanges>
            <ClaimsExchange Id="NewCredentials" TechnicalProfileReferenceId="LocalAccountWritePasswordUsingObjectId" />
          </ClaimsExchanges>
        </OrchestrationStep>
      </OrchestrationSteps>
    </SubJourney>
  </SubJourneys>
```

- Order=1
  - emailのvalidateを行う
- Order=2
  - 新しいパスワードに変換する

### アップロードする

全て記述したらアップロードして試してみましょう。
アップロード方法とテストは👇公式Docを参考に
https://docs.microsoft.com/ja-jp/azure/active-directory-b2c/tutorial-create-user-flows?pivots=b2c-custom-policy#upload-the-policies