シナリオ別にみる EWS での OAuth 利用方法


すでに Exchange Team Blog や Office 365 管理センターのメッセージ センターでご確認いただいている方も多いと存じますが、Exchange Online の EWS (Exchange Web サービス) では 2020 年 10 月 13 日をもって基本認証のサポートが終了します。
そのため、基本認証で EWS を使用して Exchange Online へ接続している既存のアプリケーションや PowerShell スクリプトは、何らかの対策を行う必要があります。

これを機に Microsoft Graph へ移行することも 1 つの方法ではありますが、既存のコード資産を有効に活用するのであれば OAuth を利用するように修正することも有効です。
今回はすでに EWS アプリケーションを開発した経験のある方に向けて、3 つのシナリオで OAuth を使用した EWS 接続を行う方法を紹介します。

はじめに

OAuth を使用するにあたり、クライアントがネイティブ アプリなのか Web アプリなのかを意識する必要があります。
PowerShell スクリプトや Windows アプリケーションの場合はネイティブ アプリです。
ブラウザーからアクセスし、EWS 接続を Web サーバーから行う場合は Web アプリです。

ここからは次の 3 つのシナリオに分けて説明を行います。

A) PowerShell スクリプトで EWS に接続する
B) C# で実装した Windows Forms アプリケーションで EWS に接続する
C) C# で実装した ASP.NET Web ページで EWS に接続する

いずれも OAuth を使用する EWS のデモとして必要最低限の内容になっています。
実際の開発ではこれらをさらに発展させて、要件に合わせた実装が必要です。

OAuth を使用する場合、まずはアプリケーションを Azure Active Directory へ登録します。
次にアクセス トークンを取得するコードを実装し、取得したアクセス トークンを EWS へ渡します。

Azure Active Directory へアプリケーションを登録する際、そのアプリケーションがネイティブ アプリなのか Web アプリなのかを選択します。
両方の手順を紹介しますが、シナリオ A および B の場合はネイティブ アプリとして登録します。
シナリオ C の場合は Web アプリとして登録します。

はじめにネイティブ アプリの登録方法を紹介し、次にシナリオ A の PowerShell スクリプトの場合の実装を説明します。
続けてシナリオ B の Windows Forms アプリケーションの場合の実装を説明します。

次にシナリオ C の ASP.NET Web ページの場合の実装を説明します。
その中で Web アプリの登録方法を紹介します。

