クイック スタート: msal.js を使用して Dataverse の SPA アプリケーションを登録して構成する
このトピックでは、msal.js および Cross-origin Resource Sharing (CORS) を使用して Microsoft Dataverse のデータにアクセスするために最も単純化された Single Page Application (SPA) の登録および構成のプロセスについて説明されます。 詳細: OAuth を使用するクロス オリジン リソース共有を使用して単一ページのアプリケーションを Dataverse に接続する。
前提条件
Dataverse 環境へアクセスします。
有効なサブスクリプションを持つ Azure アカウント。
Azure アカウントには、Microsoft Entra ID でアプリケーションを管理するためのアクセス許可が必要です。 以下の Microsoft Entra ID ロールのいずれかに、必要なアクセス許可が含まれています:
Visual Studio Code (VS Code) ダウンロード
このクイック スタートの目的
このクイック スタートを完了すると、シンプルな SPA アプリケーションを実行できるようになり、ユーザーが Dataverse からのデータを認証して取得できる機能が提供されます。
アプリケーションをデバッグする場合、最初にログインボタンのみが表示されます。
ログイン をクリックして、資格情報を入力するためのポップアップが開きます。
資格情報を入力した後は、ログイン ボタンが非表示となり、ログアウト ボタンと 取引先企業の取得 ボタンが表示されるようになります。 また、ユーザー アカウントからの情報を使用した挨拶も表示されるようになります。
取引先企業の取得 ボタンをクリックし、Dataverse 組織から 10 件の取引先企業レコードの一覧を取得します。 次のスクリーン ショットに示された結果:
最後に ログアウト ボタンをクリックしてログアウトします。
注意
この SPA アプリケーションは、堅牢な SPA アプリケーションを開発するパターンを表記することを目的としたものではありません。 アプリケーションの登録および構成におけるプロセスにフォーカスを設定することが簡素化されています。
Dataverse Web API エンドポイントの取得
開発者リソースを表示する にある手順を使用して、アクセスできる環境の Web API エンドポイントを識別します。 https://yourorg.api.crm.dynamics.com/api/data/v9.2
のように表示されます。
アプリケーションの登録
左側のナビゲーションの Power Platform 管理センター から 管理センター を展開し、 Microsoft Entra IDを選択します。
これにより、Microsoft Entra 管理センターが開きます
アプリケーション を展開して、アプリの登録 を選択します。
新規登録 をクリックします。 これにより、アプリケーションを登録する フォームが開きます。
アプリケーションの登録 フォームで、名前 を入力します。 このクイックスタートでは、名前 シンプル SPA を使用します。
サポートされているアカウントの種類 では、デフォルトの選択は次のとおりです。
この組織ディレクトリのみに含まれるアカウント (<テナント名のみ> – シングル テナント) これを変更しないでください。リダイレクト URI (オプション) には、次のオプションを使用します。
- プラットフォームを選択: シングルページ アプリケーション (SPA)
e.g. https://example.com/auth
:http://localhost:5500/index.html
登録 をクリックします。
概要 エリアに、Webアプリケーション プロジェクトの作成 の最後のステップで必要になるため、次の値をコピーします。
- アプリケーション (クライアント) ID
- ディレクトリ (テナント) ID
API アクセス許可 を選択します。
アクセス許可の追加 をクリックします。
API アクセス権限のリクエスト フライアウトで、Dynamics CRM を選択します。
- Dynamics CRM が表示されに場合は、Dataverse を探します。 または、自分の組織が使う API タブを選択して、Dataverse を検索します。
user_impersonation の委任されたアクセス許可を選択します。
アクセス許可の追加 をクリックします。
完了したら、構成されたアクセス許可はこのようになります。
Live Server Visual Studio Code 拡張をインストールする
Live Server は、Web ページのローカル開発サーバーを簡単に起動できる Visual Studio Code 拡張機能です。
次の手順を使用して、VS Code マーケットプレースで VS Code 用の Live Server 拡張機能を見つけてインストールします。
Live Server 拡張機能をインストールしたら、これらの変更をデフォルト設定に加えます。
VS Code で歯車のアイコン を選択し、設定 を選択するか、
Ctrl+,
キーボード ショートカットを使用します。検索ウィンドウで、
liveServer.settings.host
と入力して、既定値を127.0.0.1
からlocalhost
に変更します。
Web アプリケーション プロジェクトの作成
コンピューターでフォルダーを作成します。 名前は重要ではありませんが、これらの手順では名前を
simplespa
と付けます。VS コードを開き、メニューで ファイル > フォルダーを開く を選択します。
simplespa
フォルダーを選択します。index.html
と言う名前のフォルダーで新規 HTML ファイルを作成します。 (index.htm
ではない)以下の内容を index.html ファイルにコピーします。
<html> <head> <meta charset="UTF-8"> <meta http-equiv="X-UA-Compatible" content="IE=edge"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <script> const baseUrl = "https://org.api.crm.dynamics.com"; //<= Change this const clientId = "11111111-1111-1111-1111-111111111111"; //<= Change this const tenantId = "22222222-2222-2222-2222-222222222222"; //<= Change this const redirectUrl = "http://localhost:5500/index.html"; const webAPIEndpoint = baseUrl +"/api/data/v9.2"; // Configuration object to be passed to MSAL instance on creation. const msalConfig = { auth: { clientId: clientId, // Full directory URL, in the form of https://login.microsoftonline.com/<tenant-id> authority: "https://login.microsoftonline.com/"+tenantId, redirectUri: redirectUrl, }, cache: { cacheLocation: "sessionStorage" // This configures where your cache will be stored }, system: { loggerOptions: { loggerCallback: (level, message, containsPii) => { if (containsPii) { return; } switch (level) { case msal.LogLevel.Error: console.error(message); return; case msal.LogLevel.Info: console.info(message); return; case msal.LogLevel.Verbose: console.debug(message); return; case msal.LogLevel.Warning: console.warn(message); return; } } } } }; </script> <!-- Latest version of msal-browser.js from CDN as of 2022/09 --> <script type="text/javascript" src="https://alcdn.msauth.net/browser/2.28.1/js/msal-browser.min.js"> </script> <style> body { font-family: 'Segoe UI'; } table { border-collapse: collapse; } td, th { border: 1px solid black; } #message { color: green; } </style> </head> <body> <div> <button id="loginButton" onclick="signIn()">Login</button> <button id="logoutButton" onclick="signOut()" style="display:none;">Logout</button> <button id="getAccountsButton" onclick="getAccounts(writeTable)" style="display:none;">Get Accounts</button> <div id="message"></div> <table id="accountsTable" style="display:none;"> <thead><tr><th>Name</th><th>City</th></tr></thead> <tbody id="accountsTableBody"></tbody> </table> </div> <script> const loginButton = document.getElementById("loginButton"); const logoutButton = document.getElementById("logoutButton"); const getAccountsButton = document.getElementById("getAccountsButton"); const accountsTable = document.getElementById("accountsTable"); const accountsTableBody = document.getElementById("accountsTableBody"); const message = document.getElementById("message"); // Create the main myMSALObj instance const myMSALObj = new msal.PublicClientApplication(msalConfig); let username = ""; // Sets the username. Called at the end of this script. function selectAccount() { const currentAccounts = myMSALObj.getAllAccounts(); if (currentAccounts.length === 0) { return; } else if (currentAccounts.length > 1) { // Add choose account code here console.warn("Multiple accounts detected."); } else if (currentAccounts.length === 1) { username = currentAccounts[0].username; showWelcomeMessage(username); } } // Called by the loginButton function signIn() { myMSALObj.loginPopup({ scopes: ["User.Read",baseUrl+"/user_impersonation"] //<= Includes Dataverse scope }) .then(response =>{ if (response !== null) { username = response.account.username; showWelcomeMessage(username); } else { selectAccount(); } }) .catch(error => { console.error(error); }); } // Shows greeting and enables logoutButton and getAccountsButton // Called from signIn or selectAccount functions function showWelcomeMessage(username) { message.innerHTML = `Welcome ${username}`; loginButton.style.display = "none"; logoutButton.style.display = "block"; getAccountsButton.style.display = "block"; } // Called by the logoutButton function signOut() { const logoutRequest = { account: myMSALObj.getAccountByUsername(username), postLogoutRedirectUri: msalConfig.auth.redirectUri, mainWindowRedirectUri: msalConfig.auth.redirectUri }; myMSALObj.logoutPopup(logoutRequest); } // Provides the access token for a request, opening pop-up if necessary. // Used by GetAccounts function function getTokenPopup(request) { request.account = myMSALObj.getAccountByUsername(username); return myMSALObj.acquireTokenSilent(request) .catch(error => { console.warn("Silent token acquisition fails. Acquiring token using popup"); if (error instanceof msal.InteractionRequiredAuthError) { // fallback to interaction when silent call fails return myMSALObj.acquireTokenPopup(request) .then(tokenResponse => { console.log(tokenResponse); return tokenResponse; }).catch(error => { console.error(error); }); } else { console.warn(error); } }); } // Retrieves top 10 account records from Dataverse function getAccounts(callback) { // Gets the access token getTokenPopup({ scopes: [baseUrl+"/.default"] }) .then(response => { getDataverse("accounts?$select=name,address1_city&$top=10", response.accessToken, callback); }).catch(error => { console.error(error); }); } /** * Helper function to get data from Dataverse * using the authorization bearer token scheme * callback is the writeTable function below */ function getDataverse(url, token, callback) { const headers = new Headers(); const bearer = `Bearer ${token}`; headers.append("Authorization", bearer); // Other Dataverse headers headers.append("Accept", "application/json"); headers.append("OData-MaxVersion", "4.0"); headers.append("OData-Version", "4.0"); const options = { method: "GET", headers: headers }; console.log('GET Request made to Dataverse at: ' + new Date().toString()); fetch(webAPIEndpoint+"/"+url, options) .then(response => response.json()) .then(response => callback(response)) .catch(error => console.log(error)); } // Renders the table with data from GetAccounts function writeTable(data) { data.value.forEach(function (account) { var name = account.name; var city = account.address1_city; var nameCell = document.createElement("td"); nameCell.textContent = name; var cityCell = document.createElement("td"); cityCell.textContent = city; var row = document.createElement("tr"); row.appendChild(nameCell); row.appendChild(cityCell); accountsTableBody.appendChild(row); }); accountsTable.style.display = "block"; getAccountsButton.style.display = "none"; } selectAccount(); </script> </body> </html>
注意
HTML ページの JavaScript コードは、Microsoft Graph に接続する、こちら https://github.com/Azure-Samples/ms-identity-javascript-v2 で公開されているサンプル コードを基にしています。
主な違いは、アクセス トークン を取得するときに使用されるスコープです。
ログイン ボタンに次のスコープを使用します。
// Called by the loginButton function signIn() { myMSALObj.loginPopup({ scopes: ["User.Read",baseUrl+"/user_impersonation"] //<= Includes Dataverse scope })
これらのスコープには、Microsoft Graph
User.Read
範囲だけでなく、Dataverseuser_impersonation
範囲も両方が含まれます。。 ログイン時にこれらのスコープの両方を含めることにより、最初の同意ダイアログには、アプリケーションで使用されるすべての必要なスコープが含まれます。次に、Dataverse 呼び出しに使用されるスコープを指定するときは、
/.default
または/user_impersonation
のどちらでも使用できます。// Retrieves top 10 account records from Dataverse function getAccounts(callback) { // Gets the access token getTokenPopup({ scopes: [baseUrl+"/.default"] })
/user_impersonation
範囲は、委任されたアクセス許可に対してのみ機能するため、ここではこれを使用できます。/.default
は、委任されたアクセス許可とアプリケーションのアクセス許可の両方で機能します。ログイン時に
baseUrl+"/user_impersonation"
範囲を含めない場合は、ユーザーは アカウントを取得する ボタンを初めてクリックしたときにもう一度同意する必要があります。。その他の SPA の例とチュートリアルは、こちらにあります: シングル ページ アプリケーション (SPA) のドキュメント 。
index.html ページ内で、次の構成変数を見つけ、前の手順 あなたの Dataverse Web API エンドポイントを取得する と アプリケーションを登録する で収集した情報を使用して設定します。
const baseUrl = "https://org.api.crm.dynamics.com"; //<= Change this const clientId = "11111111-1111-1111-1111-111111111111"; //<= Change this const tenantId = "22222222-2222-2222-2222-222222222222"; //<= Change this
アプリのデバッグ
Live Server Visual Studio Code 拡張機能をインストールする で Live Server 拡張機能をインストールしたため、VS Code ツールバーに次のボタンがあります: 。
ライブ移行 ボタンをクリックすると、新しいブラウザ ウィンドウが開き、
http://localhost:5500/index.html
が index.htmlページをレンダリングします。初めてアプリを実行して ログイン ボタンをクリックすると、次のような同意ダイアログが表示されます。
管理者 の場合は、組織を代表して同意する チェックボックスを使用すると、他のユーザーも リクエストされた権限 ダイアログを使用することなくアプリを実行できるようになります。
承認 をクリックしてテストを続行して、アプリが このクイック スタートの目的 の説明どおりに動作することを確認します。
トラブルシューティング
このクイック スタートでの体験は、ライブ サーバーのポート設定が既定値: 5500
であるかどうかによって異なります。 Live Server が既にインストールされていて、ポート設定を変更した場合は、デフォルト設定またはアプリ登録で設定された URL を変更する必要があります。
liveServer.settings.port
は ワークスペース に設定することもでき、ユーザー 設定を上書きしますので注意してください。
複数の Live Server インスタンスを開くと、ポート設定が 5501 以上に増加する場合があります。 これにより、認証に使用されるコールバックが壊れます。これは、ポートが http://localhost:5500/index.html
のようにアプリケーション登録に 'ハードコード' されているためです。
参照
単一ページ アプリケーション (SPI) 説明書
OAuth を使用するクロス オリジン リソース共有を使用して Dataverse の単一ページのアプリケーションへ接続する
クライアント アプリケーション作成
注意
ドキュメントの言語設定についてお聞かせください。 簡単な調査を行います。 (この調査は英語です)
この調査には約 7 分かかります。 個人データは収集されません (プライバシー ステートメント)。