MSAL React の使用を開始する

この記事では、 @azure/msal-reactの使用を開始する方法について説明します。 初期化、ユーザーの認証の判断、コンポーネントの保護、ユーザーのサインイン、アクセス トークンの取得について説明します。

Prerequisites

初期化

@azure/msal-reactReact コンテキスト API 上に構築されており、認証を必要とするアプリのすべての部分を MsalProvider コンポーネントにラップする必要があります。 まず、のインスタンスをPublicClientApplicationしてから、これを prop としてMsalProviderに渡す必要があります。

import React from "react";
import { createRoot } from "react-dom/client";

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

import App from "./app.jsx";

// MSAL configuration
const configuration: Configuration = {
    auth: {
        clientId: "client-id"
    }
};

const pca = new PublicClientApplication(configuration);

// Component
const AppProvider = () => (
    <MsalProvider instance={pca}>
        <App />
    </MsalProvider>
);

const root = createRoot(document.getElementById("root"));
root.render(<AppProvider />);

MsalProviderの下にあるすべてのコンポーネントは、コンテキストだけでなく、PublicClientApplicationによって提供されるすべてのフックとコンポーネントを介して@azure/msal-react インスタンスにアクセスできます。

ユーザーが認証されているかどうかを判断する

ほとんどのアプリケーションでは、ユーザーがサインインしているかどうかに基づいて、特定のコンポーネントを条件付きでレンダリングする必要があります。 @azure/msal-react は、これを行う 2 つの簡単な方法を提供します。

AuthenticatedTemplate および UnauthenticatedTemplate

AuthenticatedTemplate および UnauthenticatedTemplate コンポーネントは、ユーザーがそれぞれ認証または認証されていない場合にのみ子をレンダリングします。

import React from 'react';
import { AuthenticatedTemplate, UnauthenticatedTemplate } from "@azure/msal-react";

export function App() {
    return (
        <React.Fragment>
            <p>Anyone can see this paragraph.</p>
            <AuthenticatedTemplate>
                <p>At least one account is signed in!</p>
            </AuthenticatedTemplate>
            <UnauthenticatedTemplate>
                <p>No users are signed in!</p>
            </UnauthenticatedTemplate>
        </React.Fragment>
    );
}

useIsAuthenticated フック

アプリの上のラッパー コンポーネントの代わりに、 useIsAuthenticated フックを使用できます。 詳細については、 MSAL React の Hooks を参照してください。

import React from 'react';
import { useIsAuthenticated } from "@azure/msal-react";

export function App() {
    const isAuthenticated = useIsAuthenticated();

    return (
        <React.Fragment>
            <p>Anyone can see this paragraph.</p>
            {isAuthenticated && (
                <p>At least one account is signed in!</p>
            )}
            {!isAuthenticated && (
                <p>No users are signed in!</p>
            )}
        </React.Fragment>
    );
}

コンポーネントの保護

認証されたユーザーにのみ表示するコンポーネントがある場合は、上記のいずれかの方法を使用できます。 しかし、ユーザーがまだ認証されていない場合にログインを自動的に呼び出す場合はどうでしょうか。 @azure/msal-react は、 MsalAuthenticationTemplate または useMsalAuthentication フックでこれを行う 2 つの方法を提供します。

MsalAuthenticationTemplate コンポーネント

MsalAuthenticationTemplate コンポーネントは、ユーザーが認証されている場合はその子要素をレンダリングし、そうでない場合はユーザーをサインインさせようとします。 使用する対話の種類 (リダイレクトまたはポップアップ) と、必要に応じて、ログイン API に渡す 要求オブジェクト 、認証の進行中に表示するコンポーネント、またはエラーが発生した場合に表示するコンポーネントを指定するだけです。

この実際の例は、 ページのいずれかの/profileで確認できます。

import React from "react";
import { MsalAuthenticationTemplate } from "@azure/msal-react";
import { InteractionType } from "@azure/msal-browser";

function ErrorComponent({error}) {
    return <p>An Error Occurred: {error}</p>;
}

function LoadingComponent() {
    return <p>Authentication in progress...</p>;
}

export function Example() {
    const authRequest = {
        scopes: ["openid", "profile"]
    };

    return (
        // authenticationRequest, errorComponent and loadingComponent props are optional
        <MsalAuthenticationTemplate 
            interactionType={InteractionType.Popup} 
            authenticationRequest={authRequest} 
            errorComponent={ErrorComponent} 
            loadingComponent={LoadingComponent}
        >
            <p>At least one account is signed in!</p>
        </MsalAuthenticationTemplate>
      )
};

useMsalAuthentication フック

useMsalAuthentication フックは、まずユーザーがサインインしているかどうかを確認してから、サインインしたユーザーがいない場合にユーザーのサインインを試みます。 使用する対話の種類 (リダイレクトまたはポップアップ) を指定する必要があります。 ログイン操作の結果、発生したエラー、再試行する必要がある場合に使用できるログイン関数が返されます。

このフックの詳細については、 hooks ドキュメントを参照してください。

import React from 'react';
import { useMsalAuthentication } from "@azure/msal-react";
import { InteractionType } from '@azure/msal-browser';