ネイティブ アプリの登録

  1. 以下の URL にアクセスして、Azure Active Directory 管理センターにサインインします。
    https://aad.portal.azure.com/
  2. 左ペインより [Azure Active Directory] をクリックします。
  3. [アプリの登録] - [新しいアプリケーションの登録] をクリックします。
  4. 以下のように設定し、[作成] をクリックします。
    名前 : 任意のアプリケーションの名前を指定します (例 : MyEwsDemoApp01)
    アプリケーションの種類 : ネイティブ
    リダイレクト URI : 任意の URI (アクセス可能である必要はありません。例 : https://localhost/MyEwsDemoApp01)
  5. [アプリケーション ID] の値を控えておきます。
  6. [設定] - [必要なアクセス許可] - [追加] - [API を選択します] をクリックします。
  7. [Office 365 Exchange Online (Microsoft.Exchange)] を選択し、[選択] をクリックします。
  8. [委任されたアクセス許可] の中の [Access mailboxes as the signed-in user via Exchange Web Services] のチェックをオンにし、[選択] をクリックします。
  9. [完了] をクリックします。

PowerShell スクリプトの場合の実装

実装を始める前に、ネイティブ アプリの登録を行っていることを確認してください。

通常、OAuth を使用するフローでは GUI (ブラウザー) を使用して Azure AD の認証画面や同意画面を表示し、ユーザーの同意を得られればアクセス トークンが発行されます。
しかし PowerShell スクリプトで EWS に接続している場合は、自動化を目的としているためにスクリプト化していることが多いと考えられます。
そのようなシナリオにおいて認証画面などが表示されると、自動化の妨げになります。

そのため、ここではあえて資格情報をスクリプトに埋め込み、認証画面を表示しないでアクセス トークンを取得する実装を紹介します。
OAuth のメリットが失われ、多要素認証などモダンな認証方式にも対応できませんが、EWS 接続の自動化にはこのような対処が必要です。

ただしアプリケーションがトークンを使用するための同意は必要となるため、代わりに Azure Active Directory 管理センターで同意します。

  1. 先ほどのアプリケーションの登録の時と同じように Azure Active Directory 管理センターにサインインします。この時、EWS 接続に使用するユーザーの資格情報でサインインします。
  2. 左ペインより [Azure Active Directory] をクリックします。
  3. [アプリの登録] をクリックします。
  4. [アプリケーションをすべて表示] をクリックします。
  5. 先ほど登録したアプリケーションをクリックします。
  6. [設定] をクリックします。
  7. [必要なアクセス許可] をクリックします。
  8. [アクセス許可の付与] をクリックします。
  9. 確認が表示されますので、[はい] をクリックします。

これで作業を行ったユーザーは、このアプリケーションがトークンを使用することに同意したことになります。
この操作を Office 365 全体管理者が行った場合は、すべてのユーザーついて、このアプリケーションがトークンを使用することに同意したことになりますので、管理者が代行することも可能です。

次にスクリプトを実装します。
なお後述するライブラリ (ADAL) を PowerShell で使用することも技術的には可能ですが、用意する手間やスクリプトの手軽さを優先して、今回はライブラリを使用しません。
ライブラリを使用しない代わりに、以下のように関数を定義します。

<#
.SYNOPSIS
Acquire the access token for EWS non-interactively.

.DESCRIPTION
Acquire the access token for EWS non-interactively.

.PARAMETER TenantName
Tenant name of the user to be used for authentication.

.PARAMETER ClientId
Client ID of the application registered in Azure Active Directory.

.PARAMETER Credential
Credential of the user to be used for authentication.

.EXAMPLE
Get-TokenForEws -TenantName "contoso.onmicrosoft.com" -ClientId "7acfa599-e1bf-43e1-aab8-f12c1d952d3d" -Credential:(Get-Credential)

#>
function Get-TokenForEws {
    param (
        [Parameter(Mandatory = $true, ValueFromPipeline = $false)]
        [ValidateNotNullOrEmpty()]
        [string]$TenantName,

        [Parameter(Mandatory = $true, ValueFromPipeline = $false)]
        [ValidateNotNullOrEmpty()]
        [string]$ClientId,

        [Parameter(Mandatory = $true, ValueFromPipeline = $false)]
        [ValidateNotNullOrEmpty()]
        [PSCredential]$Credential
    )

    $RequestBody = @{
        resource   = "https://outlook.office.com/";
        client_id  = $ClientId;
        grant_type = "password";
        username   = $Credential.UserName;
        password   = $Credential.GetNetworkCredential().Password;
        scope      = "openid"
    }

    return Invoke-RestMethod -Uri "https://login.microsoftonline.com/$($TenantName)/oauth2/token" -Method Post -Body $RequestBody
}

<#
.SYNOPSIS
Acquire the access token for EWS non-interactively using the refresh token.

.DESCRIPTION
Acquire the access token for EWS non-interactively using the refresh token.

.PARAMETER TenantName
Tenant name of the user to be used for authentication.

.PARAMETER ClientId
Client ID of the application registered in Azure Active Directory.

.PARAMETER RefreshToken
The refresh token acquired by Get-TokenForEws Cmdlet.

.EXAMPLE
Get-TokenForEwsUsingRefreshToken -TenantName "contoso.onmicrosoft.com" -ClientId "7acfa599-e1bf-43e1-aab8-f12c1d952d3d" -RefreshToken $Token1.refresh_token #> function Get-TokenForEwsUsingRefreshToken {
    param (
        [Parameter(Mandatory = $true, ValueFromPipeline = $false)]
        [ValidateNotNullOrEmpty()]
        [string]$TenantName,

        [Parameter(Mandatory = $true, ValueFromPipeline = $false)]
        [ValidateNotNullOrEmpty()]
        [string]$ClientId,

        [Parameter(Mandatory = $true, ValueFromPipeline = $false)]
        [ValidateNotNullOrEmpty()]
        [string]$RefreshToken
    )

    $RequestBody = @{
        resource      = "https://outlook.office.com/";
        client_id     = $ClientId;
        grant_type    = "refresh_token";
        refresh_token = $RefreshToken;
        scope         = "openid"
    }

    return Invoke-RestMethod -Uri "https://login.microsoftonline.com/$($TenantName)/oauth2/token" -Method Post -Body $RequestBody
}

