しばやん雑記

Azure とメイドさんが大好きなフリーランスのプログラマーのブログ

Azure AD B2C のサインイン用メールアドレスを変更する

Azure AD B2C を使ってローカルアカウントのサインアップを実装するのが非常に簡単なのですが、用意されているユーザーフローでは登録時に使用したメールアドレスを変更出来ないという問題が出てきます。

プロフィールの編集やパスワードのリセット機能はありますが、メールアドレスを変更することは出来ないためアプリケーション側でどのように対応するかが課題となってきます。とはいえ、完全にメールアドレスを変更できないというわけではないので、メールアドレスを変更する 2 種類の方法を紹介します。

メールアドレスの変更ポリシーを追加

1 つ目の方法は公式サンプルでも提供されているカスタムポリシーを利用した方法です。一般的なメールアドレス変更フローと同じように、まずはメールアドレスが正しいか確認コードを送信後に実際に変更を行うので入力間違いの可能性が低くなります

具体的なメールアドレスの変更フローは以下のようになります。当然ながら最初に既存のアカウントでのログインが必要になりますが、サンプルでは非常にシンプルな画面として実装されています。

  1. 既存のメールアドレスとパスワードでログイン
  2. 新しいメールアドレスを入力
  3. 確認用のコードが新しいメールアドレス宛に送信
  4. 検証が成功するとメールアドレスを書き込み

新しいメールアドレスの入力画面はよく見るシンプルなものです。これはカスタマイズ必須だと思います。

このカスタムポリシーの肝となる部分は AAD-UserWriteEmailUsingObjectId Technical Profile で、実際に新しい検証されたメールアドレスを Azure AD に対して書き込みを行っています。

        <TechnicalProfile Id="AAD-UserWriteEmailUsingObjectId">
          <Metadata>
            <Item Key="Operation">Write</Item>
            <Item Key="RaiseErrorIfClaimsPrincipalAlreadyExists">false</Item>
            <Item Key="RaiseErrorIfClaimsPrincipalDoesNotExist">true</Item>
          </Metadata>
          <IncludeInSso>false</IncludeInSso>
          <InputClaims>
            <InputClaim ClaimTypeReferenceId="objectId" Required="true" />
          </InputClaims>
          <PersistedClaims>
            <PersistedClaim ClaimTypeReferenceId="objectId" />
            <!-- Demo: Persist the email address to the user's profile-->
            <PersistedClaim ClaimTypeReferenceId="email" PartnerClaimType="signInNames.emailAddress" />
          </PersistedClaims>
          <IncludeTechnicalProfile ReferenceId="AAD-Common" />
        </TechnicalProfile>
samples/policies/change-sign-in-name/policy/TrustFrameworkExtensions_ChangeSignInName.xml at master · azure-ad-b2c/samples · GitHub

新しいメールアドレスの書き込み先は signInNames.emailAddress となっていますが、この値についての簡単な説明は以下のドキュメントにあります。他にも signInNames.userName もあるので、ユーザー名を使ったログインの場合はこちらを書き換えれば良さそうな感があります。

多少カスタマイズはしたくなりますが、メールアドレスの変更フローはこのポリシーをデプロイするだけで大体実現できました。肝となる部分を理解しておけば割と安心です。

難点は組み込みのユーザーフローを利用している場合、カスタムポリシーを使う必要があるという点です。カスタムポリシーが少しでも混ざってくると急に複雑さが増してきます。

Graph API を使って書き換え

カスタムポリシーでは Azure AD の signInNames.emailAddress を書き換えているだけなのが発覚しましたので、即ち Graph API を使っても同じことが出来るという話です。既に Graph API を利用した先人が居るので、動作するのは間違いないでしょう。

この Stack Overflow の回答では signInNames.emailAddress ではなく、Identities に対して書き換えを行っていますが、以下のドキュメントにあるように signInNames.emailAddressidentities の実体は同じものです。カスタムポリシーでも外部アカウント連携を行う際には identities が出てきます。

実際に Microsoft Graph SDK を利用して identities に保存されたメールアドレスを書き換えるサンプルは以下のようになります。Issuer の値はテナント名になるので間違えないようにしてください。正直なところ Graph SDK の知識のが重要になります。

var graphServiceClient = new GraphServiceClient(clientSecretCredential, scopes);

var updatedUser = new User
{
    Identities = new List<ObjectIdentity>
    {
        new ObjectIdentity
        {
            SignInType = "emailAddress",
            Issuer = "***.onmicrosoft.com",
            IssuerAssignedId = "<new mail address>"
        }
    }
};

await graphServiceClient.Users[<userid>].PatchAsync(updatedUser);

SignInType は色々な値が存在しているので、上手く書き換えてあげれば Facebook などのソーシャル連携もバックエンドで自動的に行うことが出来そうです。

サンプルでは認証周りのコードを丸ごと省いているので、以下のドキュメントを見て必要な認証プロバイダーを選んでください。アプリケーションに組み込む場合は Client Secret Credential か Managed Identity のどちらかになると思います。

Graph API を利用して書き換える方法では、見てわかるようにメールアドレスが正しいかどうかのチェックは出来ないため、何らかの方法で確認の後に Azure AD 側を書き換えるというステップを踏む必要があります。

パスワード変更と同様に組み込みのユーザーフローとして提供して貰いたさしかないのですが、カスタムポリシーや Graph API を利用すると変更も一応可能なので、必要に応じて組み込んでみてください。