MSAL v1 から @azure/msal-react および @azure/msal-browser への移行ガイド

この記事では、MSAL v1 から @azure/msal-react および @azure/msal-browser への移行の概要について説明します。 PKCE と条件付きアクセスを使用した承認コード フローを使用して、パフォーマンスを向上させ、セキュリティを向上させるために移行することをお勧めします。 さらに、シングル ページ アプリケーションのサポートが向上しています。

Prerequisites

  • アクティブなサブスクリプションを持つ Azure アカウント。 無料でアカウントを作成する
  • Microsoft Entra テナントに登録されている既存のアプリケーション。

アプリの登録の更新

@azure/msal-react ライブラリは、PKCE を使用して@azure/msal-browserを実装するのラッパーです。 これは、 暗黙的フローを実装する MSAL v1 ライブラリからの重要な更新です。

新しいアプリの登録を作成するか、既存のアプリを更新して、新しい redirectUri の種類 "SPA" を使用する必要があります。 詳細については、「 シングルページ アプリケーション: アプリの登録 」を参照してください。

@azure/msal-reactのインストールと@azure/msal-browser

@azure/msal-reactとそのピア依存関係@azure/msal-browserの両方を npm からインストールできます。 古い MSAL パッケージをアンインストールすることが重要です。 ターミナルを開き、次のコマンドを実行します。

npm uninstall msal
npm install @azure/msal-react @azure/msal-browser

react-aad-msal のアップグレード

アプリで現在認証に React Microsoft Entra MSAL を使用していて、このセクション@azure/msal-reactに移行しようとしている場合は、2 つのライブラリの違いと、行う必要がある変更の一部について説明します。 React Microsoft Entra MSAL はサード パーティ製のライブラリであり、MSAL React は一から構築されており、MSAL React でカバーされていない、またはサポートされていないエッジ ケースがある場合があります。

react-aad-msalではサポートされていない@azure/msal-reactでサポートされる機能を次に示します。

  • 保護されたコンポーネントをレンダリングする前に IdToken の有効期限を確認する & 期限切れの IdToken の自動更新
  • Redux ストアを標準でサポート(代替案は以下)

react-aad-msalでは可能でも、@azure/msal-reactではできなくなったその他のケースについては、microsoft-authentication-library-for-js の GitHub リポジトリで Issue を作成してください。

初期化

react-aad-msalでは、後でMsalAuthProvider コンポーネントに渡されるAzureAD オブジェクトを作成して、MSAL インスタンスを初期化します。

import { MsalAuthProvider } from "react-aad-msal";

const authProvider = new MsalAuthProvider(config, authenticationParameters, options);

@azure/msal-reactでは、PublicClientApplicationからエクスポートされた@azure/msal-browserを使用して MSAL インスタンスを初期化し、MsalProviderからエクスポートされた@azure/msal-react コンポーネントに渡します。 構成オプションは、 msal@azure/msal-browserの間でほぼ似ていますが、最新の構成オプションについては 構成の種類 を参照できます。

authenticationParametersで使用されるoptionsパラメーターとreact-aad-msal パラメーターは@azure/msal-reactでは使用されませんが、個々のコンポーネントでも同様の機能を実現できます。 これについては、このドキュメントの後半で説明します。

@azure/msal-react では React Context API を使用して、コンポーネントツリー全体で PublicClientApplication と認証状態を利用できるようにします。

import { PublicClientApplication } from "@azure/msal-browser";
import { MsalProvider } from "@azure/msal-react";

const pca = new PublicClientApplication(config);

function App() {
    return (
        <MsalProvider instance={pca}>
            <YourAppComponents />
        </MsalProvider>
    );
}

MsalProvider コンポーネントに関する一般的な注意事項:

  • @azure/msal-reactによって公開される認証状態またはフック/コンポーネントへのアクセスを必要とするすべてのコンポーネントは、コンポーネント ツリーの上位にMsalProviderする必要があるため、MsalProvider可能な限りルートの近くにレンダリングすることをお勧めします。
  • アプリでは、特定のページに 1 つ以上の MsalProvider コンポーネントをレンダリングしないでください。
  • 再レンダリングの可能性があるため、コンポーネント内で PublicClientApplication を初期化することはお勧めしません

コンポーネントの保護

react-aad-msal コンポーネントは、AzureAD コンポーネントまたは withAuthentication HOC を使用して保護されます。これは内部で AzureAD を使用してコンポーネントをラップします。 AzureAD コンポーネントは、ユーザーが認証された場合にのみ子コンポーネントをレンダリングし、ユーザーが認証されていない場合は必要に応じてログインを開始します。 ログインに使用するオプション (スコープ、ポップアップやリダイレクトのどちらを使用するかなど) は、 authProvider prop を作成するときに先に指定します。

import { MsalAuthProvider } from "react-aad-msal";