<#
.SYNOPSIS
Check if re-acquiring the access token is needed or not.

.DESCRIPTION
Check if re-acquiring the access token is needed or not.

.PARAMETER Token
The return value of Get-TokenForEws Cmdlet.

.EXAMPLE
ShouldRefresh -Token $Token1
#>
function ShouldRefresh {
    param (
        [Parameter(Mandatory = $true, ValueFromPipeline = $false)]
        [ValidateNotNullOrEmpty()]
        [PSCustomObject]$Token
    )

    $BaseDate = New-Object System.DateTime(1970, 1, 1, 0, 0, 0, [System.DateTimeKind]::Utc)
    $UtcNow = (Get-Date).ToUniversalTime() - $BaseDate
    $TimeSpan = $Token.expires_on - $UtcNow.TotalSeconds
    return (($TimeSpan / 60) -le 5 )
}

Get-TokenForEws は EWS 接続用のアクセス トークンや、アクセス トークンの更新に必要になるリフレッシュ トークンを取得します。
Get-TokenForEwsUsingRefreshToken は、取得済みのリフレッシュ トークンを使用してアクセス トークンを更新 (新しいアクセス トークンを取得) します。
ShouldRefresh はアクセス トークンの有効期間を確認して、更新が必要かどうか判定します。

アクセス トークンを取得する以外にも 2 つの関数を用意しているのは、アクセス トークンの有効期限が既定で 1 時間のためです。
適宜 ShouldRefresh で有効期限が迫っており更新が必要か (上記の実装では、少し余裕を持たせて有効期限が残り 5 分を切っていないかどうか) 判定し、更新が必要であれば Get-TokenForEwsUsingRefreshToken を使用してアクセス トークンを更新します。

これらの関数と EWS Managed API と組み合わせると、以下のようになります。

# 環境に合わせて変更
$DllPath  = "C:\Program Files\Microsoft\Exchange\Web Services\2.2\Microsoft.Exchange.WebServices.dll" # EWS Managed API のパス
$EwsAdmin = "EwsAdmin01@contoso.onmicrosoft.com" # EWS 接続に利用するアカウントのユーザー プリンシパル名 (UPN) 
$Password = "password" # EWS 接続に利用するアカウントのパスワード
$TenantName = "contoso.onmicrosoft.com" # 接続するテナントの onmicrosoft.com のドメイン名
$ClientId = "7acfa599-e1bf-43e1-aab8-f12c1d952d3d" # Azure Active Directory に登録したアプリケーションのアプリケーション ID 
$EwsUrl = "https://outlook.office365.com/EWS/Exchange.asmx" # EWS の URL

# EWS Managed API のアセンブリをロード
$Assembly = [Reflection.Assembly]::LoadFile($DllPath)
if ($Assembly -eq $null) {
    exit
}

$Token = Get-TokenForEws -TenantName $TenantName -ClientId $ClientId -Credential:(New-Object System.Management.Automation.PSCredential($EwsAdmin, (ConvertTo-SecureString $Password -AsPlainText -Force)))

# ExchangeService インスタンスの生成
$Service = New-Object Microsoft.Exchange.WebServices.Data.ExchangeService([Microsoft.Exchange.WebServices.Data.ExchangeVersion]::Exchange2013_SP1, $TimeZone)
$Service.Url = New-Object System.Uri($EwsUrl)
$Service.Credentials = New-Object Microsoft.Exchange.WebServices.Data.OAuthCredentials($Token.access_token)

# 以降、通常と同じ EWS の処理
# $Inbox = [Microsoft.Exchange.WebServices.Data.Folder]::Bind($Service, [Microsoft.Exchange.WebServices.Data.WellKnownFolderName]::Inbox)

# 処理が長時間におよぶ場合
if (ShouldRefresh -Token $Token) {
    # アクセス トークンの更新が必要
    $Token = Get-TokenForEwsUsingRefreshToken -TenantName $TenantName -ClientId $ClientId -RefreshToken $Token.refresh_token
    $Service.Credentials = New-Object Microsoft.Exchange.WebServices.Data.OAuthCredentials($Token.access_token)
}

