SharePoint Framework で Microsoft Graph を実行する
SharePoint Framework のクライアント側 Web パーツまたは拡張機能から、Azure Active Directory (Azure AD) と Open Authorization (OAuth 2.0) でセキュリティ保護された REST API を実行することが、エンタープライズレベルの一般的なビジネス シナリオです。
v1.4.1で導入された SharePoint Framework を使用して、Microsoft Graph REST API、または Azure AD に登録されているその他の REST API を使用できます。
この記事では、Microsoft Graph API とカスタムのアクセス許可セットを使用する SharePoint Framework ソリューションを作成する方法を学習します。 このテクノロジの概念については、「SharePoint Framework ソリューションでの Azure AD で保護された API への接続」を参照してください。
重要
ネイティブ GraphHttpClient を使用するか、Microsoft ID platfomr 認証ライブラリの暗黙的な OAuth フローを直接使用して、v1.4.1 より前のバージョンのSharePoint Frameworkで Microsoft Graph APIを使用できます。 しかし、前者のアプローチは、事前定義された一連のアクセス許可にバインドされているため、いくらかの制限があります。一方、後者は開発の観点からすると複雑です。 OAuth の暗黙的フローの実装方法については、「Azure Active Directory でセキュリティ保護された API への接続」を参照してください。
ソリューションの概要
この記事の手順では、次のスクリーンショットに示すように、現在のテナント内のユーザーを検索できるクライアント側の Web パーツを作成する方法を示します。 検索は Microsoft Graph に基づいており、少なくとも User.ReadBasic.All アクセス許可が必要です。
クライアント側の Web パーツでは、ユーザーをその名前に基づいて検索すると、DetailsList Office UI Fabric コンポーネントによって、一致するユーザーが表示されます。 Web パーツのプロパティ ウィンドウには、Microsoft Graph へのアクセス方法を選択するオプションがあります。 SharePoint Framework v.1.4.1 以降のバージョンで、ネイティブのグラフ クライアント (MSGraphClient)、または任意の Azure AD でセキュリティ保護された REST API (AadHttpClient) へのアクセスに使用する低レベル タイプのどちらかを使用して、Microsoft Graph にアクセスすることができます。
注:
このソリューションのソース コードを入手するには、api-scopes GitHub リポジトリを参照してください。
SharePoint Framework ソリューションの作成方法を既に理解している場合は、「API アクセス許可要求を構成する」に進むことができます。
最初のソリューションの作成
SharePoint Framework ジェネレーターの以前のバージョンを使用している場合は、これを v1.4.1 以降に更新する必要があります。 それを行うには、次のコマンドを実行して、最新バージョンのパッケージをグローバルにインストールします。
npm install -g @microsoft/generator-sharepoint
次に、新しい SharePoint Framework ソリューションを作成します。
ファイル システムにフォルダーを作成します。 ソリューションのソース コードを格納し、現在のパスをこのフォルダーに移動します。
Yeoman ジェネレーターを実行して新しいソリューションをスキャフォールディングします
yo @microsoft/sharepoint
プロンプトが表示されたら、以下の値を入力します (以下で省略されたすべてのプロンプトに対して既定のオプションを選択します):
- ソリューション名は何ですか? spfx-api-scopes-tutorial
- どのベースライン パッケージをコンポーネントのターゲットにしたいですか? SharePoint Online のみ (最新)
- どの種類のクライアント側コンポーネントを作成しますか? Web パーツ
- Web パーツ名は何ですか? GraphConsumer
- どのフレームワークを使用しますか? React
Visual Studio Code (または必要なコード エディター) を現在のフォルダーのコンテキスト内で開始する。
code .
基本的な Web パーツ要素の構成
次に、クライアント側の Web パーツの初期の要素を構成します。
カスタム プロパティの構成
ソリューションの src/webparts/graphConsumer/components フォルダー内に新しいソース コード ファイルを作成します。
新しいファイル ClientMode.ts を呼び出し、これを使用して TypeScript
enum
と、Web パーツのClientMode
プロパティに使用できるオプションを宣言します。export enum ClientMode { aad, graph, }
ソリューションの ./src/webparts/graphConsumer フォルダーにある GraphConsumerWebPart.ts ファイルを開きます。
ClientMode 型の値を受け入れるように、
IGraphConsumerWebPartProps
インターフェイスの定義を変更します。export interface IGraphConsumerWebPartProps { clientMode: ClientMode; }
クライアント側の Web パーツの
getPropertyPaneConfiguration()
メソッドを更新し、プロパティ ウィンドウでの選択肢の選択を可能にします。 以下の例は、メソッドの新しい実装を示します。protected getPropertyPaneConfiguration(): IPropertyPaneConfiguration { return { pages: [ { header: { description: strings.PropertyPaneDescription }, groups: [ { groupName: strings.BasicGroupName, groupFields: [ PropertyPaneChoiceGroup('clientMode', { label: strings.ClientModeLabel, options: [ { key: ClientMode.aad, text: "AadHttpClient"}, { key: ClientMode.graph, text: "MSGraphClient"}, ] }), ] } ] } ] }; }
クライアント側の Web パーツの
render()
メソッドを更新して、レンダリングのための React コンポーネントのインスタンスを正しい構成で作成するようにします。 以下のコードは、更新メソッドの定義を示しています。public render(): void { const element: React.ReactElement<IGraphConsumerProps > = React.createElement( GraphConsumer, { clientMode: this.properties.clientMode, context: this.context, } ); ReactDom.render(element, this.domElement); }
このコードを機能させるには、次の例に示すように、GraphConsumerWebPart.ts ファイルの先頭にいくつかの import ステートメントを追加する必要があります。
PropertyPaneChoiceGroup
コントロールのインポート、およびClientMode
列挙型のインポートに注目してください。import * as React from "react"; import * as ReactDom from "react-dom"; import { Version } from "@microsoft/sp-core-library"; import { BaseClientSideWebPart, IPropertyPaneConfiguration, PropertyPaneChoiceGroup, } from "@microsoft/sp-webpart-base"; import * as strings from "GraphConsumerWebPartStrings"; import GraphConsumer from "./components/GraphConsumer"; import { IGraphConsumerProps } from "./components/IGraphConsumerProps"; import { ClientMode } from "./components/ClientMode";
リソース文字列を更新する
ソリューションをコンパイルするには、ソリューションの ./src/webparts/graphConsumer/loc フォルダー内の mystrings.d.ts ファイルを更新する必要があります。
リソース文字列を定義するインターフェイスを次のコードで書き換えます。
declare interface IGraphConsumerWebPartStrings { PropertyPaneDescription: string; BasicGroupName: string; ClientModeLabel: string; SearchFor: string; SearchForValidationErrorMessage: string; }
同じフォルダー内の en-us.js ファイルを更新して、新しく作成したリソース文字列に正しい値を設定します。
define([], function () { return { PropertyPaneDescription: "Description", BasicGroupName: "Group Name", ClientModeLabel: "Client Mode", SearchFor: "Search for", SearchForValidationErrorMessage: "Invalid value for 'Search for' field", }; });
クライアント側の Web パーツのスタイルを更新する
SCSS スタイル ファイルを更新する必要もあります。
ソリューションの ./src/webparts/graphConsumer/components フォルダー内の GraphConsumer.module.scss を開きます。 .title
クラスの直後に次のスタイル クラスを追加します:
.form {
@include ms-font-l;
@include ms-fontColor-white;
}
label {
@include ms-fontColor-white;
}
Web パーツをレンダリングする React コンポーネントを更新する
ここで、ソリューションの ./src/webparts/graphConsumer/components フォルダー内の GraphConsumer React コンポーネントを更新できます。
Web パーツの実装に必要なカスタム プロパティを受け入れるために、IGraphConsumerProps.ts ファイルを更新します。 更新された IGraphConsumerProps.ts ファイルの内容を次の例で示します。
ClientMode
列挙型定義のインポート、およびWebPartContext
型のインポートに注目してください。 これは後で使用します。import { WebPartContext } from "@microsoft/sp-webpart-base"; import { ClientMode } from "./ClientMode"; export interface IGraphConsumerProps { clientMode: ClientMode; context: WebPartContext; }
React コンポーネントの状態を保持する新しいインターフェイスを作成します。 ./src/webparts/graphConsumer/components フォルダーで新しいファイルを作成し、その名前を IGraphConsumerState.ts とします。 次にインターフェイスの定義を示します。
import { IUserItem } from "./IUserItem"; export interface IGraphConsumerState { users: Array<IUserItem>; searchFor: string; }
IUserItem
インターフェイスを (./src/webparts/graphConsumer/components フォルダーに保存されている IUserItem.ts というファイル内で) 定義します。 そのインターフェイスは、状態ファイルにインポートされます。 そのインターフェイスは、現在のテナントから取得され、UI のDetailsList
にバインドされるユーザーのアウトラインの定義に使用されます。export interface IUserItem { displayName: string; mail: string; userPrincipalName: string; }
GraphConsumer.tsx ファイルを更新します。 まず、事前に定義した種類をインポートする import ステートメントをいくつか追加します。
IGraphConsumerProps
、IGraphConsumerState
、ClientMode
、およびIUserItem
のインポートに注目してください。 React コンポーネントの UI のレンダリングに使用される Office UI Fabric コンポーネントのインポートもいくつかあります。import * as strings from "GraphConsumerWebPartStrings"; import { BaseButton, Button, CheckboxVisibility, DetailsList, DetailsListLayoutMode, PrimaryButton, SelectionMode, TextField, } from "office-ui-fabric-react"; import * as React from "react"; import { AadHttpClient, MSGraphClient } from "@microsoft/sp-http"; import { escape } from "@microsoft/sp-lodash-subset"; import { ClientMode } from "./ClientMode"; import styles from "./GraphConsumer.module.scss"; import { IGraphConsumerProps } from "./IGraphConsumerProps"; import { IGraphConsumerState } from "./IGraphConsumerState"; import { IUserItem } from "./IUserItem";
インポートの後、Office UI Fabric の
DetailsList
コンポーネントのための列のアウトラインを定義します。// Configure the columns for the DetailsList component let _usersListColumns = [ { key: "displayName", name: "Display name", fieldName: "displayName", minWidth: 50, maxWidth: 100, isResizable: true, }, { key: "mail", name: "Mail", fieldName: "mail", minWidth: 50, maxWidth: 100, isResizable: true, }, { key: "userPrincipalName", name: "User Principal Name", fieldName: "userPrincipalName", minWidth: 100, maxWidth: 200, isResizable: true, }, ];
React コンポーネントの
render()
メソッドで分かるように、この配列はDetailsList
コンポーネントの設定で使用されます。このコンポーネントを次のコードと置き換えます。
public render(): React.ReactElement<IGraphConsumerProps> { return ( <div className={ styles.graphConsumer }> <div className={ styles.container }> <div className={ styles.row }> <div className={ styles.column }> <span className={ styles.title }>Search for a user!</span> <p className={ styles.form }> <TextField label={ strings.SearchFor } required={ true } onChange={ this._onSearchForChanged } onGetErrorMessage={ this._getSearchForErrorMessage } value={ this.state.searchFor } /> </p> <p className={ styles.form }> <PrimaryButton text='Search' title='Search' onClick={ this._search } /> </p> { (this.state.users != null && this.state.users.length > 0) ? <p className={ styles.form }> <DetailsList items={ this.state.users } columns={ _usersListColumns } setKey='set' checkboxVisibility={ CheckboxVisibility.hidden } selectionMode={ SelectionMode.none } layoutMode={ DetailsListLayoutMode.fixedColumns } compact={ true } /> </p> : null } </div> </div> </div> </div> ); }
次の例に示すように、React コンポーネント タイプの宣言を更新してコンストラクターを追加します。
export default class GraphConsumer extends React.Component<IGraphConsumerProps, IGraphConsumerState> { constructor(props: IGraphConsumerProps, state: IGraphConsumerState) { super(props); // Initialize the state of the component this.state = { users: [], searchFor: "" }; }
検索条件を収集する
TextField
コンポーネントのための評価ルールと処理イベントがいくつかあります。 メソッドの実装を次に示します。これら 2 つのメソッドを
GraphConsumer
クラスの最後に追加します:private _onSearchForChanged = (event: React.FormEvent<HTMLInputElement | HTMLTextAreaElement>, newValue?: string): void => { // Update the component state accordingly to the current user's input this.setState({ searchFor: newValue, }); } private _getSearchForErrorMessage = (value: string): string => { // The search for text cannot contain spaces return (value == null || value.length == 0 || value.indexOf(" ") < 0) ? '' : `${strings.SearchForValidationErrorMessage}`; }
PrimaryButton
は\_search()
関数を実行し、これによって Microsoft Graph を実行するために使用するクライアントのテクノロジが決定されます。 このメソッドをGraphConsumer
クラスの最後に追加します:private _search = (event: React.MouseEvent<HTMLAnchorElement | HTMLButtonElement | HTMLDivElement | BaseButton | Button, MouseEvent>) : void => { console.log(this.props.clientMode); // Based on the clientMode value search users switch (this.props.clientMode) { case ClientMode.aad: this._searchWithAad(); break; case ClientMode.graph: this._searchWithGraph(); break; } }
コンポーネントの状態の users
プロパティにアイテムがある場合、DetailsList
コンポーネント インスタンスが render()
メソッドにレンダリングされます。
API アクセス許可要求の構成
Microsoft Graph またはその他のサード パーティ製 REST API を実行するには、OAuth の観点からのアクセス許可要件をソリューションのマニフェスト内で明示的に宣言する必要があります。
SharePoint Framework v.1.4.1 以降では、それはソリューションの config フォルダー内で、package-solution.json に webApiPermissionRequests
プロパティを構成することによって行えます。 現在のソリューションについて、そのファイルの抜粋を次の例に示します。
webApiPermissionRequests
プロパティの宣言をコピーします。
{
"$schema": "https://developer.microsoft.com/json-schemas/spfx-build/package-solution.schema.json",
"solution": {
"name": "spfx-api-scopes-tutorial-client-side-solution",
"id": "841cd609-d821-468d-a6e4-2d207b966cd8",
"version": "1.0.0.0",
"includeClientSideAssets": true,
"skipFeatureDeployment": true,
"webApiPermissionRequests": [
{
"resource": "Microsoft Graph",
"scope": "User.ReadBasic.All"
}
]
},
"paths": {
"zippedPackage": "solution/spfx-api-scopes-tutorial.sppkg"
}
}
webApiPermissionRequest
アイテムの配列である webApiPermissionRequests
に注目してください。 各アイテムはアクセス許可要求の resource
と scope
を定義します。
resource
は、アクセス許可要求を構成するリソースの名前、または ObjectId (Azure AD 内) のいずれかです。 Microsoft Graph の場合、名前は Microsoft Graph です。 ObjectId は一意ではなく、テナントごとに異なります。
scope
はアクセス許可の名前、またはそのアクセス許可の一意の ID のいずれかです。 アクセス許可の名前は、API のドキュメントから入手できます。 アクセス許可の ID は、API のマニフェスト ファイルから入手できます。
注:
Microsoft Graph で利用可能なアクセス許可の一覧については、「Microsoft Graph のアクセス許可のリファレンス」を参照してください。
既定のサービス プリンシパルでは、Microsoft Graph への明示的なアクセス許可は付与されていません。 ただし、Microsoft Graph のアクセス トークンを要求すると、ユーザーに関する情報を読み取るために使用できる user_impersonation
アクセス許可 (User.Read.All) を持つトークンを取得します。 テナント管理者が付与する追加のアクセス許可を要求できます。 詳細については、「SharePoint Framework ソリューションでの Azure AD で保護された API への接続」を参照してください。
User.ReadBasic.All 権限は、ユーザーを検索してdisplayName
、mail
、およびuserPrincipalName
を取得するのに十分です。
ソリューションをパッケージ化して展開するとき、ユーザー (または管理者) は、要求されるソリューションに対するアクセス許可を付与する必要があります。 詳細については、「ソリューションの展開とアクセス許可の付与」を参照してください。
Microsoft Graph の実行
これで、Microsoft Graph を実行するメソッドを実装できるようになりました。 次の 2 つの方法があります。
- AadHttpClient クライアント オブジェクトを使用する
- MSGraphClient クライアント オブジェクトを使用する
AadHttpClient クライアント オブジェクトは、REST API の実行に便利です。 これは Microsoft Graph、または他のサード パーティ (またはファースト パーティ) の REST API の実行に使用できます。
MSGraphClient クライアント オブジェクトは、Microsoft Graph のみを実行できます。 これは内部的に AadHttpClient クライアント オブジェクトを使用し、Microsoft Graph SDK の fluent 構文をサポートしています。
AadHttpClient の使用
AadHttpClient クライアント オブジェクトを使用して REST API を使用するには、context.aadHttpClientFactory.getClient()
メソッドを呼び出し、ターゲット サービスの URI を指定して、AadHttpClient
型の新しいインスタンスを作成します。
作成したオブジェクトによって、次の要求を行うメソッドが提供されます。
get()
: HTTP GET 要求を行うpost()
: HTTP POST 要求を行うfetch()
: 指定されたHttpClientConfiguration
およびIHttpClientOptions
引数に基づき、その他のあらゆる種類の HTTP 要求を行う。
これらすべてのメソッドでは、JavaScript/TypeScript の非同期開発モデルをサポートしているため、それを見込んで結果を処理することができます。
次の例は、サンプル ソリューションの \_searchWithAad()
メソッドを示しています。
private _searchWithAad = (): void => {
// Log the current operation
console.log("Using _searchWithAad() method");
// Using Graph here, but any 1st or 3rd party REST API that requires Azure AD auth can be used here.
this.props.context.aadHttpClientFactory
.getClient("https://graph.microsoft.com")
.then((client: AadHttpClient) => {
// Search for the users with givenName, surname, or displayName equal to the searchFor value
return client
.get(
`https://graph.microsoft.com/v1.0/users?$select=displayName,mail,userPrincipalName&$filter=(givenName%20eq%20'${escape(this.state.searchFor)}')%20or%20(surname%20eq%20'${escape(this.state.searchFor)}')%20or%20(displayName%20eq%20'${escape(this.state.searchFor)}')`,
AadHttpClient.configurations.v1
);
})
.then(response => {
return response.json();
})
.then(json => {
// Prepare the output array
var users: Array<IUserItem> = new Array<IUserItem>();
// Log the result in the console for testing purposes
console.log(json);
// Map the JSON response to the output array
json.value.map((item: any) => {
users.push( {
displayName: item.displayName,
mail: item.mail,
userPrincipalName: item.userPrincipalName,
});
});
// Update the component state accordingly to the result
this.setState(
{
users: users,
}
);
})
.catch(error => {
console.error(error);
});
}
get()
メソッドは、入力引数として OData 要求の URL を取得します。 要求が成功すると、応答とともに JSON オブジェクトが返されます。
MSGraphClient の使用
Microsoft Graph をターゲットにしている場合は、さらに fluent 構文が提供される MSGraphClient クライアント オブジェクトを使用できます。
次の例は、サンプル ソリューションの _searchWithGraph()
メソッドの実装を示しています。
private _searchWithGraph = () : void => {
// Log the current operation
console.log("Using _searchWithGraph() method");
this.props.context.msGraphClientFactory
.getClient()
.then((client: MSGraphClient) => {
// From https://github.com/microsoftgraph/msgraph-sdk-javascript sample
client
.api("users")
.version("v1.0")
.select("displayName,mail,userPrincipalName")
.filter(`(givenName eq '${escape(this.state.searchFor)}') or (surname eq '${escape(this.state.searchFor)}') or (displayName eq '${escape(this.state.searchFor)}')`)
.get((err, res) => {
if (err) {
console.error(err);
return;
}
// Prepare the output array
var users: Array<IUserItem> = new Array<IUserItem>();
// Map the JSON response to the output array
res.value.map((item: any) => {
users.push( {
displayName: item.displayName,
mail: item.mail,
userPrincipalName: item.userPrincipalName,
});
});
// Update the component state accordingly to the result
this.setState(
{
users: users,
}
);
});
});
}
context.msGraphClientFactory.getClient()
メソッドを呼び出すことで、MSGraphClient
型のインスタンスを取得します。
次に、Microsoft Graph SDK の fluent API を使用して、ターゲットの Microsoft Graph エンドポイントに対して実行される OData クエリを定義します。
その結果は JSON 応答として返されるため、デコードし、型指定された結果にマッピングする必要があります。
注:
Microsoft Graph TypeScript 型を使用することにより、完全に型指定されたアプローチを使用できます。
ソリューションの展開とアクセス許可の付与
これで、ソリューションを構築、バンドル、パッケージ化、および展開する準備が整いました。
gulp コマンドを実行して、ソリューションが正しくビルドされていることを確認します。
gulp build
次のコマンドを実行して、ソリューションをバンドルし、パッケージ化します。
gulp bundle gulp package-solution
ターゲット テナントのアプリ カタログを参照し、ソリューション パッケージをアップロードします。 ソリューション パッケージは、使用しているソリューションの sharepoint/solution フォルダーにあります。 ソリューション パッケージは .sppkg ファイルです。 ソリューション パッケージをアップロードした後、アプリ カタログによって、次のスクリーンショットに示すようなダイアログ ボックスが表示されます。
ソリューション パッケージにアクセス許可の承認が必要であることを示すメッセージが画面の下部に表示されます。 これは、package-solution.json ファイル内の
webApiPermissionRequests
プロパティによるものです。最新の SharePoint Online 管理センターの左側のクイック起動メニューの [詳細設定] で、[API アクセス] メニュー項目を選択します。 次のようなページが表示されます。
このページを使用すると、自分で (または SharePoint Online テナントの他の管理者が) 保留中のアクセス許可要求を承認または拒否することができます。 どのソリューション パッケージでどのアクセス許可が要求されているかは確認できません。アクセス許可は、テナント レベルで一意のアプリケーションに対して定義されているためです。
注:
テナント レベルのアクセス許可範囲が内部的に機能する方法の詳細は、「関連項目」のセクションの記事を参照してください。
ソリューションの package-solution.json ファイルで要求したアクセス許可を選択し、[アクセスを承認または拒否する] を選択し、[承認] を選択します。 次のスクリーンショットは、管理 UI のパネルを示しています。
警告
アクセス許可 ([HTTP]:400 - [CorrelationId]
) を承認しようとしたときに予期しない例外が発生した場合は、このチュートリアルの前半で指示されたように、Microsoft Graph
ではなく Microsoft.Azure.AgregatorService
の値を使用するように package-solution.json の resource
属性を更新してください。 既存の要求を拒否し、アプリ カタログのソリューション パッケージを更新値で更新します。
ソリューションのテスト
次の gulp コマンドを使用してソリューションを実行します。
gulp serve --nobrowser
ブラウザーを開き、次の URL に移動し、SharePoint Framework ワークベンチのページに移動します。
https://<your-tenant>.sharepoint.com/_layouts/15/Workbench.aspx
クライアント側の Web パーツ GraphConsumer を追加し、[クライアント モード] を構成してユーザーを検索します。
最初の要求を行うと、ポップアップ ウィンドウが表示され、非表示になります。 これは ADAL JS で使用されるサインイン ウィンドウであり、OAuth 暗黙的フローを使用して Azure AD からアクセス トークンを取得するために、SharePoint Framework によって内部的に使用されます。
これで完了です。 これで、Azure AD で保護された REST API を使用するエンタープライズ レベルのソリューションをビルドできるようになりました。