OfficeDev / ews-managed-api

Other
584 stars 316 forks source link

ExchangeImpersonation SOAP header must be present for this type of OAuth token. #245

Closed LaGuillotine closed 3 years ago

LaGuillotine commented 4 years ago

Hi,

I am trying to implement OAuth authentication for our exchange mail daemon. I followed this step by step guide in order to grant full_access_as_app. Authentication and impersonation with the exchange server works fine. However, once I try to subscribe to streaming notifications, find a folder or to create one, this exception is thrown:

Microsoft.Exchange.WebServices.Data.ServiceResponseException : ExchangeImpersonation SOAP header must be present for this type of OAuth token.

The only similar issue I have found so far is #212. Even though it sounds almost identical (weren't it for the negation), all proposed solutions did not work/were not applicable for me.

This is roughly how my code looks:

IConfidentialClientApplication app = ConfidentialClientApplicationBuilder
    .Create("foo-client")
    .WithAuthority(AzureCloudInstance.AzurePublic, "bar-tenant")
    .WithClientSecret("foobar-secret")
    .Build();

AuthenticationResult authResult = app
    .AcquireTokenForClient(new [] { "https://outlook.office.com/.default" })
    .ExecuteAsync()
    .Result;

ExchangeService service = new ExchangeService {
    Credentials = new OAuthCredentials(authResult.AccessToken),
    ConnectionGroupName = Guid.NewGuid().ToString(),
    ImpersonatedUserId = "foo@bar.com"
};

service.AutodiscoverUrl(mailAddress, url => true);

// This throws the exception
StreamingSubscription subscription = service
    .SubscribeToStreamingNotifications(
        new [] { new FolderId(WellKnownFolderName.MsgFolderRoot) },
        EventType.NewMail
    );

These are the HTTP requests and responses that I logged:

Request

<Trace Tag="EwsRequestHttpHeaders" Tid="11" Time="2020-03-17 16:09:04Z">
POST /EWS/Exchange.asmx HTTP/1.1
Content-Type: text/xml; charset=utf-8
Accept: text/xml
User-Agent: ExchangeServicesClient/2.2.1.0
Accept-Encoding: gzip,deflate
X-AnchorMailbox: 
X-PreferServerAffinity: true
Authorization: Bearer eyJ0eXAiOiJKV1QiLCJub25jZSI6ImxL.......

</Trace>

<Trace Tag="EwsRequest" Tid="11" Time="2020-03-17 16:09:04Z" Version="2.2.1.0">
  <?xml version="1.0" encoding="utf-8"?>
  <soap:Envelope xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:m="http://schemas.microsoft.com/exchange/services/2006/messages" xmlns:t="http://schemas.microsoft.com/exchange/services/2006/types" xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/">
    <soap:Header>
      <t:RequestServerVersion Version="Exchange2013_SP1" />
    </soap:Header>
    <soap:Body>
      <m:Subscribe>
        <m:StreamingSubscriptionRequest>
          <t:FolderIds>
            <t:DistinguishedFolderId Id="msgfolderroot" />
          </t:FolderIds>
          <t:EventTypes>
            <t:EventType>NewMailEvent</t:EventType>
          </t:EventTypes>
        </m:StreamingSubscriptionRequest>
      </m:Subscribe>
    </soap:Body>
  </soap:Envelope>
</Trace>

Response

<Trace Tag="EwsResponseHttpHeaders" Tid="11" Time="2020-03-17 16:09:04Z">
HTTP/1.1 500 Internal Server Error
request-id: 029adbf7-96f1-4e21-905d-332a45606945
X-CalculatedFETarget: AM6P192CU001.internal.outlook.com
X-BackEndHttpStatus: 500,500
X-FEProxyInfo: AM6P192CA0005.EURP192.PROD.OUTLOOK.COM
X-CalculatedBETarget: AM0PR0602MB3684.eurprd06.prod.outlook.com
X-RUM-Validated: 1
x-ms-appId: e711e17d-5cf8-441c-96c3-27e3fa8210d0
X-BeSku: WCS5
X-DiagInfo: AM0PR0602MB3684
X-BEServer: AM0PR0602MB3684
X-Proxy-RoutingCorrectness: 1
X-Proxy-BackendServerStatus: 500
X-FEServer: AM6P192CA0005,ZR0P278CA0029
Content-Length: 743
Cache-Control: private