export function App() {
    const {login, result, error} = useMsalAuthentication(InteractionType.Popup);

    return (
        <React.Fragment>
            <p>Anyone can see this paragraph.</p>
            <AuthenticatedTemplate>
                <p>At least one account is signed in!</p>
            </AuthenticatedTemplate>
            <UnauthenticatedTemplate>
                <p>No users are signed in!</p>
            </UnauthenticatedTemplate>
        </React.Fragment>
    );
}

によって提供されるログイン API を使用してユーザーにサインインする @azure/msal-browser

サインインを呼び出すもう 1 つの方法は、コンテキスト内の@azure/msal-browser インスタンスから直接PublicClientApplication API を使用することです。 コンテキストからインスタンスにアクセスする方法は 3 つあります。

useMsal フック

PublicClientApplication インスタンスを返すフック、現在サインインしているすべてのアカウントの配列、および現在の msal の動作を示すinProgress値。

このフックの詳細については、 hooks ドキュメントを参照してください。

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

export function App() {
    const { instance, accounts, inProgress } = useMsal();

    if (accounts.length > 0) {
        return <span>There are currently {accounts.length} users signed in!</span>
    } else if (inProgress === "login") {
        return <span>Login is currently in progress!</span>
    } else {
        return (
            <>
                <span>There are currently no users signed in!</span>
                <button onClick={() => instance.loginPopup()}>Login</button>
            </>
        );
    }
}

生のコンテキストの利用

クラス コンポーネントを使用していて、フックを使用できない場合は、 MsalContextを介して生の msal コンテキストを使用できます。 クラス コンポーネントでの @azure/msal-react の使用の詳細 については、こちらをご覧ください

import React from "react";
import { MsalContext } from "@azure/msal-react";

class App extends React.Component {
    static contextType = MsalContext;

    render() {
        if (this.context.accounts.length > 0) {
            return <span>There are currently {this.context.accounts.length} users signed in!</span>
        } else if (this.context.inProgress === "login") {
            return <span>Login is currently in progress!</span>
        } else {
            return (
                <>
                    <span>There are currently no users signed in!</span>
                    <button onClick={() => this.context.instance.loginPopup()}>Login</button>
                </>
            );
        }
    }
}

コンポーネントを withMsal 上位コンポーネントでラップする

クラス コンポーネントと関数コンポーネントの両方で MSAL コンテキストを使用するもう 1 つの方法は、コンポーネントを withMsal HOC でラップし、コンポーネントのプロパティにコンテキストを挿入することです。

import React from "react";
import { withMsal } from "@azure/msal-react";

class LoginButton extends React.Component {
    render() {
        const isAuthenticated = this.props.msalContext.accounts.length > 0;
        const msalInstance = this.props.msalContext.instance;
        if (isAuthenticated) {
            return <button onClick={() => msalInstance.logout()}>Logout</button>    
        } else {
            return <button onClick={() => msalInstance.loginPopup()}>Login</button>
        }
    }
}

export default YourWrappedComponent = withMsal(LoginButton);

アクセス トークンの取得

API にアクセスするためにアクセス トークンが必要になるたびに、acquireTokenSilent オブジェクトでPublicClientApplication API を呼び出することをお勧めします。 これは、前のセクションで説明した方法と同様に実行できます。

import React, { useState, useEffect } from "react"
import { useMsal, useAccount } from "@azure/msal-react";

export function App() {
    const { instance, accounts, inProgress } = useMsal();
    const account = useAccount(accounts[0] || {});
    const [apiData, setApiData] = useState(null);

    useEffect(() => {
        if (account) {
            instance.acquireTokenSilent({
                scopes: ["User.Read"],
                account: account
            }).then((response) => {
                if(response) {
                    callMsGraph(response.accessToken).then((result) => setApiData(result));
                }
            });
        }
    }, [account, instance]);

    if (accounts.length > 0) {
        return (
            <>
                <span>There are currently {accounts.length} users signed in!</span>
                {apiData && (<span>Data retreived from API: {JSON.stringify(apiData)}</span>)}
            </>
        );
    } else if (inProgress === "login") {
        return <span>Login is currently in progress!</span>
    } else {
        return <span>There are currently no users signed in!</span>
    }
}

React コンポーネントの外部でアクセス トークンを取得する

React コンポーネントの外部でアクセス トークンが必要な場合は、acquireTokenSilentPublicClientApplication関数を直接呼び出すことができます。 コンテキスト内のコンポーネントが正しく更新されない可能性があるため、 MsalProvider によって提供される react コンテキストの外部でユーザーの認証状態 (ログイン、ログアウト) を変更する関数を呼び出すことはお勧めしません。

トークンを取得する前に、ユーザーがサインインする必要があることに注意してください。

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

// This should be the same instance you pass to MsalProvider
const msalInstance = new PublicClientApplication(config);

const acquireAccessToken = async (msalInstance) => {
    const activeAccount = msalInstance.getActiveAccount(); // This will only return a non-null value if you have logic somewhere else that calls the setActiveAccount API
    const accounts = msalInstance.getAllAccounts();

    if (!activeAccount && accounts.length === 0) {
        /*
        * User is not signed in. Throw error or wait for user to login.
        * Do not attempt to log a user in outside of the context of MsalProvider
        */   
    }
    const request = {
        scopes: ["User.Read"],
        account: activeAccount || accounts[0]
    };

    const authResult = await msalInstance.acquireTokenSilent(request);

    return authResult.accessToken
};

こちらも参照ください