const authProvider = new MsalAuthProvider(config, authenticationParameters, options);

function App() {
    return (
        <AzureAD provider={authProvider} forceLogin={true}>
            <span>Only authenticated users can see me.</span>
        </AzureAD>
    );
}

@azure/msal-react一方、開発者は、誰に表示するかをより詳細に制御できます。

  • AuthenticatedTemplate コンポーネントは、ユーザーが認証された場合に子をレンダリングします
  • UnauthenticatedTemplate コンポーネントは、ユーザーが認証されていない場合に子をレンダリングします
  • MsalAuthenticationTemplate コンポーネントは、ユーザーが認証されていない場合は自動的にログインを開始し、ユーザーが認証されると子をレンダリングします。
import { PublicClientApplication, InteractionType } from "@azure/msal-browser";
import { MsalProvider, AuthenticatedTemplate, UnauthenticatedTemplate, MsalAuthenticationTemplate } from "@azure/msal-react";

const pca = new PublicClientApplication(config);

function App() {
    return (
        <MsalProvider instance={pca}>
            <AuthenticatedTemplate>
                <span>Only authenticated users can see me.</span>
            </AuthenticatedTemplate>
            <UnauthenticatedTemplate>
                <span>Only unauthenticated users can see me.</span>
            </UnauthenticatedTemplate>
            <MsalAuthenticationTemplate interactionType={InteractionType.Popup} authenticationRequest={request}>
                <span>Only authenticated users can see me. Unauthenticated users will get a popup asking them to login first.</span>
            </MsalAuthenticationTemplate>
        </MsalProvider>
    );
}

さらに、フックベースのアプローチを採用したい場合は、@azure/msal-react には同様の結果を実現するために使用できるフックがいくつか用意されています。 これらは基本的な例にすぎません。詳細については、 MSAL React フック を参照してください。

import { PublicClientApplication, InteractionType } from "@azure/msal-browser";
import { MsalProvider, useIsAuthenticated, useMsalAuthentication } from "@azure/msal-react";

const pca = new PublicClientApplication(config);

function App() {
    return (
        <MsalProvider instance={pca}>
            <ExampleComponent />
        </MsalProvider>
    );
}

function ExampleComponent() {
    const isAuthenticated = useIsAuthenticated();
    const { error } = useMsalAuthentication(InteractionType.Popup, request); // Will initiate a popup login if user is unauthenticated

    if (isAuthenticated) {
        return <span>Only authenticated users can see me.</span>
    } else if (error) {
        return <span>An error occurred during login!</span>
    } else {
        return <span>Only unauthenticated users can see me.</span>
    }
}

アクセス トークンの取得

react-aad-msal は、API を呼び出す前にアクセス トークンを取得するために使用できる getAccessToken メソッドを公開します。

import { MsalAuthProvider } from "react-aad-msal";

const authProvider = new MsalAuthProvider(config, authenticationParameters, options);
const accessToken = authProvider.getAccessToken();

@azure/msal-react@azure/msal-browserを使用する場合は、acquireTokenSilent インスタンスでPublicClientApplicationを呼び出します。

MsalProviderの下に存在するコンポーネントまたはフック内のアクセス トークンを取得する必要がある場合は、useMsal フックを使用して、必要なオブジェクトを取得できます。

import { useState } from "react";
import { useMsal } from "@azure/msal-react";
import { InteractionRequiredAuthError } from "@azure/msal-browser";

function useAccessToken() {
    const { instance, accounts } = useMsal();
    const [accessToken, setAccessToken] = useState(null);

    if (accounts.length > 0) {
        const request = {
            scopes: ["User.Read"],
            account: accounts[0]
        };
        instance.acquireTokenSilent(request).then(response => {
            setAccessToken(response.accessToken);
        }).catch(error => {
            // acquireTokenSilent can fail for a number of reasons, fallback to interaction
            if (error instanceof InteractionRequiredAuthError) {
                instance.acquireTokenPopup(request).then(response => {
                    setAccessToken(response.accessToken);
                });
            }
        });
    }

    return accessToken;
}

MsalProviderのコンテキスト外でアクセス トークンを取得する必要がある場合は、PublicClientApplication インスタンスを直接使用し、getAllAccounts()を呼び出してアカウント オブジェクトを取得できます。

Important

MsalProviderのコンテキスト外でのみサイレント トークンの取得を試みます。 MsalProviderのコンテキスト外で対話型メソッド (リダイレクトまたはポップアップ) を呼び出さないでください。

次の例は、デモンストレーション目的での PublicClientApplication の初期化を示しています。 PublicClientApplication は、ページ読み込みごとに 1 回だけ初期化する必要があり、ここで MsalProviderに指定したのと同じインスタンスを使用する必要があります。

import { PublicClientApplication } from "@azure/msal-browser";

const pca = new PublicClientApplication(config);
const accounts = pca.getAllAccounts();

async function getAccessToken() {
    if (accounts.length > 0) {
        const request = {
            scopes: ["User.Read"],
            account: accounts[0]
        }
        const accessToken = await pca.acquireTokenSilent(request).then((response) => {
            return response.accessToken;
        }).catch(error => {
            // Do not fallback to interaction when running outside the context of MsalProvider. Interaction should always be done inside context.
            console.log(error);
            return null;
        });

        return accessToken;
    }

    return null;
}

ID トークンの取得

react-aad-msal idToken を取得または更新するために getIdToken 関数を公開しました。

import { MsalAuthProvider } from "react-aad-msal";

const authProvider = new MsalAuthProvider(config, authenticationParameters, options);
const token = await authProvider.getIdToken();
const idToken = token.idToken.rawIdToken;

idToken を取得するために、 clientId を唯一のスコープとして要求するパターンにも精通している場合があります。 これは、 @azure/msal-browserでサポートされているパターンではなくなりました。

@azure/msal-reactおよび@azure/msal-browserでは、すべてのトークン呼び出しでアクセス トークンと ID トークンの両方が返され、すべてのアクセス トークンの更新でも ID トークンが更新されます。

MsalProviderの下に存在するコンポーネントまたはフック内で ID トークンを取得する必要がある場合は、useMsal フックを使用して、必要なオブジェクトを取得できます。

import { useState } from "react";
import { useMsal } from "@azure/msal-react";

function useIdToken() {
    const { instance, accounts } = useMsal();
    const [idToken, setIdToken] = useState(null);

    if (accounts.length > 0) {
        const request = {
            scopes: ["openid"],
            account: accounts[0]
        };
        instance.acquireTokenSilent(request).then(response => {
            setIdToken(response.idToken);
        }).catch(error => {
            // acquireTokenSilent can fail for a number of reasons, fallback to interaction
            if (error instanceof InteractionRequiredAuthError) {
                instance.acquireTokenPopup(request).then(response => {
                    setIdToken(response.idToken);
                });
            }
        });
    }

    return idToken;
}

MsalProviderのコンテキスト外で ID トークンを取得する必要がある場合は、PublicClientApplication インスタンスを直接使用し、getAllAccounts()を呼び出してアカウント オブジェクトを取得できます。

Important

MsalProviderのコンテキスト外でのみサイレント トークンの取得を試みます。 MsalProviderのコンテキスト外で対話型メソッド (リダイレクトまたはポップアップ) を呼び出さないでください。

次の例は、デモンストレーション目的での PublicClientApplication の初期化を示しています。 PublicClientApplication は、ページ読み込みごとに 1 回だけ初期化する必要があり、ここで MsalProviderに指定したのと同じインスタンスを使用する必要があります。

import { PublicClientApplication } from "@azure/msal-browser";

const pca = new PublicClientApplication(config);
const accounts = pca.getAllAccounts();

async function getIdToken() {
    if (accounts.length > 0) {
        const request = {
            scopes: ["openid"],
            account: accounts[0]
        }
        const idToken = await pca.acquireTokenSilent(request).then((response) => {
            return response.idToken;
        }).catch (error => {
            // Do not fallback to interaction when running outside the context of MsalProvider. Interaction should always be done inside context.
            console.log(error);
            return null;
        });

        return idToken
    }

    return null;
}

redux ストア統合の更新/イベントへの対応

react-aad-msal ログインやログアウトなどのイベントが発生したときにアクションをディスパッチすることで、redux ストアとのすぐに統合できます。 @azure/msal-reactではこの機能は提供されませんが、同様の機能は、によって公開される@azure/msal-browser を使用して実現できます。

イベントがブロードキャストされるたびに呼び出されるイベント コールバックを登録できます (例: LOGIN_SUCCESS)。 コールバック関数は、イベントを検査し、ペイロードを使用して何かを行うことができます。 既存の redux ストアを引き続き使用する場合は、ストアにアクションをディスパッチするイベント コールバックを登録できます。

import { PublicClientApplication, EventType } from "@azure/msal-browser";
import { store } from "your-redux-store-implementation";

const msalInstance = new PublicClientApplication(config);

const callbackId = msalInstance.addEventCallback((message: EventMessage) => {
    if (message.eventType === EventType.LOGIN_SUCCESS) {
        store.dispatchAction({type: "AAD_LOGIN_SUCCESS", payload: message.payload});
    }
});

ペイロードは、 msal v1 と @azure/msal-browser で異なる場合があるため、アプリケーションが特定のフィールドまたはオブジェクトの形状に依存している場合は、いくつかの調整が必要になる場合があります。 Typedoc には、 イベントの種類ペイロードの種類 の最新の一覧が含まれており、 イベント ドキュメントで 2 つの間のマッピングを見つけることができます。

こちらも参照ください