Node.jsとTypeScriptでMicrosoft Graph APIを使う

  • June 6, 2020
  • d_yama
  • Azure

サーバサイドでもGraph APIを使いたい

TypeScript/JavaScriptでGraph APIを使うときは@microsoft/microsoft-graph-clientを使うのがスタンダードかと思います。こちらのnpmパッケージはクライアントサイドでもサーバサイドでも利用できるものですが、世の中にあるチュートリアルはクライアントサイドでの利用を想定したものが多い気がします。READMEのチュートリアルを見ても、「サーバサイドで使うときはAuthentication Providerを自分で作ってね」と書いてあるだけで、ちょっと寂しい状態です。

とはいえサーバサイド(Node.js)でGraph APIを使いたいケースもあると思うので、今回はNode.js(v12.18.0) + TypeScriptでの使い方を紹介します。

事前準備

サービスプリンシパルの作成

Graph APIにアクセスするためにはOAutn2.0におけるアクセストークンが必要です。今回はサーバサイドアプリケーションからアクセスするケースとなりますので、サービスプリンシパルを使ってアクセストークンを取得します。

サービスプリンシパルの作成方法はこちら

サービスプリンシパルがGraph APIにアクセスするためには明示的な許可が必要です。サービスプリンシパルやAPIの許可についてはこちらの記事が大変参考になります。

Azure AD のサービスプリンシパルを 3 つのユースケースから眺めてみた

npmパッケージのインストール

必要なパッケージはアクセストークンを取得するための@azure/ms-rest-nodeauthと、Graph APIにアクセスするための@microsoft/microsoft-graph-clientです。

yarn add @microsoft/microsoft-graph-client @azure/ms-rest-nodeauth

コード

Azure AD上の指定したグループに所属するユーザの情報を取得する、という利用シーンのサンプルです。

import { Client } from '@microsoft/microsoft-graph-client'
import { loginWithServicePrincipalSecret } from '@azure/ms-rest-nodeauth'

async function getGroupMembers() {
    // サービスプリンシパルのアプリケーションID
    const clientId = process.env.AZURE_CLIENT_ID
    // サービスプリンシパルのシークレット
    const clientSecret = process.env.AZURE_CLIENT_SECRET
    // Azure ADのテナントID
    const tenantId = process.env.AZURE_TENANT_ID
    // 検索対象のAzure AD セキュリティグループのオブジェクトID
    const groupId = process.env.AZURE_USER_GROUP_ID

    const applicationTokenCredentials = await loginWithServicePrincipalSecret(
        clientId, 
        clientSecret, 
        tenantId, 
        {tokenAudience: 'https://graph.microsoft.com'}
    )

    const getAccessTokenFunc = async (): Promise<string> => {
        const tokenResponse = await applicationTokenCredentials.getToken()
        return tokenResponse.accessToken
    }

    const graphClient = Client.initWithMiddleware({
        authProvider: {
            getAccessToken: getAccessTokenFunc
        }
    })

    const result = await graphClient
        .api(`/groups/${groupId}/members`)
        .select(['id', 'displayName'])
        .top(200)
        .get()
}

Authentication Providerとは、要はアクセストークンを返す関数を持つオブジェクトのインタフェースです。なのでGraph APIにアクセスできるアクセストークンさえ用意できれば、その実装は特に問われません。

今回はサーブスプリンシパルを使いOAuth2.0のClient Credentials Grantフローでアクセストークンを取得します。アクセストークンを管理するため、@microsoft/microsoft-graph-clientloginWithServicePrincipalSecret関数を使います。

    const applicationTokenCredentials = await loginWithServicePrincipalSecret(
        clientId, 
        clientSecret, 
        tenantId, 
        {tokenAudience: 'https://graph.microsoft.com'}
    )

オプションオブジェクトのtokenAudienceにMicrosoft GraphのリソースURIを指定する必要があります。一応、ライブラリ側でもgraphというオプションが用意されています。しかしこのオプションは古いリソースURIと結びついているのでこれを指定してもGraph APIへアクセスするためのアクセストークンは取得できないので注意してください。

extension

loginWithServicePrincipalSecret関数の戻り値であるApplicationTokenCredentialsオブジェクトは内部でアクセストークンとリフレッシュトークンをキャッシュしています。そのアクセストークンを取得するメソッドとしてgetTokenメソッドが用意されています。こちらはキャッシュしているアクセストークンが有効期限以内ならそのアクセストークンを、有効期限を過ぎている場合はリフレッシュトークンを使って新しいアクセストークンを返してくれます。今回はそのメソッドを使ってAuthentication Providerインタフェースを満たすオブジェクトを構成し、Clientオブジェクトの初期化メソッドに渡しています。

    const getAccessTokenFunc = async (): Promise<string> => {
        const tokenResponse = await applicationTokenCredentials.getToken()
        return tokenResponse.accessToken
    }

    const graphClient = Client.initWithMiddleware({
        authProvider: {
            getAccessToken: getAccessTokenFunc
        }
    })

今回は一つの関数内にすべて記述しましたが、Clientオブジェクトをキャッシュしておくことも可能です。先ほど説明したとおり、Clientオブジェクトの初期化メソッドに渡したAuthentication Providerはアクセストークンの有効期限が切れたら自動的に新しいアクセストークンを取得してくれるので、Graph APIのアクセス毎にClientオブジェクトを作り直す必要はありません。

ここまで来たら、あとはチュートリアルにあるような使い方と同じ書き方でGraph APIを操作できます。

    const result = await graphClient
        .api(`/groups/${groupId}/members`)
        .select(['id', 'displayName'])
        .top(200)
        .get()

まとめ

Node.jsからGraph APIへのアクセスする方法について、TypeScriptでの実装とあわせて紹介しました。

Profile
d_yama
元Microsoft MVP for Windows Development(2018-2020)
Sub-category : Windows Mixed Reality
Search