Azure AD B2C でSPA (3) サンプル実行マラソン

複数のコードサンプルを、複数のパターンで試す

プロダクトを理解するには、READMEおよびコードリーディングが早いです。

しかし、手を動かして初めてわかる知見もありますね。というわけで、いくつかのサンプルを試してみました。

結果をまとめると、Azure AD B2CのAzure Portalでの設定項目とMSAL.jsのインスタンスを作成する際のmsalConfigの関係、動作フローなどの理解が進みました。

 adal.jsのときのような実行した段階での不安定さなどもなく、多少の調整をすれば全般に簡単に動きます。安心できる品質になっていると思います。

 

以後のメモになりますが、

  • Azure Portalで設定を完了したらユーザーフローをAzure Portalから実行してAzure AD B2C側の問題をすべて解決しておく
  • SPAの場合、Azure PortalにアプリケーションのURLをSPAのURLとして追加する。
  • Microsoftアカウント、GoogleアカウントなどのIdpを追加する場合には、WebのリダイレクトURLとして追加する。
  • 今回のサンプルではオープンなCORSが設定してあるが、自分の環境では適切に準備する。

その他にも、注意すべき点などはあると思われます。たとえば、サンプルではルートページにSPAが展開され、Azure AD B2Cなどすべてのredirect_urlは同一でした。

しかし、実際のUXでは、ユーザーはあるページのSPA下から同一ドメインの他のページでもサインイン状態を継続したいというのが普通かと思います。また、別のタブを開いた時などにもその状態の維持を望むでしょう。cacheLocationをlocalStorageにすれば解決する問題も、今度は逆にセキュリティ上の問題を生じてしまうリスクを考慮しなければならないでしょう。

以前のadal.jsでは一部のedgeやI.E.で無限ループが生じるという問題が発生することがありました。それも「常に」「すべてのブラウザーで」というわけではないため問題の特定が難しい状況にありました。こういった問題はプロダクトを選択するにあたっては大きな地雷になりうるのでプロダクト選択段階であぶり出しておく必要があると思います。

本稿の目次

 

 

SPA開発にはAPIモックが必要 (JSON-SERVER)

サンプルの中には、APIのモックを稼働していただいているものもあります。

しかし、せっかくなので自前でモックを作っておく方が何かと確認しやすいはず。

今回は、簡易的なものとしてJSON Server、時間があればPHPトークン検証を含めたモックを作成したいとおもいます。

npm install -g json-server
json-server -p 3200 --watch db.json 

 こんな感じですかね。

これでlocalhost:3200にアクセスするとjson-serverの説明が出て、db.jsonに格納したリストへのアクセス方法など簡単な説明がでればOK.

検証よりもAPIまでリクエストが到達したというチェックだけでよい場合には簡単です。

Azure-SamplesのAPIモック

いくつかのサンプルで利用されるAPIのモックの構築手順がAzure-Samplesで提示されています。

node.jsとpassport.jsで構築するようです。

github.com

こちらの手順通りに、git clone、npm install、npm startの3ステップ。

 config.jsonで、テナント名やclientID policyName scopeなどを編集しました。

 

サンプル(1) Single-Page Application built on MSAL.js with Azure AD B2C

GitHub - Azure-Samples/active-directory-b2c-javascript-msal-singlepageapp: A single page application (SPA) calling a Web API. Authentication is done with Azure AD B2C by leveraging MSAL.js

最初のMSAL.jsを使ったSPAサンプルといえると思います。

手順通りに進め、Microsoftアカウント(個人用)でサインインできました。
サインインした情報がAPI経由で取得できることも確認できました。
テストした後は、

https://account.live.com/consent/Manage

にアクセスして、アクセス許可を削除しておきます。
(次回のサインアップテストができるようにするためにも)

これで、正常に登録されたアプリであれば、このサンプルが動作することはわかりましたので、次は、自前のテナントと先ほど作ったAPIで実行できるかどうかを確かめます。

自テナントでの試行【サンプル(1)】

b2cScopesの構成

自テナントのアプリケーションでb2cScopesを定義します。

Azure Portalの[管理] の [API の公開]で、アプリケーション ID の URIは、テナントURLの後は、テナントIDが入っていると思いますが、これは自分で識別できればapiなどとしてよいようです。転記ミスのないようにシンプルな方がいいでしょう。

次に、同様にAPIへのアクセス許可も与えておきます。

この手順はアプリケーションを登録した時、APIを登録した時に行えばよいので、私はつい忘れがちです。

ポリシーの構成

対応するポリシー(ユーザーフロー)をまだ作っていなければ、作って設定します。

テナントIDの構成

authConfig.jsに登録したアプリのアプリケーションID(クライアントID)を転記

CORSの構成

先ほどのAzure-SamplesのAPIモックでは、最初からCORS設定済みのようです。

自テナントでの実行結果【サンプル(1)】

前提として、サンプルを試す前に自テナント内でユーザーフローを実行してhttps://jwt.msで受信して確認しておくというのはやっておきます。

それでREADMEの通りにやってみたのですが、やはり手を動かしておく意味はありますね。

サインインの画面まではいくのに、その後のアクションが起きません。

B2Cのユーザーフローには入っており、consoleログを見ると、id_tokenも取れている。
それでauthRedirect.jsをチェックしたのですが、claimとしてacrをチェックしています。ところが、B2Cからのトークンにacrは含まれていないようなのです。

トークンの概要 - Azure Active Directory B2C | Microsoft Docs