# 処理を継続
#$SentItems = [Microsoft.Exchange.WebServices.Data.Folder]::Bind($Service, [Microsoft.Exchange.WebServices.Data.WellKnownFolderName]::SentItems)

以上で PowerShell スクリプトの場合の実装は完了です。

Windows Forms アプリケーションの場合の実装

実装を始める前に、ネイティブ アプリの登録を行っていることを確認してください。

Windows Forms アプリケーションの場合、ユーザーがアプリケーションを起動し、アプリケーションが操作を行っているユーザーを識別した上でユーザーの代わりに EWS 接続を行います。
この一連の処理を実現するために、ここでは ADAL (Azure Active Directory 認証ライブラリ) を使用して操作しているユーザーのアクセス トークンを取得し、EWS 接続に使用する方法を紹介します。

  1. Visual Studio を起動し、新しい C# の Windows Forms アプリケーションのプロジェクトを作成します。
    使用するライブラリの要件を満たすため、ターゲットとする .NET Framework は 4.5 以上である必要があります。
    ソリューション名やプロジェクト名は任意でかまいません。
  2. [ツール] - [NuGet パッケージ マネージャー] - [ソリューションの NuGet パッケージの管理] をクリックします。
  3. [参照] タブに移動します。
  4. 以下の 2 つのパッケージ (ADAL と EWS Managed API) を検索してインストールします。
    なお執筆時点では Microsoft.IdentityModel.Clients.ActiveDirectory バージョン 3.19.8 および Microsoft.Exchange.WebServices バージョン 2.2.0 を使用して動作確認をしています。

    Microsoft.IdentityModel.Clients.ActiveDirectory
    Microsoft.Exchange.WebServices

  5. テキスト ボックスを 4 つ配置し、以下のようにプロパティを設定します。

    (Name) : textBox1
    Location : 12, 12
    Size : 260, 19

    (Name) : textBox2
    Location : 12, 38
    Size : 260, 19

    (Name) : textBox3
    Location : 12, 64
    Size : 260, 19

    (Name) : textBox4
    Anchor : Top, Bottom, Left, Right
    Location : 12, 119
    MultiLine : True
    Size : 260, 130

  6. Form1.cs 上にボタンを 1 つ配置し、以下のようにプロパティを設定します。

    (Name) : button1
    Location : 12, 89
    Text : 実行

  7. ボタンをダブルクリックして Click イベントのコードに移動します。
  8. コードの先頭に以下の 2 行を追記します。
    using Microsoft.Exchange.WebServices.Data;
    using Microsoft.IdentityModel.Clients.ActiveDirectory;
    
  9. 以下のよう Click イベントのコードを記述します。
            private void button1_Click(object sender, EventArgs e)
            {
                string authority = "https://login.windows.net/" + textBox1.Text;
                string clientId = textBox2.Text;
                Uri clientAppUri = new Uri(textBox3.Text);
                string serverName = "https://outlook.office365.com/";
    
                AuthenticationContext authenticationContext = new AuthenticationContext(authority, false);
                AuthenticationResult authenticationResult = authenticationContext.AcquireTokenAsync(serverName, clientId, clientAppUri, new PlatformParameters(PromptBehavior.Always)).Result;
    
                ExchangeService service = new ExchangeService(ExchangeVersion.Exchange2013_SP1);
                service.Url = new Uri("https://outlook.office365.com/EWS/Exchange.asmx");
                service.Credentials = new OAuthCredentials(authenticationResult.AccessToken);
    
                // 以降、通常と同じ EWS の処理
                Folder inbox = Folder.Bind(service, WellKnownFolderName.Inbox);
                textBox4.Text = inbox.DisplayName;
    
                // 処理が長時間におよぶ場合
                AuthenticationResult authenticationResult2 = authenticationContext.AcquireTokenSilentAsync(serverName, clientId).Result;
                service.Credentials = new OAuthCredentials(authenticationResult2.AccessToken);
    
                // 処理を継続
                Folder sentItems = Folder.Bind(service, WellKnownFolderName.SentItems);
                textBox4.Text += Environment.NewLine + sentItems.DisplayName;
            }
    

実装は以上で完了です。

