exchangewebservicesews-managed-api

I get "401 Unauthorized" error when "UseDefaultCredentials" is set to True


When I specify username and password in the call in WebCredentials my calls to EWS work fine. If I set the ExchangeService.UseDefaultCredentials property to True, then I get the 401 Unauthorized error.

I don't know if this is important, but our Exchange admin tells me that I'm working in a hybrid environment. We are running both Exchange Online, and Exchange 2010 (or 2013, I think).

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Microsoft.Exchange.WebServices.Data;
using System.Security.Cryptography.X509Certificates;
using System.Net; 

namespace EWSLib
{
    class Program
    {
        static ExchangeService _service;
        static void Main(string[] args)
        {
            string defaultEmail = System.DirectoryServices.AccountManagement.UserPrincipal.Current.EmailAddress;            
            ServicePointManager.ServerCertificateValidationCallback = CertificateValidationCallBack;


            try
            {
                _service = new ExchangeService();

                Appointment meeting = new Appointment(_service);

                //-----------------------------------------------
                // If I comment out
                // "_service.UseDefaultCredentials = true" 
                // and uncomment the line "_service.Credentials = new WebCredentials("user@company.com", "password")"
                // Then Everything works fine.
                //------------------------------------------------
                _service.UseDefaultCredentials = true;
                //_service.Credentials = new WebCredentials("user@company.com", "password");


                _service.TraceEnabled = true;
                _service.TraceFlags = TraceFlags.All;
                //_service.AutodiscoverUrl(defaultEmail, RedirectionUrlValidationCallback);
                _service.Url = new Uri("https://example.com/EWS/Exchange.asmx");

                // TEST MEETING
                meeting.Subject = "The subject";
                meeting.Body = "The boddy";
                meeting.Start = DateTime.UtcNow.AddDays(5);
                meeting.End = DateTime.UtcNow.AddDays(5).AddHours(1);
                meeting.Location = "Someplace";
                meeting.IsReminderSet = true;



                meeting.RequiredAttendees.Add(defaultEmail);
                meeting.ReminderMinutesBeforeStart = 60;


                // Delegation
                meeting.Save(new FolderId(WellKnownFolderName.Calendar, "delegator@company.com"), SendInvitationsMode.SendToAllAndSaveCopy);



                // Verify that the meeting was created.
                Item item = Item.Bind(_service, meeting.Id, new PropertySet(ItemSchema.Subject));




            }
            catch (Exception e)
            {
                //System.Diagnostics.Debug.WriteLine(e);
                Console.WriteLine(e);
            }

            Console.ReadLine();
        }

        private static bool RedirectionUrlValidationCallback(string redirectionUrl)
        {
            // The default for the validation callback is to reject the URL.
            bool result = false;
            Uri redirectionUri = new Uri(redirectionUrl);
            // Validate the contents of the redirection URL. In this simple validation
            // callback, the redirection URL is considered valid if it is using HTTPS
            // to encrypt the authentication credentials. 
            if (redirectionUri.Scheme == "https")
            {


                result = true;
            }
            return result;
        }

        private static bool CertificateValidationCallBack(
    object sender,
        System.Security.Cryptography.X509Certificates.X509Certificate certificate,
    System.Security.Cryptography.X509Certificates.X509Chain chain,
    System.Net.Security.SslPolicyErrors sslPolicyErrors)
        {
            // If the certificate is a valid, signed certificate, return true.
            if (sslPolicyErrors == System.Net.Security.SslPolicyErrors.None)
            {
                Console.WriteLine("Valid Signed Certificate");
                return true;
            }

            // If there are errors in the certificate chain, look at each error to determine the cause.
            if ((sslPolicyErrors & System.Net.Security.SslPolicyErrors.RemoteCertificateChainErrors) != 0)
            {
                if (chain != null && chain.ChainStatus != null)
                {
                    foreach (System.Security.Cryptography.X509Certificates.X509ChainStatus status in chain.ChainStatus)
                    {
                        if ((certificate.Subject == certificate.Issuer) &&
                       (status.Status == System.Security.Cryptography.X509Certificates.X509ChainStatusFlags.UntrustedRoot))
                        {
                        // Self-signed certificates with an untrusted root are valid. 
                            Console.WriteLine("Self-signed certificates with an untrusted root are valid.");
                            continue;
                        }
                        else
                        {
                            if (status.Status != System.Security.Cryptography.X509Certificates.X509ChainStatusFlags.NoError)
                            {
                                // If there are any other errors in the certificate chain, the certificate is invalid,
                                // so the method returns false.
                                Console.WriteLine("there are any other errors in the certificate chain");
                                return false;
                            }
                        }
                    }
                }

                // When processing reaches this line, the only errors in the certificate chain are 
                // untrusted root errors for self-signed certificates. These certificates are valid
                // for default Exchange server installations, so return true.
                Console.WriteLine("Valid for some reason");
                return true;
            }
            else
            {
                // In all other cases, return false.
                Console.WriteLine("there are any other errors in the certificate chain");
                return false;
            }
        }
    }
}

Solution

  • If your mailboxes are on Office365 then I would suggest you use Oauth instead. As basic auth is going away next year for EWS https://blogs.technet.microsoft.com/exchange/2018/07/03/upcoming-changes-to-exchange-web-services-ews-api-for-office-365/ .

    If you use oAuth then you can use windows integrated Auth with the ADAL library https://github.com/AzureAD/azure-activedirectory-library-for-dotnet/wiki/AcquireTokenSilentAsync-using-Integrated-authentication-on-Windows-(Kerberos) to take advantage for the currently logged on user.