前回は AAD B2C の設定をするだけで力尽きました。今回は Console App を使って、前回設定した Azure AD B2C のユーザーの情報やカスタム属性を取得・変更してみましょう。
カスタム属性は、前回 Tenant という名前の属性を追加したってことを前提に、Tenant の値を取得したり編集してみます。
コンソールアプリのプロジェクト作成
VS 2019 で .NET Core のコンソールアプリのプロジェクトを作ります。B2C を嗜むひとなら細かい手順は不要ですね。
NuGet のインストール
プロジェクトを作成したら NuGet パッケージをインストールしますかね。
必要なのは以下3つですが、Microsoft.Graph.Auth
をインストールすれば Microsoft.Identity.Client
も入ってきますけど、重要なので書いておきます。
- Microsoft.Graph: v3.2.0
- Microsoft.Graph.Auth: 1.0.0-preview.4
- Microsoft.Identity.Client: 4.11.0
(ブログを書いた時点で最新のバージョンです。投稿日と異なって3月ころ書いたんですよ...下書きは1月くらいに書いて放置してた...)
余談ですがこの Microsoft.Identity.Client
が通称 MSAL (えむさる、Microsoft Authentication Library)と呼ばれるライブラリで、Azure AD や Azure AD B2C への認証トークンを取得したり、AAD のユーザーの情報の取得、つまり Graph API へのアクセスして情報を取得するをするためのライブラリです。C# の他にも JS や Java とか他の言語用のライブラリもあります。
あと、本題からちょっとずれますが Graph API で取得した情報を Console で Json フォーマットで表示するのに Json.NET ではなくこっちを入れておきましょう。
- System.Text.Json : v5.0.0-preview.2.20160.6
個人的には System.Text.Json は v5 系 (プレビュー版) を使っています。v4.7.1とかだと この問題 が若干うざいのでって理由です。Json.NET ではもちろん対応してるんですけどね...今後を見据えて System.Text.Json を使っていきます。
Azure AD B2C の情報をセットする
前提: クライアント・クレデンシャルズ・フローを使う
Azure AD B2C には Graph API でデータを取得します。その際、どの認証フローを使うかは、ユーザーのインタラクションなしに使う場合は意識しておく必要があるでしょう。ドキュメントでまとまってるのでリンクを貼っておきます。
今回は Client Credentials フロー(クライアント・クレデンシャルズ・フロー、MS のドキュメントだと翻訳で ”クライアント資格情報" フローって書いてる...なんか違和感)でやります。 ユーザーを管理する Web API で Graph API を使うようなイメージです。
Azure AD B2C の情報4つをセット
今回使う Azure AD B2C の情報を定義しておきましょう。作成したコンソールアプリで Program.cs
を開いて以下のようにしちゃいましょう。
- 8-11 行目: 今回必要な Azure AD B2C の情報を入れておく変数を用意しました。
- 13 行目: 非同期メソッド使うから最初からシグネチャを変えておきます。C#7.1 の機能なので古いバージョンだと使えません...ってかもう7.1より古いの使ってる人おらんか。。。
8-11行目にセットする情報を Azure Portal から情報を取ってきましょう。
TenantId と AppId を取得
前回のブログ で作成した Azure AD B2C を Azure Portal から開き、アプリの登録(プレビュー) > 前回作成したアプリケーション(私の場合、ConsoleDemo というのを作りました)を開きます。
概要 が開かれます。
- アプリケーション ID を上記コードの9行目
AppId
に入れましょう。 - テナント ID は、上記コードの 8行目
TenantId
に入れます。余談ですがこの TenantId には B2C のドメイン名を入れても、後述で使うメソッドが対応してるのでOKですが、名前と別のものいれるとか煩わしいので素直にテナント ID を入れます。
ClientSecret を取得
証明書とシークレット を開き、新しいクライアントシークレット をクリックしてシークレットを生成します。生成されたシークレットを上記コードの 10行目 ClientSecret
に入れます。
B2CExtensionAppClientId を取得
画面上部の Azure AD B2C | アプリの登録 (プレビュー) をクリック > すべてのアプリケーション をクリックします。そうすると、b2c-extension-app.... という名称のものが(悪いことをしない限り)必ずありますのでクリックしてます。で、その中の アプリケーション(クライアント)ID の値を、上記コードの 11 行目 B2CExtensionAppClientId
にいれます。
これは、Graph API に REST で直接叩くこともできます。なるほどねー♪です。
twitter.com/applications?api-version=1.6&$filter=startswith(displayName,'b2c-extensions-app') のvalueのappIdがextensionのうしろにつくやつです(ハイフンは除く
— こすもす.えび (@kosmosebi) 2020年4月3日
これで必要な情報は取得できました。あとは SDK を使ってアクセスするだけの簡単なお話です。
Graph API を使ってAzure AD B2C のユーザー情報を操作
このブログを書き始めのころ、それぞれ細かくコードを書いてたんですがなんかわかりにくくなったので下の方に完成版のコードをはりました。ここからは下にあるコードの解説をしていきます。
変数のセット
- 16-19行目の Azure AD B2C の情報は既にはいってるはずです。
- 21行目は、カスタム属性のキーを入力します。前回のブログで、
Tenant
と入れましたので、ここではTenant
と入れてます。 - 32 行目: Azure AD B2C で登録されているユーザーのオブジェクト ID を取得しておきましょう。Azure Portal で Azure AD B2C を開く > ユーザー を開けばオブジェクト ID を確認することができます。
- 33行目: これから更新する Tenant の値を適当に入れましょう。
GraphServiceClientの作成
SDK を使って Graph API をアクセスする場合は、主に GraphServiceClient
class を使ってアクセスします。このインスタンス生成のメソッドを 53-64行目 に書きました。
クライアントクレデンシャルズのフロー使う際は、SDK の仕様でこんな感じに書くんやって話です。それを Main
メソッドの 37 行目で呼び出しています。
ObjectId をキーにしてユーザーの情報を取得
Main
メソッドの 40 行目で呼び出してる ShowUserAsync
メソッドの定義は、67-75行目です。
71行目 の Select
句がなければ全属性を取得します。全部が必要なんてことが無ければ、Select
句で最小限のデータだけ取得しましょう。
余談: Json へシリアライズ
74 行目で、取得した情報を Json フォーマットにシリアライズしてコンソールに表示しています。インデントをつけたり Null の値を無視したりしてるのは、23-28行目 の JsonSerializeOptions
の設定によるものです。
ユーザーのカスタム属性の情報も取得する
カスタム属性を取得するには、Select 句でカスタム属性の属性名を指定すればよいのですが、それがちょっとわかりにくい。属性名の Full Name は、121-126行目 のメソッドにあるようにして取得します(コードを呼んだ方が説明するよりわかりがいい...ですよね)。
情報を取得しているのは、**77-88行目です。Select 句では、Lambda expression ではなく 83行目のように文字列でべたべた書きます。これでカスタム属性の値が取得できます。
うまく取得できないときは、カスタム属性の属性名が間違ってる可能性があるので、Select 句を消してユーザーの情報を呼び出してみましょう。すべての属性データが取得できるので、それで属性名を確認できます。
カスタム属性を編集する
カスタム属性のインスタンスは、94-97行目 のように Dictionary<string, object>
型で作ります。
99-103行目の更新したい情報だけを User
型(Microsoft.Graph
namespace) でインスタンス化して値をセットします。
今回は、デフォルトで用意されている属性の例として OfficeLocation
をセットしました(101行目)。
また、カスタム属性は、Dictionary<string, object>
型 のインスタンスを AdditionalData
プロパティにセットします(102行目)。
これで 105-107行目でやっているように objectId を指定して更新すれば、設定した値だけが更新されます。REST API でいうところの PUT ですね。
コード全体
終わりに
SDK を使って更新るのは簡単です。
この他にもユーザーを削除したり、バルクでユーザーを登録したりも簡単にできるので個人的にやりたいことは網羅できそう(複雑なことをやる予定はないのでw)。
次のステップとして、もっと一連のフローをいい感じに構成したいときは カスタムポリシーを使うと思いますが、それは頼れる猛者たちにお任せですかね。私はここまでで十分かなと思っています。