Exchange Online の EWS に OAuth を使用して接続する

2018/06/24
最新の情報に合わせて書き直しました。

Exchange Online に EWS で接続する際、OAuth 認証を使用することができるので、手順をまとめてみました。今回は開発言語は C# を使用しています。また、Web アプリケーションではなくデスクトップ アプリケーションを想定しています。いくつか参考になる情報はインターネット上で見つかりますが、Azure も Exchange Online も常に変化しているので、普遍的な内容ではないのでご注意ください。

アプリケーションの登録

  1. 以下の URL にアクセスして、Azure Active Directory 管理センターにサインインします。
    https://aad.portal.azure.com/
  2. 左ペインより [Azure Active Directory] をクリックします。
  3. [アプリの登録] – [新しいアプリケーションの登録] をクリックします。
  4. 以下のように設定し、[作成] をクリックします。
    名前 : 任意のアプリケーションの名前を指定します (例 : MyEwsDemoApp02)
    アプリケーションの種類 : ネイティブ
    リダイレクト URI : 任意の URI (アクセス可能である必要はありません。例 : https://localhost/MyEwsDemoApp02)
  5. [アプリケーション ID] の値を控えておきます。
  6. [設定] – [必要なアクセス許可] – [追加] – [API を選択します] をクリックします。

  7. [Office 365 Exchange Online (Microsoft.Exchange)] を選択し、[選択] をクリックします。
  8. [委任されたアクセス許可] の中の [Access mailboxes as the signed-in user via Exchange Web Services] のチェックをオンにし、[選択] をクリックします。
  9. [完了] をクリックします。

以上で、Azure Active Directory へのアプリケーションの登録は完了です。

ステップ 2 : ライブラリの追加

OAuth を使うにあたって、Active Directory Authentication Library (ADAL) があったほうが簡単に扱えるので、C# のプロジェクトに追加します。色々方法はあると思いますが、Visual Studio の NuGet Package Manager で Microsoft.IdentityModel.Clients.ActiveDirectory をインストールするのが早いです。今回は EWS で接続しますので、EWS Managed API の Microsoft.Exchange.WebServices.dll も参照の追加をする必要がありますが、これも NuGet Package Manager から追加できます。
[Tools] – [NuGet Package Manager] – [Manage NuGet Packages for Solution] から以下のように Microsoft.Exchange.WebServices と Microsoft.IdentityModel.Clients.ActiveDirectory を追加しておいてください。

ステップ 3 : コーディング

準備が整ったので実際にコードを書きます。まずはサンプルとして全文を記載します。なお事前にボタンとテキストボックスがフォームに配置されている前提です。ボタンのクリック イベントのハンドラーとして戻り値が void の button1_ClickAsync メソッドを使用しています。

using Microsoft.Exchange.WebServices.Data;
using Microsoft.IdentityModel.Clients.ActiveDirectory;
using System;
using System.Diagnostics;
using System.Windows.Forms;

namespace MyEwsDemoApp02
{
    public partial class Form1 : Form
    {
        public Form1()
        {
            InitializeComponent();
        }

        private async void button1_ClickAsync(object sender, EventArgs e)
        {
            string authority = "https://login.windows.net/contoso.onmicrosoft.com"; // テナント名を指定する
            string clientId = "12345678-abcd-1234-5678-1234567890ab"; // 登録したアプリケーションのクライアント ID
            Uri redirectUri = new Uri("https://localhost/MyEwsDemoApp02"); // 登録したアプリケーションのリダイレクト URL
            string resourceName = "https://outlook.office365.com";

            AuthenticationResult authenticationResult = null;
            AuthenticationContext authenticationContext = new AuthenticationContext(authority, false);

            string errorMessage = null;
            try
            {
                Debug.WriteLine("Trying to acquire token");

                // Office365 のサインイン ページ (および初回の同意画面) を表示する場合
                authenticationResult = await authenticationContext.AcquireTokenAsync(resourceName, clientId, redirectUri, new PlatformParameters(PromptBehavior.Always));

                // すでに同意済みで資格情報をハードコードする場合
                //authenticationResult = await authenticationContext.AcquireTokenAsync(resourceName, clientId, new UserPasswordCredential("User01@contoso.onmicrosoft.com", "password"));
            }
            catch (AdalException ex)
            {
                errorMessage = ex.Message;
                if (ex.InnerException != null)
                {
                    errorMessage += "\nInnerException : " + ex.InnerException.Message;
                }
            }
            catch (ArgumentException ex)
            {
                errorMessage = ex.Message;
            }

            if (!string.IsNullOrEmpty(errorMessage))
            {
                MessageBox.Show("Failed: " + errorMessage);
                return;
            }

            ExchangeService service = new ExchangeService(ExchangeVersion.Exchange2013_SP1);
            service.Url = new Uri("https://outlook.office365.com/ews/exchange.asmx");
            service.Credentials = new OAuthCredentials(authenticationResult.AccessToken);

            // 偽装権限を使用する場合
            //service.ImpersonatedUserId = new ImpersonatedUserId(ConnectingIdType.SmtpAddress, "User02@contoso.onmicrosoft.com");

            FindItemsResults<Item> result = service.FindItems(WellKnownFolderName.Inbox, new ItemView(1));
            textBox1.Text = result.Items[0].Subject;
        }
    }
}

注意点はそれほどありませんが、冒頭の変数を環境に合わせる必要があります。
authority に実際のテナントの名前を使用するので、contoso の部分を適宜変更してください。
clientId は、Azure AD にアプリケーションを登録した後に表示されているアプリケーション ID です。
redirectUri は、Azure AD にアプリケーションを登録した時に指定したリダイレクト URI です。

それだけ変更すれば、後は実行するだけです。まず Office365 のサインイン画面が表示されるので、接続先となるユーザーの資格情報でサインインします。そのあとは通常の EWS です。参考として、資格情報をハードコードする方法と、偽装権限 (Impersonation) を使用する方法も、コメント アウトして記載しています。

このように比較的簡単に OAuth で EWS を使用できます。EWS Managed API としては資格情報を指定するコードが変わるだけで、他は基本認証の時のコードのまま使用できます。ただし Access Token は既定で有効期間が 1 時間のため、処理が 1 時間以上続くような場合は Access Token を取得しなおす必要があります。そのあたりの ADAL の使い方は Wiki を参照して実装してください。