Support for .Net Core
support using the ews-mananged-api from applications built for .net core
@OfficeDev Is their any news on this topic. Microsoft is putting big money on .NET core, but connecting to Exchange from a .NET Core application is still a no-go (using only the official package).
Is this coming anytime soon? Should we try to port it ourself? Any solutions?
I already made a port and we're using it in production. Here it is https://github.com/sherlock1982/ews-managed-api. It's available on Nuget. Though I made (almost) everything async. NET Standard version is slightly less functional than NET Framework version while Framework version has all the functionality as original.
- .NET Standard: LDAP Autodiscovery feature will not work
- Linux: DNS Autodiscovery feature will not work
- .NET Standard: Requests which requires serialization/deserialization of TimeZones will not work.
- .NET Standard: Authorization using login/password only. PartnerTokenCredentials, X509CertificateCredentials, WSSecurityUtilityIdSignedXml excluded
Other than that I'm quite happy.
Thanks @sherlock1982 can you provide us with the link to the correct nuget package? The readme in your repository still points to Microsoft.Exchange.Webservice.
Keep up the great work!
It's here: https://www.nuget.org/packages/Microsoft.Exchange.WebServices.NETStandard/1.0.4
@sherlock1982 Thank you for your work. Could you also include a small example project? I always get the exception message "Unable to load DLL 'dnsapi.dll'" and would like to know if I have any dependency issues.
dnsapi.dll is a Windows system dll which is used as a last attempt in Autodiscover process to resolve server by FQDN. So on Windows you will not get this message. On Linux the message "Unable to load DLL 'dnsapi.dll" should be treated as "Autodiscover failed". That's a minor issue.
Without Autodiscover you need to supply EWS link manually. Look here for example http://nuanceimaging.custhelp.com/app/answers/detail/a_id/13098/~/determining-the-exchange-web-services-(ews)-url-for-the-sharescan-exchange
For example for Office365 I use https://outlook.office365.com/ews/exchange.asmx Though I checked Autodiscover with Office365 and it works both Windows and Linux. You can get it failing with on premise Exchange and then you need a link.
Here's a sample for you:
var autodiscover = new AutodiscoverService(ExchangeVersion.Exchange2013_SP1)
{
RedirectionUrlValidationCallback = x => true,
Credentials = new WebCredentials(credentials.Username, credentials.Password)
};
var userSettings = autodiscover.GetUsersSettings(emails, UserSettingName.ExternalEwsUrl, UserSettingName.InternalEwsUrl, UserSettingName.GroupingInformation);
var successResponse = userSettings.First(x => x.ErrorCode == AutodiscoverErrorCode.NoError);
Than with response.TryGetSettingValue you should try to get UserSettingName.ExternalEwsUrl and if not found UserSettingName.InternalEwsUrl. That will be your urls. In case Autodiscover fails just supply the EWS url to your next call.
I plan to make this call async later.
@sherlock1982 Thank you for your answer. On Mac OS I still can't fix it. I've tested on Windows and here it works fine. Next I'm gonna give it a try on Ubuntu.
I enabled issues here https://github.com/sherlock1982/ews-managed-api/issues. You can report it and we can discuss. Please supply a sample which is not working and I'll try to help.
Though note one important thing. If you're using https than on Mac/Linux you should have a valid SSL certificate. Using NET Framework for Windows you can ignore certificate errors globally. It might be your issue as well.
@sherlock1982: Also, it doesn't work with NTLM on Linux, because .NET Core2/CURL has a bug in negotiating NTLMv2 when two "WWW-Authenticate"-http-headers are sent...
In other words, everything that requires a login with username/password doesn't work, too (on Linux, possibly Mac too).
WWW-Authenticate: Negotiate
WWW-Authenticate: NTLM
The "dnsapi.dll' can be worked around by setting the service.Url manually. Also, it can be replaced with the ARSoft DNS library, and the novell directory ldap. I'm working on that, but struggled with NTLM. Now being able to do it with Fiddler/Proxy that removes the Negotiate header.
@ststeiger issue with authentication should be fixed with a workaround we made already. This is here https://github.com/sherlock1982/ews-managed-api/issues/14
Concerning Autodiscovery I'm not that interested. Because well... I doubt anybody needs it. Everybody wants Office365 and Autodiscovery works perfectly. DNS is the last attempt to find url for on premise installations.
@sherlock1982: I noticed. Non the less, I'm confident I can fix that relatively easily. Thanks for the workaround link.
Concerning timezones, I fixed that in this commit here.
According to
https://stackoverflow.com/questions/4967903/linux-windows-timezone-mapping
you can map windows and linux timezones with the xml file from unicode.org.
Then all you need to do is serialize all timezonmappings, and use this to write a translate function to translate the read and write operations in XML for the timezone:
Microsoft.Exchange.WebServices.Data\ComplexProperties\TimeZones\TimeZoneDefinition.cs
I guess my code (looping though all entries in the xml file, finding the right one) is not very efficient and so could be optimized, but it was quick and it works.
I'm not sure if that fixes all issues with timezone, but it seems to work when listing folders. The result is here (netstandard2 - originating from the latest ms/OfficeDev-repo): https://github.com/ststeiger/RedmineMailService/tree/master/Microsoft.Exchange.WebServices.Data
Tested your workaround. It doesn't work, because exchange doesn't have basic-authentication enabled. So far, I solved the problem by proxying in Fiddler, removing the negotiate header. Reopened your issue.
@sherlock1982: I have it working on Linux now - used Titanium.Web.Proxy to remove the negotiate header.
See here: https://github.com/ststeiger/RedmineMailService
ExchangeService service = new ExchangeService(ExchangeVersion.Exchange2007_SP1);
// Titanium web-proxy is running on port 8000, and removing the negotiate header...
service.WebProxy = new System.Net.WebProxy("127.0.0.1", 8000);
service.PreAuthenticate = true;
service.UseDefaultCredentials = false;
service.Credentials = new WebCredentials("user@your_domain.com", "TOP_SECRET");
You really don’t need a proxy to use NTLM. You Just have to set it correctly.
In the attached image you’ll find the working code to use NTLM or Basic authentication. (Sorry that it’s an image, but that is because it is copied from a source control.

Yes you do, because the .NET framework on Linux doesn't handle multiple www-authenticate headers correctly - so you need to remove the value negotiate from the www-authenticate header in the proxy.
But it's not a given that it uses NTLM authentication, or basic-authentication. This is for the application to negotiate. Just sending the credentials with every request is wrong.
If you do it this way, you have a library which's feature-set can be accurately described as "works on my machine", but unfortunately not on yours.
And basically, removing the negotiate header is wrong as well. Microsoft just needs to fix their lousy framework.
Unfortunately, just updating CURL to the latest version on my machine doesn't solve it. Probably they have CURL statically linked.
Resolved. There's now a (potentially - not everything tested) fully working version for .NET Core here: https://github.com/ststeiger/RedmineMailService/tree/master/Microsoft.Exchange.WebServices.Data Clone as part of Redmine Mail Service.
This isn't resolved. The .NET Core Repo for Redmine doesn't have a nuget package and has no kind of support whats so ever. We need an OFFICIAL fix from Microsoft - this is craziness.
This ain't craziness. This is the latest code from the MSFT repository copied into a .NET Core project, and then about 10 simple bugfixes, all to be seen in the github commit list. If you want to make a nuget package, feel free to do so - I'll continue to stay a nuget non-fan. You know, in case you ever need a bugfix, you should have the sourcecode of your nuget packages anyway (of exactly that version that you used).
Also, I wouldn't put any exchange credentials into a package made by god-knows-who with sourcecode compiled from god-knows-which-sources. And that includes msft employees (no offense intended).
Your post leads me to believe that you should be a little more careful.
@sherlock1982
Hello, quick question about this
.NET Standard: Authorization using login/password only. PartnerTokenCredentials, X509CertificateCredentials, WSSecurityUtilityIdSignedXml excluded
We are using oAuth with OAuthCredentials, since this is not mentioned on this list i was wondering if you have tested support for it? I'm starting a port of one app to net standard and don't know if this will viable since we have a requirement of using only oAuth for authentication
Thanks for your work Regards
@jmosquedah: This might depend on the used NetStandard version. NetStandard 2.1 will/should/might handle the negotiate header correctly. NetStandard < 2.1 will not (on non-windows platforms).
Theoretically, it should work. There's no source-code reason why it won't. But you need to test it yourselfs, on all the platforms you want to use. Because there are runtime-reasons. Testing shouldn't be much of an effort if you have an exchange server setup for oauth. As stated, the version by @sherlock1982 has issues with timezones. Also, that version has started converting code to async, so you need to modify the samples. And it might have been pulled from a earlier version of officedev/ews....
In my repo, these issues are fixed, pulled from the current version, no async code yet, and there is a negotiate-header-removing proxy as workaround to the negotiate-header bug in the .NET-Core runtime. Also, there's fully working examples. No samples with oauth though.
ExchangeService exchangeService = new ExchangeService(ExchangeVersion.Exchange2013);
exchangeService.Url = new Uri(ConfigurationManager.AppSettings["serverName"]+"ews/exchange.asmx");
exchangeService.TraceEnabled = true;
exchangeService.TraceFlags = TraceFlags.All;
exchangeService.Credentials = new OAuthCredentials(authenticationResult.AccessToken));
exchangeService.FindFolders(WellKnownFolderName.Root, new Folderview(10));
Actually, since System.Drawing.Common and System.DirectoryServices are no longer preview now, it would now be simple to achive this.
Create a SHARED project. Copy all existing code into it. Fix timezone handling, such as [here] (maybe with a smarter variant for more performance).(https://github.com/ststeiger/RedmineMailService/commit/b2bf15b6c3e91b3f0fb76516a968da486ce1df9).
Go to Microsoft.Exchange.WebServices.Data\Core\Requests\HangingServiceRequestBase.cs and conditionally compile "HttpException",
//245:
//#if !(NETSTANDARD1_0 || NETSTANDARD1_1 || NETSTANDARD1_2 || NETSTANDARD1_3 || NETSTANDARD1_4 || NETSTANDARD1_5 || NETSTANDARD1_6 || NETSTANDARD1_7 || NETSTANDARD1_8 || NETSTANDARD1_9 || NETSTANDARD2_0 || NETSTANDARD2_1 || NETSTANDARD2_2)
// catch (HttpException ex)
// {
// // Stream is closed, so disconnect.
// this.Disconnect(HangingRequestDisconnectReason.Exception, ex);
// return;
// }
//#endif
Create a .NET 4.0, 4.5 project, as well as a netstandard 2.0 and 2.1 project Reference the shared project in each project. Add nugets System.DirectoryServices System.Drawing.Common System.Security.Cryptography.Xml to the netstandard-projects. Finished. Negotiate-header-removing proxy can be added by anybody who wishes to use netstandard 2.0 on Linux. Otherwise, netstandard 2.1 should be running fine everywhere. Afterwards, when everything runs smoothly, the projects can be separated/forked, and the latest version can be asyncified. Or you can just leave it like that, and have no async at all, until the lower frameworks die out.
.NET Core 3 is better at using .NET Framework packages (when on Windows), and just working. However you will get a warning that it "may not be fully compatible". However, an official .NET Core port will never happen as EWS ~~is being phased out in favour of Microsoft Graph~~ will no longer receive feature updates. So I suspect this can probably be closed.
https://developer.microsoft.com/en-us/graph/blogs/upcoming-changes-to-exchange-web-services-ews-api-for-office-365/
However, an official .NET Core port will never happen as EWS is being phased out in favour of Microsoft Graphby the looks of it. So I suspect this can probably be closed.
They just suggest that you shouldn't use it for new applications. It is still supported (and working perfectly fine) for existing applications, but they will remove basic authentication (for obvious reasons) in favor of Oauth tokens.
The version Sherlock has created works great in dotnet core, and has a nuget package. If you don't trust the existing nuget you can build your own from source. Just use this version: https://github.com/sherlock1982/ews-managed-api/
I worded my comment poorly. But they do actually state that they "strongly suggest migrating to Microsoft Graph". So that's what I'm going to focus on.
It's worth noting that in Sherlock's post, he calls out his fork as only supporting login/password for authentication. And the MS blog post I linked to states they are decommissioning basic authentication and it will no longer work for new or existing applications on October 13th, 2020.
Does anyone have a mapping of whats in EWS and whats available in MS Graph APIs? As much as it's great to say you should migrate to MS graph, that is only possible if the APIs you need are there. Perhaps MS could encourage people to move off EWS by showing that it is possible :)
@nickalbrecht I can confirm you can set the service.Credentials to OAuthCredentials with a token got from Azure AD. And it just works.
I’m just stating it here do be clear, I’m not trying to convince you to stay with EWS. Graph is the way to the future.
The future for me will not hold a cloud-based Exchange server. I will have to keep communicating with an on-premise Exchange server. And if on-premise Exchange is phased out, I will investigate migration to a non-Microsoft mail server solution. On premise. So Graph is definitely not the way to the future for me.
@Bart76 we are using the .net core version from sherlock to connect to on-premise Exchange servers and in the past we used it to connect to Office 365 with either basic authentication or OAuth tokens.
To show a little insight, they still run Exchange in the cloud the serve all the Office 365 customers. And they will only decommission the basic auth for Office 365 (oktober 1st 2020). So you can continue to use @sherlock1982 version to connect to Office 365. Just set the credentials to OauthCredentials (see above).
Basic auth deprecation is deferred until futher notice but only for customers that are actively using it for EWS. New customers will have to use OAuth. https://techcommunity.microsoft.com/t5/exchange-team-blog/basic-authentication-and-exchange-online-february-2021-update/ba-p/2111904
To me, for 365 it's best/recommended to switch to Graph API. From experience EWS is poorly designed to cope with distributed CAS and mailbox server architecture in 365 when using Streaming Notifications in particular and especially scaling up to 100s of mailboxes, and it's very complicated. Graph is much better suited and way more performant, even with polling requests.
I know switching to Graph can be a problem with legacy code. I've just been in a position with a big rewrite to finally get Graph working for 365 (It's a lot easier to code than EWS!).
However the major problem is on-premises, which doesn't have Graph API. Hybrid however can be okay if the mailboxes you need to access are in the cloud.
On-prem as far as I can tell only supports Basic Auth. There may be some unofficial ways to hack OAuth into on-prem but would assume it involves Azure AD for the auth and something hacked in the Exchange server to authenticate it, and I don't think Microsoft support it at all. MS would rather on-prem Exchange goes away anyway.
@tjmoore the "hack" you're talking about is actually just a change in configuration on the on-premise front-end exchange server.
But you're right about that it's not supported.
https://stackoverflow.com/a/64576570/639153
@svrooij thanks! I'm moving from a legacy application .NET Framework to a rewrite in .NET Core (and needs to work on both Linux and Windows) and I've broken support into Graph for 365 and on-prem with EWS. The legacy app had OAuth support as we were using EWS at the time for 365, but now we're just targetting on-prem with EWS I was thinking of only supporting basic auth. I can still put in OAuth support, but if the .NET Core library doesn't support it that's an issue. Question really is whether there are many customers who have gone with OAuth for on-prem when it's unsupported (our customers are corporate, enterprise, education etc).