はまったりひらめいたり…とか…

Angularや.NETやAzureやその他色々。

msal.js v2.0.1 をAngularで使用する

はじめに

今回の記事で使用するコードは下記の構成で実装しています。

記事を参照されるタイミングによっては動作しない可能性がありますのでご留意ください。

  • @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で使用するのに便利なライブラリとなっています。

f:id:TakasDev:20200809223243p:plain

👆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の構成自体も大きく変わっていないようで、clientIdauthorityなどしか項目を使っていない場合は設定記述部分は変更する必要はありません。

参考 - Configuration

handleRedirectCallback

loginRedirectなどを行った場合に引っ掛けるhandleRedirectCallbackは大きく変更されています。

Promiseで実行されるようになっておりそれに合わせた変更が必要です。

const res = await this.clientApp.handleRedirectPromise().catch(err => {
    console.log({err});
});

自分は検証でエラーをトラップしたかったので雑に👆の実装にしていますが、resがvoid | AuthenticationResultとなるのでthenを使うのもありです。

handleRedirectPromiseの内部処理実行前にloginRedirect処理などが走るとエラーが発生するので

handleRedirectPromiseの処理終了を待機して、処理終了後にログイン処理を行うのがベターです。

login

loginを行う処理のインターフェースは変わっていません。loginRedirectloginPopupでログインを実行します。

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準拠のインターフェースや使い方になりそうな気はしているので

これを使用して、変わりそうな箇所を抑えておくのも良いかもしれません。

今回検証で使用したソースコードは下記にのごった煮レポジトリ格納しています。

github.com