BEACHSIDE BLOG

Azure とか C# 好きなエンジニアの個人メモ ( ・ㅂ・)و ̑̑

Azure AD の AppRoles ( アプリ ロール ) で認可 - 3: ASP.NET Core の実装

Azure AD で AppRoles を使って認証・認可をするのも、ようやく今回 ASP.NET Core (3.1)で Web API を作り、認証・認可を実現するターンです。 過去2つのブログでは

  • 前々回、Azure AD の設定と AppRoles を定義
  • 前回は、フロント側のサンプルコードで認証をして認証トークンを取得

をしましたねーやることは少ないのにブログで書くと長かった。

今回作る Web API は UI を持たない Web API を想定しています。UI は React とか Vue とかで作るイメージです。

ASP.NET Core の実装

プロジェクトの作成

Visual Studio 2019 にて ASP.NET Core のプロジェクトを作りましょう。GUI で作成すると途中で以下のような画面になりますね。

今回は、以下の感じで進めます。

f:id:beachside:20200602143846p:plain

認証 は 「認証なし」で進めて自分でコード書きます。今回作るやつに UI はないし Azure AD の"アプリの登録" も設定済みなので、ここで設定して余計なものが自動生成されたくないってわけです。
ちなみに前提が異なる場合は、上図の認証を選択してやるとさくっと認証ができたりします。

appsettings.json

まずは環境変数の設定からしましょうか。
appsettings.json を開いて以下のように AzureAd のオブジェクトを追加します。
よくあるサンプルだと TenantIdInstance がありますが、あれを元にAuthority を構成するだけだしそもそも Authority の値が何かを理解してないとやってられないと思ってるので、今回は実際に必要な AuthorityClientId だけの構成にしました。

{
  "AzureAd": {
    "Authority": "",
    "ClientId": ""
  },
  "Logging": {
    "LogLevel": {
      "Default": "Information",
      "Microsoft": "Warning",
      "Microsoft.Hosting.Lifetime": "Information"
    }
  },
  "AllowedHosts": "*"
}

AuthorityClientId の値を入れましょう。

  • Authority: 前回ブログでクライアント側の実装時に msalConfig に設定した Authority の後ろに /v2.0 をつけた値になります。msalConfig に設定した値が https://login.microsoftonline.com/a1234567-abcd-1234-11aa-b515ea8541d4 なら今回入力する値は、https://login.microsoftonline.com/a1234567-abcd-1234-11aa-b515ea8541d4/v2.0 になります。
  • ClientId: 前回ブログでクライアント側の実装時に msalConfig に設定した ClientId の値と一緒です。

Authority に "v2.0" と付けてますが、クライアント側は Msal.js がつけてくれてるだけで、クライアント/サーバーサイドの実際の値は同一です。

今回は雑に appsettings.json に書きましたが、場合に応じてにちゃんとやりましょう(WebApps にホストするならこんな感じとか...)。

Startup.cs

次は、Startup.cs を開いて以下のようにします。

恒例(?)のざっくりな解説いきましょう。

コードを追加したところには HACK: から始まるコメントをつけています(using のどこを追加/削除したかはめんどくて書いてません)。

いくつかピックアップすると...

  • 28行目: 今回追加する DI 絡みの内容のメソッドを呼んでます。
  • 53-76行目 今回のポイントのひとつで、認証関連の設定です。いうてもシンプルなものですね。
    • 58-59行目で appsettings に設定した値を取ってきてセットしてます。
    • 64-67行目: トークンの validation をカスタマイズできます。jwtOptions.TokenValidationParameters は初期値がインスタンス化されているので、今回のようにインスタンスを new せず jwtOptions.TokenValidationParameters のプロパティを直接操作しても問題ないです。後述の TokenValidationParameters のメモ にもうちょい役立つ情報を書いてます。
  • 72-75行目: 認証エラーに時のイベントをカスタマイズしたときにはこんな感じで。この Events のカスタマイズは、デバッグ用途でプロダクションで使うものじゃないってイメージを持ってます。なので 63 行目でDev環境の時のみ設定するようにしました。


