Wednesday, January 25, 2012

401 errors in Exchange Online using EWS WSDL proxy code and raw HTML


If your developing EWS code against Exchange Online or possibly another large load balanced Hosted Exchange system you may find that a lot of the sample code you see on MSDN or other places that uses EWS Proxy code will always return 401 errors when you try to use it against Exchange Online (The Managed API isn't affected) eg a standard example is something like http://msdn.microsoft.com/en-us/library/bb408524%28v=EXCHG.140%29.aspx. What can cause this problem is the failure to process the exchangecookie in an appropriate way. There is description of what the exchangecookie is used for on http://blogs.msdn.com/b/exchangedev/archive/2011/07/20/client-access-server-affinity-and-network-load-balancing-considerations-for-programmatic-access-to-exchange-online.aspx . Although this says nothing about Authentication if the request object your using be its a SOAP proxy or HttpwebRequest doesn't have the cookiecontainer initialized (which by default they don't) before you submit the first request you'll get a 401. So  basically if you have a normal EWS sample after you set the URL or Credentials make sure you initialize the cookie container.eg.
 
esb.Url = "https://CAS01.contoso.com/EWS/exchange.asmx";

esb..CookieContainer = new CookieContainer();


If your using Raw HTML and a HttpWebRequest make sure you do the same thing before submitting the first request.

A code sample is worth a 1000 words so here's a basic Exchange Online/Office365 Autodiscover and WDSL proxy sample that just does a simple GetFolder to the Inbox.

  1. String MbMailbox = "user@domain.onmicrosoft.com";  
  2. String AutoDiscoURL = "https://autodiscover-s.outlook.com/autodiscover/autodiscover.xml";  
  3. NetworkCredential ncCred = new NetworkCredential("user@domain.onmicrosoft.com","password");  
  4. String EWSURL = null;  
  5. String auDisXML = "<Autodiscover xmlns=\"http://schemas.microsoft.com/exchange/autodiscover/outlook/requestschema/2006\"><Request>" +  
  6.         "<EMailAddress>" + MbMailbox + "</EMailAddress>" +  
  7.         "<AcceptableResponseSchema>http://schemas.microsoft.com/exchange/autodiscover/outlook/responseschema/2006a</AcceptableResponseSchema>" +  
  8.         "</Request>" +  
  9.         "</Autodiscover>";  
  10. System.Net.HttpWebRequest adAutoDiscoRequest = (System.Net.HttpWebRequest)System.Net.HttpWebRequest.Create(AutoDiscoURL);  
  11.   
  12.               
  13. byte[] bytes = Encoding.UTF8.GetBytes(auDisXML);  
  14. adAutoDiscoRequest.ContentLength = bytes.Length;  
  15. adAutoDiscoRequest.ContentType = "text/xml";  
  16. adAutoDiscoRequest.Headers.Add("Translate""F");  
  17. adAutoDiscoRequest.Method = "POST";  
  18. adAutoDiscoRequest.Credentials = ncCred;  
  19.   
  20. Stream rsRequestStream = adAutoDiscoRequest.GetRequestStream();  
  21. rsRequestStream.Write(bytes, 0, bytes.Length);  
  22. rsRequestStream.Close();  
  23. adAutoDiscoRequest.AllowAutoRedirect = false;  
  24. WebResponse adResponse = adAutoDiscoRequest.GetResponse();  
  25. String Redirect = adResponse.Headers.Get("Location");  
  26. if (Redirect != null)  
  27. {  
  28.     adAutoDiscoRequest = (System.Net.HttpWebRequest)System.Net.HttpWebRequest.Create(Redirect);  
  29.     adAutoDiscoRequest.ContentLength = bytes.Length;  
  30.     adAutoDiscoRequest.ContentType = "text/xml";  
  31.     adAutoDiscoRequest.Headers.Add("Translate""F");  
  32.     adAutoDiscoRequest.Method = "POST";  
  33.     adAutoDiscoRequest.Credentials = ncCred;  
  34.     rsRequestStream = adAutoDiscoRequest.GetRequestStream();  
  35. }  
  36. rsRequestStream.Write(bytes, 0, bytes.Length);  
  37. rsRequestStream.Close();  
  38. adResponse = adAutoDiscoRequest.GetResponse();  
  39. Stream rsResponseStream = adResponse.GetResponseStream();  
  40. XmlDocument reResponseDoc = new XmlDocument();  
  41. reResponseDoc.Load(rsResponseStream);  
  42. XmlNodeList pfProtocolNodes = reResponseDoc.GetElementsByTagName("Protocol");  
  43. foreach (XmlNode xnNode in pfProtocolNodes)  
  44. {  
  45.     XmlNodeList adChildNodes = xnNode.ChildNodes;  
  46.     foreach (XmlNode xnSubChild in adChildNodes)  
  47.     {  
  48.         switch (xnSubChild.Name)  
  49.         {  
  50.             case "EwsUrl": EWSURL = xnSubChild.InnerText;  
  51.                 break;  
  52.         }  
  53.     }  
  54. }  
  55. ExchangeServiceBinding esExchangeServiceBinding = new ExchangeServiceBinding();  
  56. esExchangeServiceBinding.Credentials = ncCred;  
  57. esExchangeServiceBinding.Url = EWSURL;  
  58. esExchangeServiceBinding.CookieContainer = new CookieContainer();         
  59. DistinguishedFolderIdType[] dsFolderid = new DistinguishedFolderIdType[1];  
  60. dsFolderid[0] = new DistinguishedFolderIdType();  
  61. dsFolderid[0].Id = DistinguishedFolderIdNameType.inbox;  
  62. GetFolderType gfFolderType = new GetFolderType();  
  63. gfFolderType.FolderIds = dsFolderid;  
  64. gfFolderType.FolderShape = new FolderResponseShapeType();  
  65. gfFolderType.FolderShape.BaseShape = DefaultShapeNamesType.Default;  
  66. GetFolderResponseType gfGetFolderResponse = esExchangeServiceBinding.GetFolder(gfFolderType);  
  67. if (gfGetFolderResponse.ResponseMessages.Items[0].ResponseClass == ResponseClassType.Success) {  
  68.     FolderType Folder = (FolderType)((FolderInfoResponseMessageType)gfGetFolderResponse.ResponseMessages.Items[0]).Folders[0];  
  69.     Console.WriteLine(Folder.DisplayName);  
  70. }  

1 comment:

Anonymous said...

Thank you! I've been trying to figure out why my EWS tool was working fine against on-premise accounts but not against EWS Online accounts. This solved my problem!