しばやん雑記

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

Azure AD B2C のユーザーを Graph API を使って管理する

Azure AD B2C は後ろが Azure AD なのでユーザー情報は Graph API を使って色々弄れます。

Microsoft Graph を使ってユーザー情報を取得したりできますが、B2C に必要なプロパティが Microsoft Graph だとまだサポートされていないので、Azure AD Graph を使う必要があります。

調べた感じでは Azure AD Graph のクライアントはかなり古くて、今の時代に .NET Standard へ対応してない系なので使わない方が良さそうでした。

公式ドキュメントは自前で Graph API を叩くものばかりなので、それに従うことにします。そして Service Principal を作る部分からわかりにくかったので、メモがてら手順を残します。

Service Principal を作成する

以下の 2 つのドキュメントに Azure AD Graph API を使って Azure AD B2C に新規ユーザーの作成や削除する方法が載っています。サンプルコードのリンクもありますが、それは後回しにします。

必要な Service Principal の作り方に書いてあるように、パスワード変更や削除は別の権限が必要です。

Application Permissions は Read and write directory data にチェックを入れて保存します。ディレクトリに対する読み書きなので結構強い権限です。

f:id:shiba-yan:20191012190204p:plain

この権限ではユーザーの作成は出来ますが、パスワードの変更や削除は行えません。権限としてはいけそうな気がしますが、追加で User administrator のロールを Service Principal に割り当てる必要があります。

ディレクトリへのアクセス権限は Azure AD テナント側で追加しますが、ロールは Azure AD B2C 側で割り当てないといけないので結構わかりにくいです。こっちは RBAC です。

f:id:shiba-yan:20191012190219p:plain

Service Principal に権限を与え終えたら Client Secret を作成しますが、Azure Portal 上は Key とか Password と呼ばれているので割と混乱します。

更に UI がわかりにくいですが Description と Expires を設定して保存すれば生成されます。

f:id:shiba-yan:20191012190212p:plain

あとは Application Id (ClientId) と Azure AD の TenantId (ObjectId) をコピーしておけば完了です。権限設定がいろいろ散らばっているのは正直扱いにくいと感じます。多分 Azure AD が全て悪い。

C# アプリケーションから利用する

先ほどの公式ドキュメントでサンプルコードが紹介されていますが、例によって実装がとっ散らかっていてリポジトリもバラバラというあまり好ましくない状態でした。

Microsoft Graph で扱えるようになればシンプルになりますが、今のところはどうしようもないのでサンプルコードにあったクライアントを修正して使うことにしました。

実装の肝としては Microsoft.IdentityModel.Clients.ActiveDirectory を使って Access Token を取る部分と、Graph API が要求するモデルクラスを用意する部分ぐらいでした。

折角なので修正したクライアントは以下のリポジトリで公開しています。修正とは言っていますが、クラス名以外はほぼ変更している感じがあります。

簡単な確認用のサンプルコードで Azure AD B2C にユーザーを作成と削除を行ってみます。今回必要だった API しか実装していないですが、Graph API はモデルさえあれば扱いは簡単なので問題ないと思います。

ClientId や Client Secret は Service Principal の作成時にコピーしておいたものを指定します。

class Program
{
    static async Task Main(string[] args)
    {
        var clientId = "<client_id>";
        var clientSecret = "<client_secret>";
        var tenantId = "<tenant_id>";

        var graphClient = new B2CGraphClient(tenantId, clientId, clientSecret);

        var user = User.Create("demo@example.com", "P@ssw0rd!", "demo user");

        var createdUser = await graphClient.CreateUserAsync(user);

        Console.WriteLine("Created");
        Console.WriteLine($"ObjectId: {createdUser.ObjectId}, DisplayName: {createdUser.DisplayName}");

        var result = await graphClient.FindByNameAsync("demo@example.com");

        Console.WriteLine("Found");
        Console.WriteLine($"ObjectId: {result[0].ObjectId}, SignInName: {result[0].SignInNames[0].Value}");

        await graphClient.DeleteUserAsync(createdUser.ObjectId);

        Console.WriteLine("Deleted");
        Console.WriteLine($"ObjectId: {createdUser.ObjectId}");
    }
}

コードを見てわかるように、メールアドレスを使ったログインのみに対応しています。

本来なら Social Login にも対応していますが、コードを分かりやすくするために省きました。Provider の種類と ID を追加するぐらいなので特に難しいことはないですが、まずはちゃんと動かしておきたかったので。

Service Principal の設定を追加して実行すると、Azure AD B2C のユーザーの作成と削除が行われます。

f:id:shiba-yan:20191013013149p:plain

ちゃんと ObjectId が発行されているので、ユーザーの作成が行われていることがわかります。

もちろん Azure Portal からも確認できますが、反映されるのに少し時間がかかるケースがあるようなので注意です。API レベルでは遅延はないので安心してください。