Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Support for .Net Core #96

Open
xsi640 opened this issue Aug 9, 2016 · 36 comments
Open

Support for .Net Core #96

xsi640 opened this issue Aug 9, 2016 · 36 comments

Comments

@xsi640
Copy link

xsi640 commented Aug 9, 2016

support using the ews-mananged-api from applications built for .net core

@svrooij
Copy link

svrooij commented Jan 23, 2017

@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?

@sherlock1982
Copy link

sherlock1982 commented Jan 23, 2017

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.

@svrooij
Copy link

svrooij commented Jan 23, 2017

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!

@sherlock1982
Copy link

It's here: https://www.nuget.org/packages/Microsoft.Exchange.WebServices.NETStandard/1.0.4

@lgkonline
Copy link

@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.

@sherlock1982
Copy link

sherlock1982 commented Jan 25, 2017

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.

@lgkonline
Copy link

@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.

@sherlock1982
Copy link

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.

@ststeiger
Copy link

ststeiger commented May 18, 2018

@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.

@sherlock1982
Copy link

sherlock1982 commented May 18, 2018

@ststeiger issue with authentication should be fixed with a workaround we made already.
This is here sherlock1982#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.

@ststeiger
Copy link

ststeiger commented May 18, 2018

@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.

@ststeiger
Copy link

ststeiger commented May 20, 2018

@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");

@svrooij
Copy link

svrooij commented May 22, 2018

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.
d03be7ed-6362-49df-9bc1-3dcf9d41d9f7

@ststeiger
Copy link

ststeiger commented May 22, 2018

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.

@ststeiger
Copy link

ststeiger commented May 24, 2018

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.

@mackcoding
Copy link

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.

@ststeiger
Copy link

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.

@jmosquedah
Copy link

jmosquedah commented Jun 5, 2018

@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

@ststeiger
Copy link

ststeiger commented Jun 6, 2018

@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));

Source

@ststeiger
Copy link

ststeiger commented Jun 14, 2018

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).(ststeiger/RedmineMailService@b2bf15b).

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.

@nickalbrecht
Copy link

nickalbrecht commented Aug 15, 2019

.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/

@svrooij
Copy link

svrooij commented Aug 15, 2019

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/

@nickalbrecht
Copy link

nickalbrecht commented Aug 15, 2019

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.

@LoungeFlyZ
Copy link

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 :)

@svrooij
Copy link

svrooij commented Aug 15, 2019

@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.

@Bart76
Copy link

Bart76 commented Mar 3, 2020

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.

@svrooij
Copy link

svrooij commented Mar 3, 2020

@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).

@tjmoore
Copy link

tjmoore commented Aug 12, 2021

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.

@svrooij
Copy link

svrooij commented Aug 12, 2021

@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

@tjmoore
Copy link

tjmoore commented Aug 12, 2021

@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).

@svrooij
Copy link

svrooij commented Aug 12, 2021

We are just running a modified version of this library, where we supply out with our own http client (with default headers set).

Not using it any more though, everybody is migrated to the cloud. Just saying it is possible.

Depreciation of basic auth is cloud only anyway.

@tjmoore
Copy link

tjmoore commented Aug 12, 2021

Yeah, I'm looking at .NET Core libraries at the moment, though there are a lot of forks.

In particular https://github.com/sherlock1982/ews-managed-api and https://github.com/OfficeDevUnofficial/ews-managed-api

Not sure what's best though, but they seem to be ahead of the official so assume they have all the current fixes. Only concern is on Linux as @sherlock1982 mentions LDAP and DNS autodiscover issues. In our legacy app I actually defaulted to not using autodiscover for 365 as it was causing a lot of problems likely due to customers not having DNS set up correctly and can use standard 365 endpoints anyway. For on-prem though autodiscover is more useful especially in a large multi-server Exchange deployment.

@deeprobin
Copy link

ping @OfficeDev

@sherlock1982
Copy link

sherlock1982 commented Mar 30, 2022

@deeprobin I don't exactly remember why I turned off autodiscover - but I guess it didn't make any sense in Linux environment. My main purpose was to make the lib async because I wanted to have multiple notif. channels at once with different users.
I also merged latest changes from this uplink

@MichelZ
Copy link

MichelZ commented Mar 30, 2022

@deeprobin You will not get an answer. See the README file, this project (EWS Managed Library) from Microsoft is DEAD. If you want to keep using it, you should use one of the forks.

@ststeiger
Copy link

@deeprobin:
.NET Core does support autodiscover.
They have ported the respecitive LDAP-library to Linux.
The only problem is with TimeZone mapping between Linux and Windows.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests