背景
EWS(Exchange Web Services)を利用して、Exchange Serverからメールを取得したり、メールを送信したりするアプリは多く存在するかと思いますが、EWSに対する基本認証が2020年10月13日で廃止されるようです。正確にはOffice365へ移行していたりすると、Office365側が基本認証を受け付けてくれなくなるようです。解決方法
解決方法としては基本認証からOauth認証に切り替える必要があるようです。参考URLはこちら
https://blogs.technet.microsoft.com/exchangeteamjp/2018/08/01/how-to-use-oauth-with-ews-in-some-scenarios/
Step.1 AzureADでのアプリ登録
EWSにOauthを利用してアクセスするためにはAzureAD上でアプリ登録を事前に行う必要があります。なぜこのようなアプリ登録が必要かというと、ユーザ名パスワードを知っていればどこからでもアクセスできるという状況を防ぐためと、勝手に理解しています。
イメージとしては今回登録したアプリがOffice365へのアクセス権を持っており、ユーザの代理でOffice365からメールの中身などを取得しに行きます。
詳細な設定方法は下記URLのネイティブアプリの登録を参照ください。
Step.2 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;
client_secret = 'アプリ登録時にクライアントシークレットをセット'
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 )
}
# 環境に合わせて変更
$DllPath = "C:\Program Files\Microsoft\Exchange\Web Services\2.2\Microsoft.Exchange.WebServices.dll" # EWS Managed API のパス
$EwsAdmin = "aaa@xxxx.onmicrosoft.com" # EWS 接続に利用するアカウントのユーザー プリンシパル名 (UPN)
$Password = 'xxxxx' # EWS 接続に利用するアカウントのパスワード
$TenantName = "xxxx.onmicrosoft.com" # 接続するテナントの onmicrosoft.com のドメイン名
$ClientId = "アプリ登録後に確認可能" # 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)
0 件のコメント:
コメントを投稿