acrのチェックは使用したユーザーフローのチェック、つまりユーザーの意図をチェックするものと言えますので、これをtfpに変えてみたところ、無事サインインまでできたようです。

 で、チェックしてから検索したところ、stackoverflowさんに出てますね。acrとtfpの切り替えができるそうです。ユーザーフロー毎のプロパティ設定のようですが、パスワードリセットの際は、acrでないとダメ?なのかもしれません。

azure - acr claim is null for the Active Directory B2C JWT token acquired using Angular SPA - Stack Overflow

 

これで、Azure AD B2Cに登録したユーザーIDでの認証とAPI呼び出しはうまくいきました。

また、Microsoftアカウントで認証すると例外が発生しました。

ServerError: AADB2C90273: An invalid response was received : 'Error: invalid_request,Error Description: Proof Key for Code Exchange is required for cross-origin authorization code redemption.'

PKCEの設定ができていないように思います。実は、acr関係のトラブルに対処するのに、基本の設定からいろいろと変えて試しているうちに迷走してしまったようです。Microsoft用アカウントのリダイレクト先をWebからSPAに変えてしまったためのようです。ドキュメントではWebとして登録すると書いてあったのですが、今回のサンプルがSPAであったため、acrが原因ではなく、そこかな?と思い変更していたのでした。

 

他にもどの設定を間違えるとどんなエラーになるのかというのは一通り経験しておくというのもいいと思います。エラーログを見ているうちに、どの設定がどこで使われているのかが体感できます。

詳しくはログの確認なども必要かもしれません。

MSAL アプリでのログ記録 - Microsoft identity platform | Microsoft Docs

 

サンプル(2) JavaScript Single-page Application secured with MSAL.js using the Authorization Code Flow (PKCE) on Azure AD B2C

github.com

次のサンプルは、Azure AD B2CにMSAL.js v2でアクセスします。いわゆる、承認コードフローです。

インストールしてnpm startするだけで、問題なく動作しました。

自テナントでの実行結果【サンプル(2)】

自テナントに加え、APIは先ほどの例と同様のモックを使います。

編集するファイルも先ほどのものと同様のようです。

いずれも問題なく動作しました。

無事動作してしまうと、あまり収穫がないですが、とりあえず問題なさそうです。

 

Azure Blobへのディプロイ

このサンプルのREADMEにはAzure Blobへのディプロイについて説明があります。
今のところ、私はAzure Storageを使う予定はないので割愛します。

(後日テストしたら更新したいと思います。)

 

サンプル(3) Angular 11 MSAL Angular 2.x B2C Sample

microsoft-authentication-library-for-js/samples/msal-angular-v2-samples/angular11-b2c-sample at dev · AzureAD/microsoft-authentication-library-for-js · GitHub

こちらは、MSAL.js v2とAngular 11を使ったサンプルです。本稿時点では最新の環境と思われます。githubの更新も頻繁に行われていますね。

$ git clone git@github.com:AzureAD/microsoft-authentication-library-for-js.git

$ cd microsoft-authentication-library-for-js/samples/msal-angular-v2-samples/angular11-b2c-sample/

事前準備:

https://github.com/AzureAD/microsoft-authentication-library-for-js/tree/dev/lib/msal-angular

msal-angularのREADMEに沿って準備をしておきます。

npm install @azure/msal-browser @azure/msal-angular@alpha

テストを実行する場合には、

npm run build

npm run test

 とするところでしょうか。

事前準備が終わったらnpm startしてみます。

問題なく動作しますね。外見上はAngularであるかどうか気付く要素もないですが()

consoleにaccess_tokenやid_tokenが出るようになっているようです。

自テナントでの実行結果【サンプル(3)】

 READMEでは、b2c-config.tsとapp.module.tsを編集しろとありますが、おそらく、app.componet.spec.tsについても編集する必要がありそうです。

また、本sampleの実行URLをB2C側のリダイレクトURLとして追加します。

 

これで、動きました。

α版とはいえAngular の11で何事もなく動作しましたので、今後も期待したいと思います。

 

サンプル(4) MSAL.js 2.x Sample - Authorization Code Flow in Single-Page Applications

microsoft-authentication-library-for-js/samples/msal-browser-samples/VanillaJSTestApp2.0 at dev · AzureAD/microsoft-authentication-library-for-js (github.com)

上記のリポジトリにはMSAL.jsの複数のサンプルがあり、すべてを試す必要はなさそうです。

ここでは、最もシンプルであろうMSAL.js 2.xとVanilla.jsのサンプルを試します。

まず、samples/msal-browser-samples/VanillaJSTestApp2.0/ に移動して、READMEを確認。

npm install @azure/msal-browserで実行するだけでは、うまくいきません。

このサンプルを確認すると、どうもローカルのlibを必要としているように見えます。おそらく、自分でビルドしたものを試したいときにはその方がよいという意図でしょう。今回はもう少し軽い使い方でindex.htmlでcdnを参照するように変更、server.jsも書き換えてindex.htmlはサンプルフォルダのものを実行するようにしてみます。

    //res.sendFile(path.join(__dirname + '/index.html'));
    res.sendFile(path.join(APP_DIR + '/index.html'));

そして、npm start

無事動きました。

今回はMicrosoft Graphへのアクセスですし、エンドポイントが

https://login.windows-ppe.net/common/

だったのですが、Office365 Enterpriseのエンドポイントなんでしょうか。