TokenValidationParameters のメモ

トークンの Validation をカスタマイズしたければ TokenValidationParameters を設定をするって例で64-67行目で書いてみました。いたずらにトークンの有効期限チェックを無効にしてますのでうかつに真似しちゃあかんです。
トークンの Validation のデフォルトは以下の通りですので、自分で設定したプロパティがオーバーライドされます。

public TokenValidationParameters()
{
    RequireExpirationTime = true;
    RequireSignedTokens = true;
    RequireAudience = true;
    SaveSigninToken = false;
    ValidateActor = false;
    ValidateAudience = true;
    ValidateIssuer = true;
    ValidateIssuerSigningKey = false;
    ValidateLifetime = true;
    ValidateTokenReplay = false;
}

Controller で認証・認可

Controller をいじる前に、Azure AD の AppRoles で定義したやつらを定義しておきますか。前々回に設定した AppRoles の中で value の値です。コードは以下になります。ブログに書いてなかったんですが、"Admin" は認可が通らないのを確認するためのおまけです。

本題の AppRoles を使った AuthZ です。前述の Startup.cs のようにしたことで、AuthN・AuthZ ができるようになります。SampleController ってコントローラーを追加して以下のように実装しました。

コードにさらっとコメントを書いてるのでここで説明する要素があまりない...。
今回は、このコントローラー全体に認証はかけつつ、認可の設定をメソッド毎に書いてます。

今回のサンプルのような書き方のドキュメントは以下にあります。

docs.microsoft.com

今回くらいシンプルなコードならいいんですが...ロールの組み合わせに複雑さが出てくるとしんどくなるので、今回は触れませんがポリシーベースの認可を実装するのが実用的だと思っています。その辺のドキュメントはこちら。

docs.microsoft.com

ポリシーベースとか Requirement handlers とか、かなり前に下書きでブログ書きかけたけど公開せずに終わってるなぁ...😴

動作確認

動作確認は、雑にローカルデバッグだけを触れておきます。

API を投げるのに JS のサンプルコードいじるのもめんどかったので、VS Code の Rest Client で動かしてみましょう。

ID トークンを HTTP リクエストのヘッダーにセットする必要があります。ID トークンは前回のブログ でちょっと触れましたが、フロント側のサンプルコードを Chrome で実行して認証したらデベロッパーツールを起動して Application タブ > Session Storage から取得できます。あとは VS Code の Rest Client でドーン。

@id_token = IDトークンをセットする!!!

GET https://localhost:5001/sample/b
Authorization: Bearer {{id_token}}

実行すると、こんな感じで成功したり、

f:id:beachside:20200603202119p:plain

認可が通らないところは 403 が返ってきたり。

f:id:beachside:20200603202049p:plain

おわりに

ということで簡単に認証・認可ができました。

Azure AD の AppRoles を使わずに完全に独自実装で認可を実現する場合とどっちがいいかなー(ケースバイケースでしょうけど)。

自前でロールの管理する実装くらいは容易だしねー。認証はIDトークンで検証、認可は自作の Store をもとに検証って煩わしさ?はある?(今までずっとやってて困ったこともないけど)。

Azure AD の AppRoles 使えば、認証・認可をIDトークンのみで制御できるのはうれしい気がする。けど、IDトークンにロールが含まれるのでトークンをリフレッシュしないと反映されないとか、AAD の操作の実装もしないとならん(手作業で AAD の AppRoles の設定や権限付与をするとかやだー)。ということで一概に実装コストが減るわけでもなさそう。

MSAL.js でのトークンの ForceReflesh は Authorization Code grant と Implicit grant でちょっとだけコードが変わる(scope の設定)ので気づかないとハマるしね。

まーどっかでガチ運用してみて試せたらいいなと思います。

参考

Identity Platform 関連

ASP.NET Core 関連