Nodejs環境でMicrosoft GraphAPIを使用する
NodeJS(ts-node)環境でGraphAPIを使用したデータの取得を試みる
GraphAPIを使用するには認証が必要
認証のパターンは2つ
- ユーザー込みで認証する
- アプリのみで認証する
GraphAPIにアクセスするのが、ユーザーかそれともアプリかという違い
今回はアプリのバックエンドでGraphAPIを使用したいのでアプリのみで認証する方式を採用する
認証の手順
公式ドキュメントによると
- アプリをMicrosoft Entra IDに登録
- 承認を要求
- アクセストークンを要求
- アクセストークンを使用して、Microsoft Graph を呼び出す
- [省略可能]更新トークンを使用して、期限切れのアクセストークンを更新
まあ特段変わったところはない
アプリのセットアップ
-
Microsoft Entra管理センター
-
ID > アプリケーション > アプリの登録
-
証明書とシークレット > 新しいシークレットを作成
-
次の値を確認する
- クライアントID
- テナントID
- クライアントシークレット
実装
今回のサンプルコードは以下のリポジトリに置いておく
Microsoft Graph SDKのインストール
npm install @microsoft/microsoft-graph-client
認証ツールのセットアップ
npm install @azure/msal-node dotenv axios
-
msal-node
Microsoft Authentication LibraryのNode.js向けライブラリ
MicrosoftのクラウドサービスやGraph APIへのアクセスをするための認証を行ってくれる
-
dotenv
環境変数を扱うライブラリ
-
axios
APIリクエストを行うためのHTTPクライアント
環境変数
.env.exampleをコピーして.envを作成する
CLIENT_ID =""
TENANT_ID=""
CLIENT_SECRET="YOUR SECRET VALUE"
Configuration
Graph APIを使用するための基本的な設定を行う
import { ClientCredentialRequest, Configuration } from "@azure/msal-node";
import 'dotenv/config';
const msalConfig: Configuration = {
auth: {
clientId: process.env.CLIENT_ID!,
authority: `https://login.microsoftonline.com/${process.env.TENANT_ID}`,
clientSecret: process.env.CLIENT_SECRET,
}
}
const tokenRequest: ClientCredentialRequest = {
scopes: ["https://graph.microsoft.com/.default"]
}
const apiConfig = {
uri: "https://graph.microsoft.com/v1.0"
}
export const configuration = {
msalConfig,
tokenRequest,
apiConfig
}
AccessToken取得
設定した認証情報をもとにmsalクライアントを使ってアクセストークンを取得する
import { ConfidentialClientApplication } from "@azure/msal-node";
import { configuration } from "./config";
export async function getAccessToken() {
const msalClient = new ConfidentialClientApplication(configuration.msalConfig)
try {
const authResponse = await msalClient.acquireTokenByClientCredential(configuration.tokenRequest)
return authResponse?.accessToken
} catch (error) {
console.log(error)
throw new Error("Error acquireing access token")
}
}
任意のGraph APIを使用する
import axios from 'axios';
import { getAccessToken } from './accessToken';
import { configuration } from './config';
export async function FetchUserData() {
try {
const accessToken = await getAccessToken();
const response = await axios.get(`${configuration.apiConfig.uri}/users`, {
headers: {
Authorization: `Bearer ${accessToken}`
}
});
console.log(response.data.value.length,'users');
console.log('next link is:', response.data['@odata.nextLink']);
} catch (error) {
console.error(error);
}
}
export async function FetchAllUsers(nextLink = null) {
let users: any = [];
let requestUrl = nextLink || `${configuration.apiConfig.uri}/users`;
while (requestUrl) {
const accessToken = await getAccessToken();
const response = await axios.get(requestUrl, {
headers: {
Authorization: `Bearer ${accessToken}`
}
});
users = users.concat(response.data.value);
requestUrl = response.data['@odata.nextLink'];
}
console.log(users.length,'users')
// return users;
}
今回は例としてUser一覧取得のAPIをリクエストしてみる。
リクエストテスト
import { FetchAllUsers } from "./graph-api/fetchUserData";
const main = () => {
FetchAllUsers().catch(console.error);
}
main()
npx ts-node main.ts
これでユーザーの一覧が取得できるのが確認できる
User APIのページング
FetchUserDataを叩いてみると1度に100件しかユーザーが取得できなかった。
このAPIはページングという仕様で大きすぎるデータを分割して送信している
同じレスポンス内に@odata.nextLink
プロパティがある
requestUrl = response.data['@odata.nextLink'];
これは次のページ分のユーザーデータをリクエストできるURIを示す
FetchAllUserではこの@odata.nextLink
を使って全てのユーザーを取得している
Discussion