📊

Microsoft Graph で会議室の予定表を取得する

2022/01/01に公開

元ネタはこちら。

https://social.msdn.microsoft.com/Forums/ja-jp/2c124c80-585b-426f-94ed-68131049e954?WT.mc_id=M365-MVP-5002941

結構よくある話で OAuth のハマりどころの最たるものが「アプリケーションにアクセス許可を与えたのにデータが取れない」となることなのです。
上記の「Calendars.ReadWrite.Shared を付けたのに会議室の情報が取れない」というのは、アプリを使用するユーザーに権限がないからで、これはむしろ正しい動作な訳です。委任されたアクセス許可 という名前が表す通りですね。
公式ドキュメントにも説明があります。

https://developer.microsoft.com/ja-jp/graph/docs/concepts/permissions_reference?WT.mc_id=M365-MVP-5002941

委任されたアクセス許可の場合、アプリの有効なアクセス許可は、アプリに付与されている委任されたアクセス許可 (同意によって付与) と現在サインインしているユーザーの特権が重なる範囲に収まる最小権限になります。

じゃあどうするのというと、代わりに アプリケーションのアクセス許可 を使うことになります。
ただし、こちらは、バックグラウンドで動作するアプリケーションを想定しているので、気を付けなければなりません。たとえば、アクセス許可に Calendars.Read を付けると、特定のアカウントだけではなく、すべてのアカウントの予定表が見られるようになります。これはセキュリティ上のリスクが高くなるので、フロントエンドで動作するアプリでの使用は特に気を付けなければなりません。

前置きが長くなったのでサンプル コードです。今回は MSAL を使っています。

using Microsoft.Identity.Client;
using System;
using System.Net.Http;
using System.Net.Http.Headers;
using System.Threading.Tasks;

namespace ConsoleApplication1
{

    public static class Program
    {

        private static readonly string TenantId = "{{tenant-id}}";
        private static readonly string RedirectUrl = "{{redirect-url}}";
        private static readonly string ClientId = "{{client-id}}";
        private static readonly string ClientSecret = "{{client-secret}}";

        private static readonly string Authority = $"https://login.microsoftonline.com/{TenantId}/v2.0";
        private static readonly string ResourceId = "https://graph.microsoft.com/.default";
        private static readonly string RequestUrl = "https://graph.microsoft.com/v1.0/users/{0}/calendarView?StartDateTime={1:s}&EndDateTime={2:s}";

        private static void Main(string[] args)
        {
            var userId = "{{resource-id}}";
            var startDateTime = DateTime.Today;
            var endDateTime = DateTime.Today.AddDays(1);
            GetCalendarAsync(userId, startDateTime, endDateTime).GetAwaiter().GetResult();
        }

        private static async Task GetCalendarAsync(string userId, DateTime startDateTime, DateTime endDateTime)
        {
            var oauthClient = new ConfidentialClientApplication(ClientId, Authority, RedirectUrl, new ClientCredential(ClientSecret), null, new TokenCache());
            var oauthResult = await oauthClient.AcquireTokenForClientAsync(new[] { ResourceId });
            var httpClient = new HttpClient();
            httpClient.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", oauthResult.AccessToken);
            httpClient.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));
            var responseMessage = await httpClient.GetAsync(string.Format(RequestUrl, userId, startDateTime, endDateTime));
            var responseContent = await responseMessage.Content.ReadAsStringAsync();
        }

    }

}

ちなみに、findMeetingTimesアプリケーションのアクセス許可 では動作しないので、こっちを使いたい場合はメールボックスにアクセス許可を付けるしかないですね。

Discussion