はじめに
今回の記事で使用するコードは下記の構成で実装しています。
記事を参照されるタイミングによっては動作しない可能性がありますのでご留意ください。
- @angular/cli:10.0.5
- @azure/msal-browser:2.0.1
msal.js v2.0.0がリリース
先月の末頃(2020年7月21日)にmsal.jsのv2.0.0 がリリースされました。
(色々試している間にv2.0.1がリリースされちゃいましたが…
PKCEの対応が行われ、よりかんたんにセキュアに使用することができるようになります。
詳細なお話は公式のドキュメントや、今月の.NETラボを見ていただくとして…
ひとまず、僕がメインで使用しているAngularでの使用方法を確認していこうと思います。
@azure/msal-angular
msal.jsには派生ライブラリとして、Angular対応された@azure/msal-angular
というライブラリが存在します。
ServiceやInterceptorが提供され、Angularで使用するのに便利なライブラリとなっています。
👆microsoft-authentication-library-for-jsより抜粋
上図の通り、@azure/msal-angularもv2.0がPKCE対応されるはずなのですが、まだリリースされていないようです。
Roadmapにも特に予定は明記されていないようですが
何はともあれ使用したいので、プレーンな@azure/msal-browser
をAngularで使用してみましょう。
@azure/msal-browserをAngularで使用する。
基本的な使い方は@azure/msal-browserのドキュメントの通りで問題ありません。
ライブラリのインポート
npm install @azure/msal-browser
msalアプリケーションインスタンスの生成
msalのv1からmsalの処理のエントリークラスがUserAgentApplication
からPublicClientApplication
に変更されています。
インスタンスの生成にADアプリなどの設定のConfiguration
を与えるあたりの使い方は変わらないようです。
Configurationの構成自体も大きく変わっていないようで、clientId
やauthority
などしか項目を使っていない場合は設定記述部分は変更する必要はありません。
handleRedirectCallback
loginRedirect
などを行った場合に引っ掛けるhandleRedirectCallback
は大きく変更されています。
Promise
で実行されるようになっておりそれに合わせた変更が必要です。
const res = await this.clientApp.handleRedirectPromise().catch(err => { console.log({err}); });
自分は検証でエラーをトラップしたかったので雑に👆の実装にしていますが、resがvoid | AuthenticationResult
となるのでthen
を使うのもありです。
handleRedirectPromise
の内部処理実行前にloginRedirect
処理などが走るとエラーが発生するので
handleRedirectPromise
の処理終了を待機して、処理終了後にログイン処理を行うのがベターです。
login
loginを行う処理のインターフェースは変わっていません。loginRedirect
、loginPopup
でログインを実行します。
Redirectは前述するCallbackで、PopupはPromiseで結果を取得します。
ユーザー情報の取得
msal.jsのv1ではユーザー情報を取得するインタフェースがClientApp上に用意されていましたが、v2でなくなりました。
msal.jsのv2では、ログインやトークン取得時の結果としてアカウント情報が含まれた状態で返却されるので
そこからデータを取得することになります。
リダイレクトの場合は下記のようにhandleRedirectPromise
で取得できたレスポンスから抽出することになります。
const res = await this.clientApp.handleRedirectPromise().catch(err => { console.log({err}); }); this.account = (res as AuthenticationResult).account;
PopupはloginPopup
の返却値です。
アクセストークンの取得
acquireTokenSilent
で必要な引数にアカウント情報のパラメータが追加されています。
const res = await this.clientApp.acquireTokenSilent({ scopes: this.scopes, account: this.account }); return res.accessToken;
ログイン時などにアカウント情報は取得し、プライベートな変数なりに格納しておいたほうが良さそうです。
ここまでの実装はAngular特有というものではなく、普通の@azure/msal-browserの使用の仕方ですね。
本家のmsalServiceよろしく、一つのServiceクラスに👆までの処理を実装しておくと使いやすいと思います。
Interceptorの作成
Angularのサービスらしく。ということでInterceptorを作成し、WebAPIへの通信時に自動的にヘッダにトークンを付与させます。
ここは@azure/msal-angularのInterceptorをちょっとだけ参考にします。
[ { endPoint: string, scopes: string[] } ]
上記の構造の設定を使用して、アクセスするエンドポイントによって
使用するスコープを切り替える機能を実装していきます。
(👇本家と比べるとかなり雑ですが…!
@Injectable() export class MsalInterceptor implements HttpInterceptor { constructor( private auth: MsalService ) {} intercept(req: HttpRequest<any>, next: HttpHandler): Observable<HttpEvent<any>> { const target = environment.endPointTargetScope.filter(f => req.url.includes(f.endPoint)); let scopes = []; if (target.length > 0) { scopes = target[0].scopes; } if (!scopes || scopes === []) { return next.handle(req); } return from( this.auth.acquireTokenSilent(scopes) .then((token: string) => { const authHeader = `Bearer ${token}`; return req.clone({ setHeaders: { Authorization: authHeader, } }); }) ) .pipe( mergeMap(nextReq => next.handle(nextReq)) ); } }
ひとまずこれで、ログイン👉アクセストークン取得👉WebAPI通信の必要最低限な動きは確認できるようになります。
まとめ
Angularでのmsal.js v2.0.xの使用の仕方を見てきました。
@azure/msal-angularのv2がリリースされる可能性はありますが
@azure/msal-browser準拠のインターフェースや使い方になりそうな気はしているので
これを使用して、変わりそうな箇所を抑えておくのも良いかもしれません。
今回検証で使用したソースコードは下記にのごった煮レポジトリ格納しています。