This is a follow-on from my last post on Modifying your EWS Managed API code to use Hybrid Modern Authentication against OnPrem Mailboxes . If instead of the EWS Managed API you are using EWS Proxy Code (generated from the EWS WSDL) and you want to migrate it to using Modern Authentication for Office365 and/or Hybrid here's a method you can use using the MSAL Authentication library.
Unlike the EWS Managed API the WSDL generated proxy classes and specifically the ExchangeServiceBinding class doesn't have any provision to use Token Credentials. One way of implementing this in .NET is to take advantage of Polymorphism and create a new class that is derived from the ExchangeServiceBinding class and then override the method GetWebResponse from this class (which is actually derived from the SoapHttpClientProtocol class which contains the actual method we are going to override https://docs.microsoft.com/en-us/dotnet/api/system.web.services.protocols.soaphttpclientprotocol.getwebrequest?view=netframework-4.8 )
At the same time we can also add the X-AnchorMailbox header into the request which is also recommended for any Exchange Online requests you make. And because this method is called before every EWS Request we can place our Token Refresh code in there. In this example I'm using which uses the MSAL all you need to include is code that fetches the token from the TokenCache, this will trigger a Token Refresh if need or ultimately throw to Interaction if the Refresh Token isn't available. So here is a basic C# Console App that can do Hybrid/Modern Auth discover using the MSAL library. If you want the project files you can download them from here
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
using System; | |
using System.Collections.Generic; | |
using System.Linq; | |
using System.Text; | |
using System.Net; | |
using System.Threading.Tasks; | |
using System.Net.Http; | |
using Microsoft.Identity.Client; | |
using Newtonsoft.Json; | |
using EWSWSDLoAuthExample.com.office365.outlook; | |
namespace EWSWSDLoAuthExample | |
{ | |
class Program | |
{ | |
static void Main(string[] args) | |
{ | |
OAuthExchangeServiceBinding exchangeServiceBinding = new OAuthExchangeServiceBinding(); | |
String ClientId = "111111-52b3-4102-aeff-aad2292ab01c"; | |
String MailboxName = "user@domain.onmicrosoft.com"; | |
HttpClient httpClient = new HttpClient(); | |
httpClient.DefaultRequestHeaders.UserAgent.ParseAdd("Mozilla/5.0 (compatible; AcmeInc/1.0)"); | |
StringContent RealmRequest = new StringContent("{\"username\":\"" + MailboxName + "\"}"); | |
String DiscoveryURL = "https://login.microsoftonline.com/common/GetCredentialType"; | |
dynamic RealmDiscover = JsonConvert.DeserializeObject(httpClient.PostAsync(DiscoveryURL, RealmRequest).Result.Content.ReadAsStringAsync().Result); | |
if ((Int32)RealmDiscover.EstsProperties.DomainType == 1 || (Int32)RealmDiscover.EstsProperties.DomainType == 2) | |
{ | |
exchangeServiceBinding.Credentials = new NetworkCredential("user1@contoso.com", "password"); | |
exchangeServiceBinding.RequestServerVersionValue = new RequestServerVersion(); | |
exchangeServiceBinding.RequestServerVersionValue.Version = ExchangeVersionType.Exchange2010_SP2; | |
} | |
else | |
{ | |
String AutoDiscoverEndpoint = $"https://outlook.office365.com/autodiscover/autodiscover.json/v1.0/{MailboxName}?Protocol=EWS"; | |
dynamic JsonResult = JsonConvert.DeserializeObject(httpClient.GetAsync(AutoDiscoverEndpoint).Result.Content.ReadAsStringAsync().Result); | |
if (IsPropertyExist(JsonResult, "Url")) | |
{ | |
String AudienceHostName = new Uri(JsonResult.Url.ToString()).Host; | |
string scope = "https://" + AudienceHostName + "/EWS.AccessAsUser.All"; | |
PublicClientApplicationBuilder pcaConfig = PublicClientApplicationBuilder.Create(ClientId).WithAuthority(AadAuthorityAudience.AzureAdMultipleOrgs); | |
exchangeServiceBinding.PublicClientApplication = pcaConfig.Build(); | |
exchangeServiceBinding.oAuthScopes = new[] { scope }; | |
exchangeServiceBinding.AnchorMailbox = MailboxName; | |
var IntToken = exchangeServiceBinding.PublicClientApplication.AcquireTokenInteractive(exchangeServiceBinding.oAuthScopes).ExecuteAsync().Result; | |
exchangeServiceBinding.LogonHint = IntToken.Account.Username; | |
exchangeServiceBinding.Url = JsonResult.Url.ToString(); | |
exchangeServiceBinding.RequestServerVersionValue = new RequestServerVersion(); | |
exchangeServiceBinding.RequestServerVersionValue.Version = ExchangeVersionType.Exchange2016; | |
} | |
} | |
GetMailTipsType GetMailTips = new GetMailTipsType(); | |
GetMailTips.MailTipsRequested = new MailTipTypes(); | |
GetMailTips.MailTipsRequested = MailTipTypes.OutOfOfficeMessage; | |
GetMailTips.Recipients = new EmailAddressType[1]; | |
GetMailTips.Recipients[0] = new EmailAddressType(); | |
GetMailTips.Recipients[0].EmailAddress = MailboxName; | |
GetMailTips.SendingAs = new EmailAddressType(); | |
GetMailTips.SendingAs.EmailAddress = MailboxName; | |
GetMailTipsResponseMessageType GetMailTipsResponse = null; | |
try | |
{ | |
GetMailTipsResponse = exchangeServiceBinding.GetMailTips(GetMailTips); | |
Console.WriteLine("OOFTip" + GetMailTipsResponse.ResponseMessages[0].MailTips.OutOfOffice.ReplyBody.Message); | |
} | |
catch (Exception Exception) | |
{ | |
//To Do | |
} | |
} | |
public static bool IsPropertyExist(dynamic settings, string name) | |
{ | |
if (settings is Newtonsoft.Json.Linq.JObject) | |
return ((Newtonsoft.Json.Linq.JObject)settings).ContainsKey(name); | |
return settings.GetType().GetProperty(name) != null; | |
} | |
} | |
class OAuthExchangeServiceBinding : ExchangeServiceBinding | |
{ | |
public String AnchorMailbox { get; set; } | |
public IPublicClientApplication PublicClientApplication { get; set; } | |
public String[] oAuthScopes { get; set; } | |
public String LogonHint { get; set; } | |
protected override System.Net.WebResponse GetWebResponse(System.Net.WebRequest request) | |
{ | |
if (!String.IsNullOrEmpty(AnchorMailbox)) | |
{ | |
request.Headers.Add("X-AnchorMailbox", AnchorMailbox); | |
} | |
if (PublicClientApplication != null) | |
{ | |
Task<AuthenticationResult> authtask = null; | |
request.Headers.Remove("Authorization"); | |
try | |
{ | |
authtask = Task.Run<AuthenticationResult>(async () => await PublicClientApplication.AcquireTokenSilent(oAuthScopes, LogonHint).ExecuteAsync()); | |
} | |
catch (MsalUiRequiredException) | |
{ | |
authtask = Task.Run<AuthenticationResult>(async () => await PublicClientApplication.AcquireTokenInteractive(oAuthScopes).ExecuteAsync()); | |
} | |
request.Headers.Add("Authorization", "Bearer " + authtask.Result.AccessToken); | |
} | |
System.Net.HttpWebResponse | |
response = (System.Net.HttpWebResponse)base.GetWebResponse(request); | |
return response; | |
} | |
} | |
} |