このアプリケーションを実行すると、フォームが表示されますので、上の 3 つのテキスト ボックスにそれぞれ以下のように値を入力します。

 1 つ目 : 接続するテナントの onmicrosoft.com のドメイン名 (例 : contoso.onmicrosoft.com)
 2 つ目 : Azure Active Directory に登録したアプリケーションのアプリケーション ID
 3 つ目 : Azure Active Directory に登録したアプリケーションのアプリケーション ID リダイレクト URI

[実行] をクリックすると Office 365 の認証画面が表示されますのでサインインします。
初回はアクセスを許可するかの同意画面が表示されますので、内容を確認して [承諾] をクリックしてください。
画面上に、認証したユーザーの「受信トレイ」フォルダーと「送信済みアイテム」フォルダーの表示名が出力されます。

このサンプルでは、開発環境の ADAL のキャッシュの影響を受けないよう、必ず資格情報の入力が必要なように構成しています。(PromptBehavior.Always の部分)
そのため、イントラネット向けに Windows 統合認証のみを有効にした AD FS 環境で、ドメイン参加端末でテストを行う場合にエラーが発生することがあることを確認しています。
対処策としては PromptBehavior.Auto に変更してください。

なおアクセス トークンの有効期限は既定で 1 時間のため、アプリケーションを 1 時間以上使用する場合はアクセス トークンの再取得が必要です。
上記のコードでは AcquireTokenSilentAsync を使用して、必要な場合に自動でリフレッシュ トークンからアクセス トークンの再取得を行っています。

ASP.NET Web ページの場合の実装 Part 1