Content-Type: text/xml; charset=utf-8
Date: Tue, 17 Mar 2020 16:09:03 GMT
Set-Cookie: exchangecookie=6f978e7874d84178af5317f3e9550a76; expires=Wed, 17-Mar-2021 16:09:03 GMT; path=/; secure; HttpOnly
Server: Microsoft-IIS/10.0
X-AspNet-Version: 4.0.30319
X-Powered-By: ASP.NET

</Trace>

<Trace Tag="EwsResponse" Tid="11" Time="2020-03-17 16:09:04Z" Version="2.2.1.0">
  <?xml version="1.0" encoding="utf-8"?>
  <s:Envelope xmlns:s="http://schemas.xmlsoap.org/soap/envelope/">
    <s:Body>
      <s:Fault>
        <faultcode xmlns:a="http://schemas.microsoft.com/exchange/services/2006/types">a:ErrorInvalidExchangeImpersonationHeaderData</faultcode>
        <faultstring xml:lang="en-US">ExchangeImpersonation SOAP header must be present for this type of OAuth token.</faultstring>
        <detail>
          <e:ResponseCode xmlns:e="http://schemas.microsoft.com/exchange/services/2006/errors">ErrorInvalidExchangeImpersonationHeaderData</e:ResponseCode>
          <e:Message xmlns:e="http://schemas.microsoft.com/exchange/services/2006/errors">ExchangeImpersonation SOAP header must be present for this type of OAuth token.</e:Message>
        </detail>
      </s:Fault>
    </s:Body>
  </s:Envelope>

</Trace>
wfar commented 4 years ago

I'm having the same issue and can't seem to understand it either. Were you able to figure it out?

pkropachev commented 4 years ago

As far as I remember if Impersonation is enabled on server side then all EWS requests should contain ExchangeImpersonation in SOAP header.

wfar commented 4 years ago

Thanks Pavel, that was exactly it for me as well.

tjmoore commented 4 years ago

I've have the same error switching to OAuth using Application Permissions when doing tasks not specific to a mailbox.

e.g. if I use ResolveName on ExchangeService. At this point I don't require access to a specific mailbox, I just need to resolve a name to get a folder ID. I also get the same with ConvertIds which I'm using for a simple lightweight call for a connectivity check (call something basic to hit the server, tests credentials are okay to access Exchange and gets server version).

Once I want to access a mailbox (calendar in my case) I can set the Impersonation ID to that mailbox which is fine.

However for basic lookups I need an ID also otherwise this error occurs. I assume because I'm no longer logged in as a specific user when I was using basic auth and a service account,

So this means having a dedicated service account to specify as Impersonation ID. One of the benefits of OAuth and Application Permission if granted full access, is no need for a service account any more, but at the moment it looks like I still need one.

tjmoore commented 4 years ago

Update - it seems I can use any mailbox for ResolveName, so it doesn't have to be a service account, just a room mailbox will do (no licence required). However this is annoying for configuration for the end user as they'll be asked to specify a valid email address along with App ID, Tenant ID & Client Secret. Just so the ExchangeImpersonation header gets set to something valid for those kinds of calls.

stokuri commented 4 years ago

For service account without a mailbox license, for OAuth to work, Try constructing ImpersonatedUserId with idType as PrincipalName instead of SmtpAddress

LaGuillotine commented 3 years ago

It seems that I got confused about which mail address to use when specifying the user I want to impersonate. Our company uses two different domains and I was using the wrong one. 🤦‍♂️

@stokuri, I got it to work with idType SmtpAddress. Thanks for the suggestion. @tjmoore, I think you're right, since I was getting a 'No Mailbox' error message when using a wrong mail address.

Also, the guide I followed has been updated since I last checked. Apparently you have to set the X-AnchorMailbox HTTP header when using App-only authentication: ewsClient.HttpHeaders.Add("X-AnchorMailbox", "impersonated@user.com");