Azure AD B2C のススメ(2)
前回 Azure B2C のディレクトリを作成し、今回は認証を利用するアプリを実装していきます。
とりあえず
- Webブラウザからのサインイン
- 認証付きWebAPIの呼び出し
あたりを試していきたいです。
公式ドキュメントの作業開始 に色々な使用シーンに応じたサンプルが公開されているので使えそうなものを拾ってみます
ブラウザからのサインイン
WebブラウザからのサインインはWebApps - JavaScript のサンプルが簡単です。
index.html だけで完結しています。
とりあえず index.html の
var applicationConfig = {
clientID: 'e760cab2-b9a1-4c0d-86fb-ff7084abd902',
authority: "https://login.microsoftonline.com/tfp/fabrikamb2c.onmicrosoft.com/b2c_1_susi",
b2cScopes: ["https://fabrikamb2c.onmicrosoft.com/demoapi/demo.read"],
webApi: 'https://fabrikamb2chello.azurewebsites.net/hello',
};
clientID をアプリケーション作成時にメモっておいたアプリケーションID、authority のテナントとポリシー部分を自分のテナントのドメイン名・使用ポリシー名に置き換えます。
b2cScopes はスコープを利用する時は必要ですが、そうでない時は clientID と同じ値を入れておけばOKです。
webApi はサインイン時に使用するものではなく、サインイン後に表示されるボタンを押した時に叩かれる認証付きWebAPIのURLです。とりあえず放っておいて、node でサーバを立ち上げましょう。
Webアプリの作成
次はWebアプリ側の作成です。とりあえず .NET Core を使った WebAPI プロジェクトで、
パス | 機能 |
---|---|
/index.html | テストアプリページ |
/api/Debug/me | 認証付きWebAPI |
こんな感じにして index.html でサインイン後、認証付きWebAPIを叩く形にしたいです。
というわけで、Visual Studio を起動し ASP.NET Core Web アプリケーション → API のテンプレートを作っていきます、が。
いつの間にやら"認証"のオプションから必要情報を入力すると自動でやってくれるようになっていたんですね orz。
どういう手順が必要だったか思い出しながら色々書こうと思っていましたが、その必要はとっくに無くなっていたと。さすが Microsoft。
というわけで何もなかったかのようにプロジェクトを作成してソースを眺めます。
認証のセットアップに関係するのは
public void ConfigureServices(IServiceCollection services)
{
services.AddAuthentication(sharedOptions =>
{
sharedOptions.DefaultScheme = JwtBearerDefaults.AuthenticationScheme;
})
.AddAzureAdB2CBearer(options => Configuration.Bind("AzureAdB2C", options));
と
{
"AzureAdB2C": {
"Instance": "https://login.microsoftonline.com/tfp/",
"ClientId": "101abe36-adaf-4b57-9ca0-38502187511c",
"Domain": "yossyad.onmicrosoft.com",
"SignUpSignInPolicyId": "B2C_1_sign_in_up"
},
}
ですね。
AddAzureAdB2CBearer() なんて名前からして一発で Azure AD B2C の設定ができそうなメソッドまで追加されていてほんとこの記事意味あるんかって感じですね。
愚痴はおいといて認証が必要なAPIを実装していきます。
public class DebugController : Controller {
// GET api/Debug/me
[Authorize]
[HttpGet("me")]
public Dictionary<string, string> Me() {
return (User.Identity as ClaimsIdentity).Claims
.ToDictionary(c => c.Type, c => c.Value);
}
}
/api/Debug/me を追加。クレーム情報は ControllerBase.User から辿って引けるので、そのまま Dictionary にして返しています。
public void Configure(IApplicationBuilder app, IHostingEnvironment env)
{
...
app.UseDefaultFiles();
app.UseStaticFiles();
...
}
そして先程の index.html を /api/Debug/me にアクセスするように改造し、プロジェクトの wwwroot に配置。
UseDefaultFiles, UseStaticFiles を使用して wwwroot の静的htmlをホストするようにします。
最後に応答URLのポート番号を合わせ、起動してみます。
サインインし、認証つきWebAPIを叩くとこんなふうにクレーム情報が返ってきます。
ちなみにGoogleアカウントでのサインインなので identityprovider には "google.com" と入っています。ここは使用IDプロバイダーにより変わったり、ローカルアカウントであった場合は項目が無かったりします。
B2Cディレクトリ内でユーザ一意のIDを示すものは nameidentifier の GUID です。システムでユーザ個別の情報を保管する必要がある時は nameidentifier を主キーとするテーブルか何かを作ればよいでしょう。
簡単なサンプルですがソースはこちらに置いておきます。
まとめ
個人的な意見ですが今からユーザ管理を必要とするシステムを作るなら可能な限りクラウド認証基盤を利用できる形にして自前での認証は行うべきではないと考えています。
.NET Identity や devise のように簡単にユーザ管理・認証の機能を追加できるパッケージは多くありますが、やはり「認証に関するデータ」は極力自分のDBには持ちたくないというのが本音です。
ですが外部認証オンリーだとだいたい提案時に問題になります。なぜなら外部のアカウントを持たないユーザはサービスを使えないじゃないか! というのが争点になるからですね。
そして結局ローカルアカウント+外部認証の形に落ち着くというのがいつものパターンですが、ローカルアカウントの管理を含めて外部に委託できる Azure AD B2C や Amazon Cognito のような仕組みはセキュリティ的にも手間的にも導入を検討する価値があると思います。