しばやん雑記

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

Microsoft Entra ID の B2B コラボレーションの招待を Microsoft Graph SDK から行う

Microsoft Entra ID には External ID として別テナントのユーザーを招待する B2B コラボレーションという機能が用意されています。最近は External ID の扱いが良くわからなくなっていますが、今は B2B コラボレーションは External ID 扱いらしいです。

Azure Portal でいうところの、以下の Invite External User を選択するとメールアドレスで招待が出来ますが、その相手が Entra ID の別テナントの場合は B2B コラボレーションになります。

B2B コラボレーションは今回本筋ではないため詳細については詳しく説明しませんので、以下のドキュメントやサポートチームのブログを見ていただければ理解できるはずです。

Entra ID の B2B コラボレーションではメールアドレスだけ登録して招待すれば、自動的に相手のテナントの情報を使ってユーザーが登録されるので非常にお手軽です。

便利な機能ですが、毎回 Azure Portal から作業するのは面倒なので、B2B コラボレーションの招待をアプリケーションから行いたいシナリオ向けの話です。Entra ID の Graph API には招待リソースが用意されているので、これを使うことでアプリケーションから招待することが出来ます。

各言語向けの Graph SDK にも招待リソースが用意されているので、アプリケーションに B2B コラボレーションの招待を組み込みやすくなっています。例によって Graph API を叩くためには Service Principal などが必要になるので、その準備が一番面倒くさい説はあります。

Entra ID に Service Principal を作成する

Graph API を使うためには Service Principal などの認証情報が必要になるので、今回は手っ取り早く Service Principal を作成して叩けるようにします。Managed Identity を使うことも出来ますが、権限の割り当てが面倒なのでここでは紹介しません。

招待を行うために必要な最小権限は User.Invite.All となるため、最低でもこの権限を作成した Service Principal に割り当てる必要があります。

今回はアプリケーションから直接利用するので Application Permissions を選択して追加します。

権限を追加すると管理者の承認が必要になっているので、「Grant admin consent for ***」ボタンを押して承認しておきます。承認を行っておかないと実行時にエラーとなるので注意が必要です。

これで必要な権限の追加は完了したので、後はクライアントシークレットを作成して値をコピーしておき、実際に Graph SDK を使うコードを書き始めます。

Graph API を使って招待を作成する

Graph API で招待を作成する方法は公式ドキュメントにまとまっているので、実際のところ難しくはありません。Graph SDK を使った場合でも基本的には Invitation エンティティを作成して、メソッドを 1 つ呼び出すだけにマッピングされています。

地味にはまったのが invitedUserEmailAddress に指定可能なメールアドレスは制約があり、良く Gmail などで使われる + を含んだアドレスは指定できないことです。API の応答としては若干分かりにくいメッセージが返ってくるので、動作確認の際には注意が必要です。*1

実際に Graph SDK を使って招待を作成するサンプルコードが以下になります。認証プロバイダーの部分については説明しませんが、Service Principal を使うようにセットアップしています。

using Azure.Identity;

using Microsoft.Graph;
using Microsoft.Graph.Models;

var scopes = new[] { "https://graph.microsoft.com/.default" };

var clientId = "<client id>";
var clientSecret = "<client secret>";
var tenantId = "<tenant id>";

var options = new ClientSecretCredentialOptions
{
    AuthorityHost = AzureAuthorityHosts.AzurePublicCloud,
};

var clientSecretCredential = new ClientSecretCredential(tenantId, clientId, clientSecret, options);

var graphClient = new GraphServiceClient(clientSecretCredential, scopes);

var invitation = new Invitation
{
    InvitedUserEmailAddress = "user1@example.com",
    InviteRedirectUrl = $"https://myapplications.microsoft.com/?tenantid={tenantId}",
    SendInvitationMessage = true
};

await graphClient.Invitations.PostAsync(invitation);

招待なので invitedUserEmailAddress が必須なのはわかりますが、実際には InviteRedirectUrl の指定も必須なので、Azure Portal のデフォルト値を参考に My Applications に飛ばす、あるいは実際のアプリケーションの URL を指定するなど工夫が必要です。

見たことある人が多いはずの Entra ID からの招待メールを送信する場合には SendInvitationMessagetrue に設定する必要があります。このコードを実行すると、以下のようなメールが届くはずです。

デフォルトの招待メールには必要最低限の情報の未記載されていますが、追加で InvitedUserMessageInfo を設定するとカスタマイズされたメッセージを追加出来るようになっています。

この時 CustomizedMessageBody は当然ながらロケールは考慮しないため、送信先の言語設定に合わせたカスタマイズが必要になる可能性があります。

var invitation = new Invitation
{
    InvitedUserEmailAddress = "user1@example.com",
    InviteRedirectUrl = $"https://myapplications.microsoft.com/?tenantid={tenantId}",
    SendInvitationMessage = true,
    InvitedUserMessageInfo = new InvitedUserMessageInfo
    {
        CustomizedMessageBody = "Graph API から招待メールを送信するテストです"
    }
};

これを使って追加の情報をメールに付与して送信すると、以下のように本文に表示されるので招待が届いた理由を把握しやすいので、ある程度はカスタマイズしておくのは良さそうです。

招待メールの要素については以下のドキュメントで公開されているので、どの部分がカスタマイズ可能なのかを把握することが出来ます。本来なら招待を行ったユーザー情報を表示させたかったのですが、Graph API では難しそうな予感でした。

これ以上の招待メールのカスタマイズを行いたい場合には SendInvitationMessagefalse のままにして、アプリケーションが独自でメールを送信する必要が出てきます。招待メールに含まれているリンクは inviteRedeemUrl で取得出来るため、カスタマイズされたメールを送信するのは簡単です。

どのレベルまでカスタマイズするかはアプリケーションの要件次第ですが、標準の招待メールがあまりにも寂しいのでもう少しリッチにするのは全然ありだと思います。

*1:B2B コラボレーションの招待と言いつつ MSA や GitHub なども使えるのでこの制約は分かりにくい