MSAL.js+Azure AD v2を触ってみる
はじめに
使用ライブラリは下記のバージョンでお送ります。
msal.js: 1.0.1
SPAっぽいなにかを作りたかったのでAngularも使用しています。
@angular/cli: 8.0.0
ただ、記述するソースコードの殆どはmsal.jsのヴァニラなものを使用しているので
他のフレームワークやバニラJSでも問題なく動作すると思います。
Azureは2019/6時点の画面をキャプチャしています。
それぞれの内容について記事をご覧になるタイミングによっては、画面、仕様が変更されている可能性があるのでご留意くださいませ。
あらまし
マイクロソフトのアカウント認証に便利なJavascriptのライブラリ、msal.js
長らく0.x.xとβ?みたいな扱いだったのですが、いつの間にか(5/4)に1.0.0とメジャーバージョンアップしていたので改めて使い方を復習してみようと思います。
この記事ではmsal.jsの使い方にのみフォーカスを当てます。
Azure AD アプリケーションの作り方とかADのスコープの設定とか
その周りの話については割愛していきます。
Azure AD v2については流れ上触る機会があったので簡単に。。。
単純にサインイン
msal.jsのREADMEにある通り、まずはClientIdのみを指定してログイン処理を実行してみます。
import * as msal from 'msal'; export class SampleAuth { private msalClient: msal.UserAgentApplication; login() { const con: msal.Configuration = { auth: { clientId: 'd476053b-7f9a-4c9a-9241-cbd54714266b' } }; this.msalClient = new msal.UserAgentApplication(con); this.msalClient.loginPopup({scopes: ['openid']}).then( res => { console.log(res); } ); } }
単純に書くとこんなところですね。
何はともあれ実行してみましょう。
いつものログイン画面が表示されました。
ログイン後、バッチリアカウント情報も取得できているようです。
注意点-LoginPopup
LoginPopupを行う場合、ポップアップされた画面で認証画面からのリダイレクトが発生します。
しかし、リダイレクト先でUserAgentApplication
インスタンスが生成されていないと
ポップアップ画面が閉じられず残りっぱなしになってしまうようです。
ログイン用のボタン押下でインスタンス生成する処理になっている場合はこの部分で嵌りそうです(ハマった)。
リダイレクトページのコンストラクタなどでインスタンスを生成するようにしておく必要があります。
トークンを取得する
何はともあれトークンを取得しないことには始まりません。
ログインができたのであれば、トークンを取得してみましょう。
acquireTokenSilent
を使用し、openid
のスコープでトークンを取得してみます。
login() { const con: msal.Configuration = { auth: { clientId: 'd476053b-7f9a-4c9a-9241-cbd54714266b' } }; this.msalClient = new msal.UserAgentApplication(con); this.msalClient.loginPopup({scopes: ['openid']}).then( res => { console.log(res); this.msalClient.acquireTokenSilent({ scopes: ['openid' ] }).then(token => { console.log('get token'); console.log(token); }); } ); }
トークンが取得できました。
The provided value for the input parameter 'response_type' is not allowed for this client....
上記のようなエラーが出る場合は認証に使用しているADアプリケーションの暗黙的フローの許可がされていない場合があるので確認してみてください。
生成されたトークンがどのようなものか
JSON Web Tokens - jwt.io で確認してみます。
iss
でADのテナントID、aud
で使用したADアプリケーションIDが設定されていることが確認できます。
取得したトークンで、ASP.net Coreで構築した認証付きのWebAPIにアクセスしてみます。
しかし、APIにアクセスはできませんでした。
それは当然で、生成されたJWTのaud
は認証用に使用しているAzure ADアプリケーションのClientIdで
APIの認証で使用されているAzure ADアプリケーションのClientIdとは異なるからです。
WebAPIのClientIdは "ClientId": "792dd3ef-a306-4288-b12d-5aff71f16193"
が指定されています。
WebAPI側のClientIdを変更してもいいのですが、それも芸がないので、別の方法を使用してみます。
AzureADアプリケーションの設定変更
認証で使用しているAzureADアプリケーションで、作成したWebAPIのPermissionを通しておきます。
こんな状態ですね。
スコープの変更
追加されたAPI認証にしているアプリケーションのスコープは下図で取得できます。
早速、スコープを指定し、トークンを取得してみます。
this.msalClient.acquireTokenSilent({ scopes: [ 'user.read' ] })
となっていたものを
this.msalClient.acquireTokenSilent({ scopes: [ '<APIのスコープ>' ] })
のように変更してみます。
これでトークンが取得できました。
注意点-Authorityの指定
Authorityを未指定のままでトークン取得処理を実行すると下記のようなエラーが発生しました。
AADSTS501941: Resource '792dd3ef-a306-4288-b12d-5aff71f16193'(WebApplication1) is not configured as a multi-tenant application. Usage of the /common endpoint is not supported for such applications created after '10/15/2018'. Use a tenant-specific endpoint or configure the application to be multi-tenant.
標準で使用されている認証のURL https://login.microsoft.com/common
は使用できず、テナントの指定を促されました。
その場合は、msal.jsのインスタンス生成時にauthority
を指定することで回避できます。
const con: msal.Configuration = { auth: { clientId: 'd476053b-7f9a-4c9a-9241-cbd54714266b', authority: 'https://login.microsoftonline.com/<テナントのGUID>' } }; this.msalClient = new msal.UserAgentApplication(con);
注意点 - トークンの指定
ADのトークンは仕様として、複数のリソースのトークンを一挙に取得できないようです。
複数のリソースの承認を取得する (Microsoft Authentication Library for .NET) | Microsoft Docs
なので、スコープ指定時に、 scopes: [ 'user.read', '<APIのスコープURI>' ]
のようにリソースをまたがる設定にすると
AADSTS28000: Provided value for the input parameter scope is not valid because it contains more than one resource.
上記のようなエラーとなります。トークンの取得は単一リソースごとに取得が基本ということですね。
Azure AD アプリケーションのエンドポイントを v2の設定に
さて、取得できたトークンを解析してみると、aud
は下図のような状態でした。
ほしいのはClientIdであって、スコープではありません。
Azure AD v2であればClientIDを取得できそうなので(情報元は失念。。。)Azure AD アプリケーションの設定を変更します。
accessTokenAcceptedVersion
の null
を 2
に変更しました。
これで再度トークンを取得してみます。
お望みのトークンを取得できました。
ASP.net Core WebAPIの実装
さて、トークンは帰ってきましたが、Azure AD v2を使用することでissのエンドポイントが変わってしまいました。
(URLの末尾に/v2.0
がついた)
GitHubにサンプルが転がっていたのでそれと同じように実装してみましょう。
と、いってもやっていることは非常に単純で 上記GitHubで管理されているMicrosoft.Identity.Web
をAPIプロジェクトで参照して
startup.cs
の認証部分を少し書き換えるだけですね。appsettings.json
も書き換え必要ありません。
(Microsoft.Identity.Web
はNuGetで公開されていないのですね…)
startup.cs
- services.AddAuthentication(AzureADDefaults.BearerAuthenticationScheme)
- .AddAzureADBearer(options => Configuration.Bind("AzureAd", options));
+ services.AddProtectWebApiWithMicrosoftIdentityPlatformV2(Configuration);
msal.jsで取得したトークンで、APIを使用できるようになりました!
まとめ
msal.jsの基本的な使用方法と派生してAzure AD v2の認証方法を抑えることができました。
ただ、Azure ADv2は各所みるにまだまだ開発中の匂いがプンプンしますし
AzureADアプリケーションを使用して認証を行う場合は
v1エンドポイントを使用して、adal.jsを使用するほうがまだまだ無難なようです。
力尽きたので、Azure AD B2Cとmsal.jsの認証は別の機会に記事にしようと思います。。。