はじめに
最近Microsoft Graph APIについて調べる機会がありました。この記事はその中から一般的な範囲についてまとめたものです。
AzureもAD(Aictive Directory)もGraph APIも詳しくないので誤りがあるかもしれません。間違いを発見されたらコメントや編集リクエストをお待ちしています。
Microsoft Graph APIとは
ざっくり言うと、Officie 365 や Azure ADなどの情報を検索、更新できるWeb API。例えば次のようなリソースにアクセス可能(許可を与えれば)
- ユーザー
- グループ
- ディレクトリ(AD)
- デバイス情報
- カレンダー
- メール
- チャット
- ファイル
- 通知
- チーム
- 場所
今回はユーザー情報にターゲットを絞る。
Graph API の種別
- (A) ユーザーの代わりにアクセスを取得(ユーザーが対話的に許可を与える)
- ブラウザなどで、利用者本人がアプリのアクセスを許可する
- GUI アプリケーションで利用
- (B) ユーザーなしでアクセスを取得(あらかじめ管理者が許可を与えれば、ユーザ操作なしで動く)
- 管理コンソールで、管理者が許可を与える
- バッチ的なアプリケーションで利用
Graph API 利用までの準備
- Azureへのサインアップ
- アプリの登録
- APIアクセス許可の付与
- 管理者の同意
- クライアントシークレットの生成
Azureへのサインアップ
- Azure にサインアップ ... 今回は12ヶ月の無料体験でスタート
- Azureポータルにログイン https://portal.azure.com
- Azure Active Directory のページに移動
- ホームからアイコンをクリック
- または、左上のメニューから選択
※この時[規定のディレクトリ - 概要]で「ロール」が「グローバル管理者」になっていることを確認。
- 「ロール」が「ユーザー」の場合は、この後でアプリにWrite権限を与えることができない(最初それでハマった)
- ロールが「ユーザー」の場合は、ログアウト→再ログインでロールが変わる(?)
アプリの登録
- Active Directoryのページで、メニューから「アプリの登録」を選択
- もし「ディレクトリに登録」か「個人アカウントに関連付ける」を聞かれたら前者を選択
- (そうしないと、この後でアプリにWrite権限を与えることができない)
- [新規登録]からアプリを登録
- 名前
- 種類(シングルテナント/マルチテナント/マルチテナント+個人)
- シングルテナント or マルチテナント を選択
- リダイレクト先のURI
- アプリの情報を確認
- アプリケーションID(client_id)
- ディレクトリID(tenant ID)
アプリ登録中の画面
アプリ登録後の表示
APIアクセス許可の付与
- アプリの概要のページから、左のメニューの[APIのアクセス許可]を選択
- [+アクセス許可の追加]をクリックし、付与したいAPIを選択
- 今回はGraph APIを使って、ユーザー情報の読み書きを行う
- [Microsoft Grap API]を選択
- [委任されたアクセス許可] ... (A) 対話的にユーザーが許可を与える
- [アプリケーションの許可] ... (B) あらかじめ管理者が許可を与え、ユーザ操作なしで動く
- ※バッチ的に動かすには(B)を選択。
- この時「個人アカウントに関連付ける」では、Write許可が与えられない様子
- その中で [User.ReadWrite.All]を選択
- [アクセス許可の追加]をクリック
管理者の同意
- [規定のディレクトリに管理者の同意を与えます]をクリック
- 先のアクセス権の追加だけでなく、管理者の同意を与えて、初めてAPIのアクセス権が有効になる
- ※ポータルにログインしているユーザのロールが「グローバル管理者」になっていないと、同意ができない
クライアントシークレットを生成
- 登録したアプリを表示し、左の[証明書とシークレット]を選択
- [新しいクライアントシークレット]をクリック
- 名前と期間を指定して生成
- 生成されたシークレットの値を確認
Graph APIを使う
ここでは「ユーザーなしでアクセスを取得」のケースを記載
アクセストークンの取得
- POSTでアクセストークンを取得
- アプリのクライアントID, テナントID, クライアントシークレットを利用
curlコマンドでPOST実行し、token.jsonに保存する例
curl -d "client_id={クライアントID}" \
-d "scope=https%3A%2F%2Fgraph.microsoft.com%2F.default" \
-d "client_secret={クライアントシークレット}" \
-d "grant_type=client_credentials" \
-H "Content-Type: application/x-www-form-urlencoded" \
-X POST https://login.microsoftonline.com/{テナントID}/oauth2/v2.0/token > token.json
取得結果(token.json)の例
{
"token_type": "Bearer",
"expires_in": 3600,
"ext_expires_in": 3600,
"access_token": "xxxxxxxxxxx......."
}
→ 以後、取得したaccess_tokenの値を利用する
トークンを使って情報取得
GETでユーザーの情報を取得
- 先ほどのアクセストークンの値と、取得するユーザーのオブジェクトIDを指定する
curl -H "Authorization: Bearer {access_tokenの値}" \
https://graph.microsoft.com/v1.0/users/{取得するユーザーのオブジェクトID} > user.json
{
"@odata.context": "https://graph.microsoft.com/v1.0/$metadata#users/$entity",
"businessPhones": [],
"displayName": "Joe Test",
"givenName": null,
"jobTitle": null,
"mail": null,
"mobilePhone": null,
"officeLocation": null,
"preferredLanguage": null,
"surname": null,
"userPrincipalName": "joe@xxxxxxxxx.onmicrosoft.com",
"id": "xxxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx"
}
トークンを使って情報の更新
PATCHで部分的に属性を更新可能
- アクセストークンの値と、更新するユーザーのオブジェクトIDを指定する
- ボディ部分にjsonで更新する属性、値を指定する
curl --request PATCH https://graph.microsoft.com/v1.0/users/{更新するユーザーのオブジェクトID} \
-H "Authorization: Bearer {access_tokenの値}" \
-H "Content-Type: application/json" \
-d '{ "jobTitle": "test user"}'
APIのアクセス許可が適切に追加できていれば、指定した属性のみ更新される
Graph API「ユーザーの代わりにアクセスを取得する」場合
ユーザによる許可
- ブラウザでURLを開く
-
https://login.microsoftonline.com/common/oauth2/v2.0/authorize?client_id={クライアントID}&response_type=code&redirect_uri={リダイレクトURIのエンコード表記}&response_mode=query&scope=offline_access%20user.read%20mail.read&state={任意の数値}
- scope=でアクセス範囲を指定(この例では、user情報の取得、mailの取得)
- アクセスの許可を求める画面が出るので、許可をあたえる
-
https://login.microsoftonline.com/common/oauth2/v2.0/authorize?client_id={クライアントID}&response_type=code&redirect_uri={リダイレクトURIのエンコード表記}&response_mode=query&scope=offline_access%20user.read%20mail.read&state={任意の数値}
- リダイレクト先に飛ぶ
- URLにstateが含まれるので、先ほど指定した数値と一致することを確認
- URLにcodeが含まれるので、それを取り出す
- リダイレクト例)
アクセストークンの取得
POSTでアクセストークンを取得
curl -d "client_id={クライアントID}}" \
-d "scope=user.read mail.read" \
-d "code={ユーザーによる許可で得られたcode}" \
-d "redirect_uri={リダイレクトURIのエンコード表記}" \
-d "grant_type=authorization_code" \
-d "client_secret={クライアントシークレット}" \
-H "Content-Type: application/x-www-form-urlencoded" \
-X POST https://login.microsoftonline.com/common/oauth2/v2.0/token
{
"token_type": "Bearer",
"scope": "User.Read Mail.Read",
"expires_in": 3600,
"ext_expires_in": 3600,
"access_token": "xxxxxxxxxxxxxx...",
"refresh_token": "xxxxxxxxxxxxxxx..."
}
情報の取得
curl -H "Authorization: Bearer {アクセストークンの値}" https://graph.microsoft.com/v1.0/me
curl -H "Authorization: Bearer {アクセストークンの値}" \
https://graph.microsoft.com/v1.0/users/{ユーザーID}
その他
- 有効期間内なら refresh_token を使ってアクセストークンを再取得できる
- 情報の更新は「権限不足」で失敗
- 試したのが個人向けアプリだったため? 原因は未追求
おまけ
POSTとPUTとPATCH
今回初めてPATCHメソッドの存在を知った。
- POST ... リソースの新規追加
- PUT ... リソースの更新(置換)
- PATCH ... リソースの部分更新(アップデート)
カスタム属性の利用
ユーザーの情報としてカスタム属性を利用する場合には、「Azure AD B2C ディレクトリ」を利用する必要がある。 (無料体験のライセンスでは利用できない)
Graph API beta の利用(2019.11.16追記)
ここまでは v1.0 を利用していたが、beta ではより多くの属性が扱えることが判明したので使ってみた。
- アクセストークンの取得まではv1.0と同じ
betaでのユーザー情報の取得
v1.0と同様に、GETでユーザーの情報を取得。URLの「v1.0」の箇所を「beta」にするのが違い。
- 先ほどのアクセストークンの値と、取得するユーザーのオブジェクトIDを指定する
curl -H "Authorization: Bearer {access_tokenの値}" \
https://graph.microsoft.com/beta/users/{取得するユーザーのオブジェクトID} > user_beta.json
{
"@odata.context": "https://graph.microsoft.com/beta/$metadata#users/$entity",
"id": "xxxxxxxxxxxxxxxxxxxxxxxx",
"deletedDateTime": null,
"accountEnabled": true,
"ageGroup": null,
"businessPhones": [],
"city": null,
"createdDateTime": "2019-11-03T09:34:59Z",
"creationType": null,
"companyName": "GOOD COMPANY",
"consentProvidedForMinor": null,
"country": null,
"department": null,
"displayName": "Joe Test",
"employeeId": null,
"faxNumber": null,
"givenName": null,
"imAddresses": [],
"isResourceAccount": null,
"jobTitle": "test user",
"legalAgeGroupClassification": null,
"mail": null,
"mailNickname": "joe",
"mobilePhone": null,
"onPremisesDistinguishedName": null,
"officeLocation": null,
"onPremisesDomainName": null,
"onPremisesImmutableId": null,
"onPremisesLastSyncDateTime": null,
"onPremisesSecurityIdentifier": null,
"onPremisesSamAccountName": null,
"onPremisesSyncEnabled": null,
"onPremisesUserPrincipalName": null,
"otherMails": [],
"passwordPolicies": null,
"postalCode": null,
"preferredDataLocation": null,
"preferredLanguage": null,
"proxyAddresses": [],
"refreshTokensValidFromDateTime": "2019-11-03T09:34:58Z",
"showInAddressList": null,
"signInSessionsValidFromDateTime": "2019-11-03T09:34:58Z",
"state": null,
"streetAddress": null,
"surname": null,
"usageLocation": null,
"userPrincipalName": "joe@xxxxxxx.onmicrosoft.com",
"externalUserState": null,
"externalUserStateChangeDateTime": null,
"userType": "Member",
"assignedLicenses": [],
"assignedPlans": [],
"deviceKeys": [],
"identities": [
{
"signInType": "userPrincipalName",
"issuer": "xxxxxxxxx.onmicrosoft.com",
"issuerAssignedId": "joe@mxxxxxx.onmicrosoft.com"
}
],
"onPremisesExtensionAttributes": {
"extensionAttribute1": null,
"extensionAttribute2": null,
"extensionAttribute3": null,
"extensionAttribute4": null,
"extensionAttribute5": null,
"extensionAttribute6": null,
"extensionAttribute7": null,
"extensionAttribute8": null,
"extensionAttribute9": null,
"extensionAttribute10": null,
"extensionAttribute11": null,
"extensionAttribute12": null,
"extensionAttribute13": null,
"extensionAttribute14": null,
"extensionAttribute15": null
},
"onPremisesProvisioningErrors": [],
"passwordProfile": {
"password": null,
"forceChangePasswordNextSignIn": true,
"forceChangePasswordNextSignInWithMfa": false
},
"provisionedPlans": []
}
v1.0と比べて、取得できる属性がかなり多い。
{
"@odata.context": "https://graph.microsoft.com/v1.0/$metadata#users/$entity",
"businessPhones": [],
"displayName": "Joe Test",
"givenName": null,
"jobTitle": null,
"mail": null,
"mobilePhone": null,
"officeLocation": null,
"preferredLanguage": null,
"surname": null,
"userPrincipalName": "joe@xxxxxxxxx.onmicrosoft.com",
"id": "xxxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx"
}
betaでのユーザー情報の一部更新
betaで増えた属性の更新もURLの「v1.0」の箇所を「beta」にするとできる。
curl --request PATCH https://graph.microsoft.com/beta/users/{更新するユーザーのオブジェクトID} \
-H "Authorization: Bearer {access_tokenの値}" \
-H "Content-Type: application/json" \
-d '{ "jobTitle": "beta test user", "onPremisesExtensionAttributes": {"extensionAttribute2": "extra attribute test"} }'
更新後に再度ユーザー情報を取得すれば、指定した属性が更新されている。カスタム属性を使わなくても、extensionAttribute1 〜 extensionAttribute15 が利用できるので情報を追加で保持するのに使えそう。