Skip to main content

Modifying your EWS Managed API code to use Hybrid Modern Authentication against OnPrem Mailboxes

In this post I'm going to look at what you need to do in your EWS Managed API code to support using Hybrid Modern Authentication where previously you've been using Basic or Integrated Authentication (both of which are susceptible to password spray attacks). If you don't know what Hybrid Modern Authentication  is put simply it brings to Exchange OnPrem email clients the security benefits of Modern Authentication offered by Azure AD to Office365 tenants. If your already using OAuth to connect to Office365 you have most of the work already done but you will still need logic to ensure you have the correct Audience set in your token when that code is used against an OnPrem Mailbox. 


You need to be using Hybrid Exchange or more specifically 

Hybrid Office 365 tenant is configured in full hybrid configuration using Exchange Classic Hybrid Topology mode ref 

If you don't want to enable Hybrid Modern Authentication but still want to use oAuth in EWS you can do it and there is a good article by Ingo on how to do this

Authentication - Acquiring the Token

This is where you need to make the most changes in your current code as you will now need some logic that can be used to acquire the oAuth Tokens from AzureAD. The easiest way of doing this is to use one of the Authentication libraries from Microsoft either ADAL (if you already have this implemented in your code) or preferably use the MSAL library. The difference between ADAL and MSAL is ADAL uses the v1 Azure oauth endpoint and MSAL uses the v2 there is a good description of the differences between the two endpoints

Getting the intended Audience value for you Token Request 

The audience of an oAuth token is the intended recipient of the token (or basically the resource its going to be used against) , in our Exchange EWS context this is the host-name part of the EWS External endpoint. In Office365 the EWS Endpoint will be so the intended Audience of a token will be if you look at an Office365 token is this is what you see eg

When your using Hybrid Modern Authentication the Audience value for your token will become the external EWS endpoint's host-name of your OnPrem server (generally what you have configured in get-webservicesvirtualdirectory)

In the Authentication libraries this Audience is passed differently in

ADAL v1 Azure Endpoint its passed as the resourceURL

