BEACHSIDE BLOG

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

Azure AD の AppRoles ( アプリ ロール ) で認可 - 2: SPAで認証トークンの取得

前回 AzureAD で AppRoles で権限を作って、ユーザーに割り当てました。

blog.beachside.dev

続編の今回は、フロントエンドのサンプルアプリで認証するメモです。

Azure AD を設定する上で一番重要と思っている Azure AD の "サポートされているアカウントの種類" とプログラム内の "Authority" の関係 についてもふれています。

サンプルコードのダウンロード

フロントエンド側の作成は本質じゃないので公式のサンプルコードを使います。以下 GitHub の Repository からコードをクローンします。

github.com

このサンプルを選んだのは、まず認証フローが Authorizaition code grant with PKCE だからです。Implicit flow は今からやるのに選択肢としてないかと(がっつり個人的な見解です)。

余談ですが、Authorizaition code grant flow はマイクロソフトのドキュメントだと「承認コードフロー」とか「認証コードフロー」とか翻訳されてますが... OAuth 的には 認可コードフローだよね感があります。日本語の揺れがめんどいので、ここでは Authorization Code grant flow って言っておきます(with PKCE もつくのが当然になってるので省略...)。

Azure AD にて "アプリの登録" のセットアップ

今回大事なことなので何度も書きますが、Authorization Code grant でやります。

まずは、Azure Portal で Azure AD を開き、アプリの登録 > 対象のアプリ(前回作って AppRoles を設定したやつ)をクリックします。

f:id:beachside:20200529020828p:plain:w400

開いた画面で 認証 > プラットフォームの追加シングルページアプリケーション を選択します。
(この選択を間違えると、フロントエンドから認証した際に CORS のエラーが出ます。)

f:id:beachside:20200529021229p:plain

今回のサンプルコードでデバッグした時に http://localhost:3000 を listen しますので、リダイレクト URI には http://localhost:3000 を入力します。そして画面上部の 保存 をクリックします。

f:id:beachside:20200526015838p:plain:w400

保存後、サポートされているアカウントの種類 で「この組織ディレクトリに含まれるアカウント...」を選択して保存します。

この選択と、後述するプログラム側で設定する Authority の組み合わせ方が正しくないとトークンが取得できなかったり別のトークンが取れたりしますので注意です。

f:id:beachside:20200529185802p:plain

サンプルプログラムを編集

まず、入力する値は Azure AD の "アプリ登録" で作成したアプリの画面の 概要 の中にありますので開いておきましょう。

f:id:beachside:20200529202614p:plain

サンプルプログラムのルートディレクトリを VS Code で開き、authConfig.js を開きます。

f:id:beachside:20200529224351p:plain

msalConfig を定義しているのが3つ(6-8行目)を、以下を参考に入力します。

  • clientId: アプリケーション (クライアント) ID
  • authority: https://login.microsoftonline.com/ + ディレクトリ (テナント) ID の値。例としてテナントIDが a1234567-abcd-1234-11aa-b515ea8541d4 なら入力する値は https://login.microsoftonline.com/a1234567-abcd-1234-11aa-b515ea8541d4
  • redirectUri: 前述で設定したリダイレクト URI と同じ値: http://localhost:3000

サンプルコードの実行

VS Code のターミナルで npm install して npm start をしましょう。で私は...エラー?port の 3000 を使ってるだと?となりました。

f:id:beachside:20200529022648p:plain:w480

curl localhost:3000 叩いたら...なんかいるやん...

f:id:beachside:20200529023423p:plain

日頃の行いが悪いようです💀... ぶっころす。

f:id:beachside:20200529022602p:plain

気を取り直して npm start ...👍。

f:id:beachside:20200529022826p:plain

Chrome で http://localhost:3000 を開きます。とりあえず F12 キーを押してデベロッパーツールを開いておきましょう。
で、Sign In ボタンをクリックしてサインインしましょう。


💉 エラーが起きてしまったらデベロッパーツールの Console タブ見てエラーを確認しましょう。

  • CORS のエラーなら、前述の プラットフォームの追加で シングルページアプリケーション以外を選んで死んでます、たぶん。
  • その他のエラーは、後述の Azure AD の "サポートされているアカウントの種類" とプログラム内の "Authority" の関係 を見ると問題解決をブーストできるかもしれません。


設定に問題なければこんな感じで名前が表示されます。

f:id:beachside:20200529192631p:plain

デベロッパーツールを見てみましょう。トークンが保存されているのは、Application タブ > Session Storage の http://localhost:300 の中にある {authority.... で始まる行です。クリックすると、各種トークンが見えます。

f:id:beachside:20200529192908p:plain

idToken の値をコピペして jwt.io のサイトでデコードしてみましょう。設定したロールが含まれています👌。

f:id:beachside:20200529193434p:plain

このトークンを使って ASP.NET Core の Web API で良い感じに認可の仕組みを構築できます。

その話に入る前に、Azure AD を設定してうまく認証できない要因と感じているポイントについて プチ Deep Dive ? メモです。

Azure AD の "サポートされているアカウントの種類" とプログラム内の "Authority" の関係