Web アプリの場合は、Azure Active Directory に登録するアプリケーションのサインオン URL に実際にアクセス可能である必要があります。
開発する Web アプリの URL が必要となりますが、そのためにドメインの取得や Web サーバーの用意、場合によっては Microsoft Azure のご利用をご検討いただくことになるかもしれません。
ただ、今回のお話については単純化するためにローカルの開発環境でテストを行うことを想定した手順を紹介します。
まず初めに実装から入ります。

  1. Visual Studio 2017 を起動します。
  2. C# で新しい 「ASP.NET 空の Web サイト」のプロジェクトを作成します。
    使用するライブラリの要件を満たすため、ターゲットとする .NET Framework は 4.5 以上である必要があります。
    ソリューション名やプロジェクト名は任意でかまいません。
  3. [ツール ] - [NuGet パッケージ マネージャー] - [ソリューションの NuGet パッケージの管理] をクリックします。
  4. [参照] タブに移動します。
  5. 以下の 2 つのパッケージ (ADAL と EWS Managed API) を検索してインストールします。
    なお執筆時点では Microsoft.IdentityModel.Clients.ActiveDirectory バージョン 3.19.8 および Microsoft.Exchange.WebServices バージョン 2.2.0 を使用して動作確認をしています。

    Microsoft.IdentityModel.Clients.ActiveDirectory
    Microsoft.Exchange.WebServices

  6. プロジェクトに「Default.aspx」というファイル名で新しい Web フォームを追加します。
  7. Default.aspx 上に Label コントロールを 2 つ配置します。
    ID は Label1 と Label2 (既定のまま) にします。Text はブランク (空文字列) に変更します。
  8. [ソリューション エクスプローラー] でプロジェクトを選択し、プロパティを表示します。
  9. [SSL 有効] を True に変更します。
  10. [SSL URL] の値 (https://localhost:44333/ などの localhost とポート番号の組み合わせ) を控えておきます。この値を Web アプリの URL として使用します。

Web アプリの登録

Web アプリの URL が決まったら、Azure Active Directory に登録します。

  1. 以下の URL にアクセスして、Azure Active Directory 管理センターにサインインします。
    https://aad.portal.azure.com/
  2. 左ペインより [Azure Active Directory] をクリックします。
  3. [アプリの登録] - [新しいアプリケーションの登録] の順にクリックします。
  4. 以下のように設定し、[作成] をクリックします。

    名前 : 任意のアプリケーションの名前を指定します (例 : MyEwsWebApp01)
    アプリケーションの種類 : Web アプリ/API
    サインオン URL : Web アプリの URL (例 : https://localhost:44333/)

  5. 作成したアプリケーションの情報が表示されていない場合は、アプリケーションの一覧から作成したアプリケーションをクリックしてください。
    アプリケーションの一覧に作成したアプリケーションが表示されていない場合は [マイ アプリ] が選択されているドロップダウン メニューから [すべてのアプリ] を選択してください。
  6. 作成したアプリケーションの情報から [アプリケーション ID] の値を控えておきます。
  7. [設定] - [必要なアクセス許可] - [追加] - [API を選択します] をクリックします。
  8. [Office 365 Exchange Online (Microsoft.Exchange)] を選択し、[選択] をクリックします。
  9. [委任されたアクセス許可] の中の [Access mailboxes as the signed-in user via Exchange Web Services] のチェックをオンにし、[選択] をクリックします。
  10. [完了] をクリックします。
  11. [設定] 内の [キー] をクリックします。
  12. [キーの説明] と書かれている部分に任意のキーの説明を入力します。(例 : Key01)
  13. [期間] が選択されているドロップダウン メニューから [期限なし] を選択します。
  14. [保存] をクリックすると [値] が表示されますので内容を控えておきます。

(オプション : Web アプリケーションの URL が変更や追加になるときは、[設定] 内の [応答 URL] から構成することができます。)

ASP.NET Web ページの場合の実装 Part 2

Web アプリの登録ができたら、実装を続けます。

  1. Visual Studio 2017 に戻り Default.aspx のコード (Default.aspx.cs) を表示します。
  2. ファイル冒頭の using ステートメントを以下に変更します。
    using Microsoft.Exchange.WebServices.Data;
    using Microsoft.IdentityModel.Clients.ActiveDirectory;
    using System;
    
  3. クラスに以下の内容の変数を追加します。
    コメントを参考に実際の値に変更する必要があります。

            string tenantName = "contoso.onmicrosoft.com"; // 接続するテナントの onmicrosoft.com のドメイン名 (例 : contoso.onmicrosoft.com)
            string clientID = "bf3d4a67-1c2d-41f3-b98c-bde1ac0662e5"; // Azure Active Directory に登録したアプリケーションのアプリケーション ID
            string clientAppUri = @"https://localhost:44344/"; // Azure Active Directory に登録したアプリケーションのサインオン URL
            string key = "loAdXh6WcZJfboxc6UcPgrvp1kcEdXHlnVAkwF/Twnoa"; // Azure Active Directory に登録したアプリケーションのキー
    
  4. 以下のサンプル コードを参考に、EwsWork メソッドを追加します。
    このメソッドは取得したアクセス トークンを使用して EWS に接続し、「受信トレイ」フォルダーの表示名を取得するサンプルとなっています。

            private void EwsWork(string token)
            {
                ExchangeService service = new ExchangeService(ExchangeVersion.Exchange2013_SP1);
                service.Url = new Uri("https://outlook.office365.com/EWS/Exchange.asmx");
                service.Credentials = new OAuthCredentials(token);
    
                // 以降、通常と同じ EWS の処理
                Folder inbox = Folder.Bind(service, WellKnownFolderName.Inbox);
                Label1.Text = inbox.DisplayName;
            }
    
  5. 以下のサンプル コードを参考に、Page_Load メソッドを実装します。
    このメソッドはページが表示された時に必要に応じてアクセス トークンを取得するサンプルとなっています。

            protected void Page_Load(object sender, EventArgs e)
            {
                AuthenticationContext authenticationContext = new AuthenticationContext("https://login.microsoftonline.com/" + tenantName);
                Uri redirectUri = new Uri(clientAppUri);
                ClientCredential credential = new ClientCredential(clientID, key);
    
                if (Session["AccessToken"] == null)
                {
                    // 取得済みの Access Token がまだない
    
                    if (string.IsNullOrEmpty(Request.QueryString["code"]))
                    {
                        // 認証画面からのリダイレクトではない
                        // Authorization Code を取得するために認証画面へリダイレクトする
    
                        Uri authUri = authenticationContext.GetAuthorizationRequestUrlAsync("https://outlook.office365.com/", clientID, redirectUri, UserIdentifier.AnyUser, null).Result;
                        Response.Redirect(authUri.ToString());
                    }
                    else
                    {
                        // 認証画面からリダイレクトされてきており Autorization Code を取得できている
    
                        string authCode = Request.QueryString["code"];
    
                        try
                        {
                            // アクセス トークンを取得する
                            AuthenticationResult authenticationResult = authenticationContext.AcquireTokenByAuthorizationCodeAsync(authCode, redirectUri, credential, "https://outlook.office365.com/").Result;
                            
                            // 取得したアクセス トークンとユーザー情報を Session に保存
                            Session["AccessToken"] = authenticationResult.AccessToken;
                            Session["UserIdentifier"] = authenticationResult.UserInfo.UniqueId;
    
                            // URL に含まれる code を削除するため、一度リダイレクトする
                            Response.Redirect("/");
                        }
                        catch (AdalException ex)
                        {
                            Label2.Text = ex.Message;
                        }
                    }
                }
                else
                {
                    // すでに取得済みのアクセス トークンがある
    
                    UserIdentifier userIdentifier = new UserIdentifier(Session["UserIdentifier"].ToString(), UserIdentifierType.UniqueId);
    
                    try
                    {
                        // 必要に応じてアクセス トークンを更新する
                        AuthenticationResult authenticationResult = authenticationContext.AcquireTokenSilentAsync("https://outlook.office365.com/", credential, userIdentifier).Result;
    
                        // 取得したアクセス トークンを Session に保存
                        Session["AccessToken"] = authenticationResult.AccessToken;
    
                        EwsWork(authenticationResult.AccessToken);
                    }
                    catch (AdalException ex)
                    {
                        Label2.Text = ex.Message;
                    }
                }
            }
    

実装は以上で完了です。
デバッグ実行を開始して動作を確認してください。

正常に動作した場合、認証を行った後に、EWS で取得した「受信トレイ」フォルダーの表示名が表示されます。
なお開発環境の構成によっては認証がうまく行えない場合がありますので、ブラウザーを InPrivate モードで利用することもご検討ください。

おわりに

今回は冒頭にも説明した通り 3 つのシナリオに分けて EWS 接続に OAuth を使用する方法を紹介しました。
なお特に触れませんでしたが、EWS の偽装権限を使用する場合も OAuth の処理としては特に変わりはありません。
そのため、OAuth の認証を偽装権限を持つユーザーで行えば、あとは基本認証の時と同じです。

OAuth について詳しくは以下の技術情報をご参照ください。
今回はサンプルのため割愛いたしましたが、要件によってはトークンの検証やサインアウト処理などを実装する必要がございます。

TITLE: Authorize access to Azure Active Directory web applications using the OAuth 2.0 code grant flow
URL: https://docs.microsoft.com/en-us/azure/active-directory/develop/active-directory-protocols-oauth-code

TITLE: Azure Active Directory Authentication Libraries
URL: https://docs.microsoft.com/en-us/azure/active-directory/develop/active-directory-authentication-libraries

TITLE: Home · AzureAD/azure-activedirectory-library-for-dotnet Wiki
URL: https://github.com/AzureAD/azure-activedirectory-library-for-dotnet/wiki

TITLE: Authenticate an EWS application by using OAuth
URL: https://docs.microsoft.com/en-us/exchange/client-developer/exchange-web-services/how-to-authenticate-an-ews-application-by-using-oauth

ご紹介したサンプル コードの利用にあたっては、下記の免責事項をご確認いただきますようお願いいたします。

免責事項について
================
本サンプルコードは、あくまでも説明のためのサンプルとして提供されるものであり、製品の実運用環境で使用されることを前提に提供されるものではありません。
本サンプルコードおよびそれに関連するあらゆる情報は、「現状のまま」で提供されるものであり、商品性や特定の目的への適合性に関する黙示の保証も含め、明示・黙示を問わずいかなる保証も付されるものではありません。
マイクロソフトは、お客様に対し、本サンプルコードを使用および改変するための非排他的かつ無償の権利ならびに本サンプルコードをオブジェクトコードの形式で複製および頒布するための非排他的かつ無償の権利を許諾します。
但し、お客様は、(1)本サンプルコードが組み込まれたお客様のソフトウェア製品のマーケティングのためにマイクロソフトの会社名、ロゴまたは商標を用いないこと、(2)本サンプルコードが組み込まれたお客様のソフトウェア製品に有効な著作権表示をすること、および(3)本サンプルコードの使用または頒布から生じるあらゆる損害(弁護士費用を含む)に関する請求または訴訟について、マイクロソフトおよびマイクロソフトの取引業者に対し補償し、損害を与えないことに同意するものとします。

※本情報の内容(添付文書、リンク先などを含む)は、作成日時点でのものであり、予告なく変更される場合があります

小間 竜太郎

Skip to main content