How to authenticate with OAuth to access EWS APIs

Viewed 9322

My web service is currently doing basic username/password authentication in order to subscribe the exchange user for receiving the events (like new mail event etc) like below:

var service = new ExchangeService(exchangeVersion)
                                  {
                                      KeepAlive = true,
                                      Url = new Uri("some autodiscovery url"),
                                      Credentials = new NetworkCredential(username, password)
                                  };

var subscription = service.SubscribeToPushNotifications(
                                    new[] { inboxFolderFoldeID },
                                    new Uri("some post back url"),
                                    15,
                                    null,
                                    EventType.NewMail,
                                    EventType.Created,
                                    EventType.Deleted,
                                    EventType.Modified,
                                    EventType.Moved,
                                    EventType.Copied);

Now, I am supposed to replace the authentication mechanism to use OAuth protocol. I saw some examples but all of them seem to be talking about authenticating the client (https://msdn.microsoft.com/en-us/library/office/dn903761%28v=exchg.150%29.aspx?f=255&MSPPError=-2147217396) but nowhere I was able to find an example of how to authenticate an exchange user with OAuth protocol. Any code sample will help a lot. Thanks.

3 Answers

Using the example code in this Microsoft Document as the starting point and these libraries:

  • Microsoft Identity Client 4.27
  • EWS Managed API v2.2

I am able to successfully authenticate and connect with Exchange on Office 365.

      public void Connect_OAuth()
      {
         var cca = ConfidentialClientApplicationBuilder
               .Create          ( ConfigurationManager.AppSettings[ "appId" ] )
               .WithClientSecret( ConfigurationManager.AppSettings[ "clientSecret" ] )
               .WithTenantId    ( ConfigurationManager.AppSettings[ "tenantId" ] )
               .Build();

         var ewsScopes = new string[] { "https://outlook.office365.com/.default" };

         AuthenticationResult authResult = null;

         try
         {
            authResult = cca.AcquireTokenForClient( ewsScopes ).ExecuteAsync().Result;
         }
         catch( Exception ex )
         {
            Console.WriteLine( "Error: " + ex );
         }

         try
         {
            var ewsClient = new ExchangeService();

            ewsClient.Url                = new Uri( "https://outlook.office365.com/EWS/Exchange.asmx" );
            ewsClient.Credentials        = new OAuthCredentials( authResult.AccessToken );
            ewsClient.ImpersonatedUserId = new ImpersonatedUserId( ConnectingIdType.SmtpAddress, "ccc@pppsystems.co.uk" );

            ewsClient.HttpHeaders.Add( "X-AnchorMailbox", "ccc@pppsystems.co.uk" );


            var folders = ewsClient.FindFolders( WellKnownFolderName.MsgFolderRoot, new FolderView( 10 ) );

            foreach( var folder in folders )
            {
               Console.WriteLine( "" + folder.DisplayName );
            }
         }
         catch( Exception ex )
         {
            Console.WriteLine( "Error: " + ex );
         }
      }

The Microsoft example code did not work - the async call to AcquireTokenForClient never returned.

By calling AcquireTokenForClient in a separate try catch block catching a general Exception, removing the await and using .Result, this now works - nothing else was changed.

I realise that this is not best practice but, both with and without the debugger, the async call in the original code never returned.

In the Azure set-up:

  • A client secret text string was used - a x509 certificate was not necessary
  • The configuration was 'app-only authentication'

Hope this helps someone avoid hours of frustration.

Related