String ResourceURL = """;
var AuthResults = AuthContext.AcquireTokenAsync(ResourceURL ,
 "xxxxx-52b3-4102-aeff-aad2292ab01c", new Uri("urn:ietf:wg:oauth:2.0:oob"),
 new PlatformParameters(PromptBehavior.Always)).Result; 

In MASL v2 Azure Endpoint its passed as part of the scope 

string scope = "";
PublicClientApplicationBuilder pcaConfig =
var IntToken = pcaConfig.Build().AcquireTokenInteractive(
new[] { scope }).ExecuteAsync().Result;
With Hybrid Modern Auth in the above examples would be replaced with the host name for your external EWS endpoint which you would obtain usually via Autodiscover


Autodiscover in Exchange from Exchange 2007 has been there to help you basically discover the internal or external endpoint you need for whatever API your using. It is however an Authenticated Endpoint so when you remove Basic/Intergrated Authentication your code needs to be able to deal with this change. There are two ways you could go about this the first is generate a OAuth token first and use that to make the Authenticated traditional Auto-discover request. Or the second way is to use Autodiscover v2 (or Autodiscover json) which allows you to make an unauthenticated autodiscover requests to return the API endpoint you want. 

If your code targets predominately Office365 and Hybrid tenants then just switch to use Autodiscover v2, if you have a mix of Office365, Hybrid and OnPrem islands then you still need the legacy Basic/Integrated Auth method for these OnPrem clients. The Approach that I'm taking in this post is to first do a Realm discovery against Office365 to determine if a particular set of credentials is an Office365 or Hybrid account.If it is then a v2 Autodiscover request will be made against Office365  and if not fail back to the legacy code. This isn't 100% guaranteed to work for some OnPrem (especially pre Exchange 2016) and account combinations so my advice is you always make sure your try/catch autodiscover logic includes at least one legacy auto discover-attempt as a last fail back. And make sure you do some regression testing on your code change against Exchange 2013. 

For doing a simple JSON based Autodiscover against Office365 this can be done in a few lines with httpclient in c# 

String MailboxName = "";
String EWSEndPoint = $"{MailboxName}?Protocol=EWS";
HttpClient httpClient = new HttpClient();
httpClient.DefaultRequestHeaders.UserAgent.ParseAdd("Mozilla/5.0 (compatible; AcmeInc/1.0)");
dynamic JsonResult = JsonConvert.DeserializeObject(httpClient.GetAsync(EWSEndPoint).Result.Content.ReadAsStringAsync()
Or in PowerShell you could do it as a one-liner
(Invoke-WebRequest -Uri
?Protocol=EWS | ConvertFrom-Json).url
When your submitting an Autodiscover request against Office365 if your mailbox is OnPrem and you have HMA configured you will get returned your OnPrem EWS endpoint. 

EWS Managed API 

So what does this look like in the context of your EWS Managed API code, let first look at the traditional code path for Autodiscover

ExchangeService service = new ExchangeService(ExchangeVersion.Exchange2013);
service.Credentials = new WebCredentials("", "password");
service.AutodiscoverUrl("", RedirectionUrlValidationCallback);
Here is what it would look like use a Realm Discovery and then a Json Autodiscover and some MSAL code to do the Token Acquisition otherwise following the above logic.

A PowerShell version of the same thing would look like

If you want a library free version that uses Invoke-WebRequest it would look like

Dealing with Token Refresh (Important)

Access tokens by default in Azure are valid for 1 hour, so if your application is going to run for a long period of time or is persistent then you will need to manage token expiration and refresh. If your using one of the Authentication libraries then they can perform this for you automatically however they do rely on you calling their methods before you make any authenticated EWS call. Currently the EWS Managed API doesn't offer a callback to help you integrate easily with an Authentication library (for doing the Token Refresh management) so you will need to come up with your own method of doing this (eg a simple method to check the token before any operation could be used). Or you can modify the EWS Managed API source to integrate your own callback eg a good place to look is PrepareWebRequest in . The good thing about modifying the source is that you fix the issue for any operation that you code will do now and into the future. 

Popular posts from this blog

Downloading a shared file from Onedrive for business using Powershell

I thought I'd quickly share this script I came up with to download a file that was shared using One Drive for Business (which is SharePoint under the covers) with Powershell. The following script takes a OneDrive for business URL which would look like This script is pretty simple it uses the SharePoint CSOM (Client side object Model) which it loads in the first line. It uses the URI object to separate the host and relative URL which the CSOM requires and also the SharePointOnlineCredentials object to handle the Office365 SharePoint online authentication. The following script is a function that take the OneDrive URL, Credentials for Office365 and path you want to download the file to and downloads the file. eg to run the script you would use something like ./spdownload.ps1 '

Export calendar Items to a CSV file using EWS and Powershell

Somebody asked about this last week and while I have a lot of EWS scripts that do access the Calendar I didn't have a simple example that just exported a list of the Calendar events with relevant information to a CSV file so here it is. I've talked on this one before in this howto  but when you query the calendar folder using EWS you need to use a CalendarView which will expand any recurring appointments in a calendar. There are some limits when you use a calendarview in that you can only return a maximum of 2 years of appointments at a time and paging will limit the max number of items to 1000 per call. So if you have a calendar with a very large number of appointments you need to break your query into small date time blocks. In this example script I'm just grabbing the next 7 days of appointments if you want to query a longer period you need to adjust the following lines (keeping in mind what I just mentioned) #Define Date to Query $StartDate = (Get-Date) $EndDate

The MailboxConcurrency limit and using Batching in the Microsoft Graph API

If your getting an error such as Application is over its MailboxConcurrency limit while using the Microsoft Graph API this post may help you understand why. Background   The Mailbox  concurrency limit when your using the Graph API is 4 as per . This is evaluated for each app ID and mailbox combination so this means you can have different apps running under the same credentials and the poor behavior of one won't cause the other to be throttled. If you compared that to EWS you could have up to 27 concurrent connections but they are shared across all apps on a first come first served basis. Batching Batching in the Graph API is a way of combining multiple requests into a single HTTP request. Batching in the Exchange Mail API's EWS and MAPI has been around for a long time and its common, for email Apps to process large numbers of smaller items for a variety of reasons.  Batching in the Graph is limited to a m
All sample scripts and source code is provided by for illustrative purposes only. All examples are untested in different environments and therefore, I cannot guarantee or imply reliability, serviceability, or function of these programs.

All code contained herein is provided to you "AS IS" without any warranties of any kind. The implied warranties of non-infringement, merchantability and fitness for a particular purpose are expressly disclaimed.