この2つを理解しておかないと、場合によってはエラーの闇に飲み込まれて心がダークサイドに落ちます(意味不明)。

以下の構成になっていることを前提に話を進めましょう。

  • 今回 AppRoles とか今回のシリーズで設定している Azure AD は Beachside-Sandbox-AAD です。Other-AAD で AppRoles の設定はしてません。
  • フロントエンドのアプリは Beachside-Sandbox-AAD の情報を authConfig に書いてます(前述で設定した内容のことです)。
  • Other-AAD のユーザー: user-A を Beachside-Sandbox-AAD にゲストユーザーとして追加しています。
  • Other-AAD のユーザー: user-B は Beachside-Sandbox-AAD に招待していません。

f:id:beachside:20200529220609p:plain

サポートされているアカウントの種類

"サポートされているアカウントの種類" の設定は、Azure ポータルで Azure AD を開く > アプリの登録 で今回のアプリをクリック > 認証 をクリックして設定できます。

f:id:beachside:20200529203837p:plain

設定できるのは上図の通り2択です。シングルテナント(上の方)かマルチテナント(下の方)。

  • シングルテナントを選ぶと、Beachside-Sandbox-AAD に存在するユーザーしか認証できません(そりゃそうだ)。user-A はログインできる権利を得ます。use-B はログインできません。
  • マルチテナントを選ぶと、user-A だけでなく Beachside-Sandbox-AAD に存在しない user-B もログインできる権利があります(え?マジ?ほんとです)。

ここで "ログインできる権利" と書きました。最終的にログインできるかどうかは、シングルテナントマルチテナントのどちらを設定したかと Authority の設定次第になります。

Authority の設定

Authority は、今回のサンプルアプリだと authConfig.js の中でmsalConfig オブジェクトの中で設定しています。

Authority の値は https://login.microsoftonline.com/??? です。??? に何を入れるとどうなるかを説明していきましょう。

シングルテナントの場合

  • ??? に Beachside-Sandbox-AAD のテナントID を設定:
    今回設定したのがこれです。トークンは、Beachside-Sandbox-AAD の情報です。Beachside-Sandbox-AAD で AppRoles を定義してるので、user-A に割り当てた AppRoles の情報が出力されます。トークンをデコードした時に確認できる oid (オブジェクト ID) や name は、Beachside-Sandbox-AAD で付与されたオブジェクト ID や name になります。

  • ??? に "organizations" を設定:
    ユーザーが所属する大元の AAD の情報がトークンに入ります。つまり Other-AAD の情報です。user-A でログインしてトークンをデコードして確認すると、oid や name が Other-AAD のユーザーの情報であることが確認できます。この設定だと Beachside-Sandbox-AAD で AppRoles をどんだけ設定しても、トークンの情報は Other-AAD の内容なので、AppRoles は常に空っぽです。

シングルテナントの設定をしていると、Authority でどんな設定をしても Beachside-Sandbox-AAD に存在しない user-B はログインできません。

マルチテナントの場合

マルチテナントを設定をすると user-A はもちろんログインできます。そして Beachside-Sandbox-AAD に存在しないユーザー、例えば Other-AAD の user-B もログインする権利を得ます。

先ほど同様に https://login.microsoftonline.com/??? の "???" に何を入れるかについては、

  • ??? に Beachside-Sandbox-AAD のテナントID を設定:
    トークンには Beachside-Sandbox-AAD の情報が入ります。Beachside-Sandbox-AAD に存在する user-A は先ほど同様にログインで、AppRoles の情報もあります。user-B は Beachside-Sandbox-AAD に存在しないためトークンを取得することができません。ログインする権利はあるけどこの設定ではログインできないのです。

  • ??? に "organizations" を設定:
    user-A でログインすると、シングルテナントの時同様でトークンは Other-AAD の情報になります。つまり AppRoles の情報はありません。そして Other-AAD に存在する user-B でのログインが可能になります。この設定では user-A でも user-B でも Beachside-Sandbox-AAD で設定した AppRoles は使えません。



設定したのに認証できなかったり期待するトークンが取れない場合、ここを理解しておけば OK でしょうかね。

さっきも触れましたが、エラーが起きたときは Chrome のデベロッパーツールにエラーが表示されます。Authority でなんかおかしいって表示がでた場合は、そのテナントIDがどの Azure AD をさしてるかを確認して(ゲストで招待した AAD か大元の AAD かでしょう)、設定を確認しましょう。


Authority の設定では、他にcommonconsumers も設定できますが似たような話なのでここでは触れません。ここを理解すれば、後述の参考ドキュメントをみるとすぐわかります。

おわりに

AppRoles が含まれたトークンを取得できることがやりたいことなので、サンプルコードのチュートリアルにある graph API へのアクセスとかはここでは触れません。

今回は、Authorization Code grant flow の話ですが、Implicit grant flow だと若干設定が異なるので Implicit flow のドキュメントをチェックしましょう。

次回は ASP.NET Core の Web API を作成して認証・認可をします。

blog.beachside.dev

参考ドキュメント

docs.microsoft.com