I think it should be a quite common request to access the Exchange Server of a company from an application. In our case, the Exchange Server is version 2007, and it is published through ISA Server 2006 (but it might be a ForeFront TMG 2010 as well) for external access. The authentication method is forms-based through HTTPS channel.
Most common way to access Exchange Server 2007 programmatically is Exchange Web Services (EWS). The authentication scenario outlined above requires a few tricks, as you cannot use the standard NetworkCredential directly for authentication.
FBA requires you to post the credentials (like user name and password) to the authentication URL and the response contains the cookie(s) that must be added to forthcoming request for authentication.
My example is based on the code found here:
Access the Exchange store via WebDAV with Form-Based-Authentication turned on [Updated]
There are a few difference, for example, in our case not the owaauth.dll of OWA is used for authentication, but the CookieAuth.dll of ISA.
I’ve got the simple EWS sample for my post from this article:
Using Exchange Web Services 2007: The Basics
As I’ve mentioned above, in my scenario I cannot use NetworkCredential for the web service. Instead, I should get the FBA cookie and add it to the CookieContainer of the web service proxy.
- ExchangeServiceBinding.ExchangeServiceBinding esb = new ExchangeServiceBinding.ExchangeServiceBinding();
- esb.Url = "https://mail.company.com/EWS/Exchange.asmx";
- esb.Credentials = CredentialCache.DefaultCredentials;
- CookieCollection cookies = DoFormbasedAuthentication(esb.Url, new NetworkCredential("user", "password", "doamain"));
- FindItemType fit = new FindItemType();
- fit.ItemShape = new ItemResponseShapeType { BaseShape = DefaultShapeNamesType.Default };
- fit.ParentFolderIds = new DistinguishedFolderIdType[]
- {
- new DistinguishedFolderIdType
- {
- Mailbox = new EmailAddressType{ EmailAddress="user@gcompany.com"},
- Id = DistinguishedFolderIdNameType.calendar
- }
- };
- // add cookies to WS
- esb.CookieContainer = new CookieContainer();
- esb.CookieContainer.Add(cookies);
- fit.Traversal = ItemQueryTraversalType.Shallow;
- FindItemResponseType firt = esb.FindItem(fit);
In the DoFormbasedAuthentication method we get the cookie required for FBA authentication.
- private CookieCollection DoFormbasedAuthentication(String url, NetworkCredential credential)
- {
- String server;
- HttpWebRequest request;
- HttpWebResponse response;
- byte[] body;
- Stream stream;
- try
- {
- Uri uri = new Uri(url);
- // Get the server portion of the requested uri and appen the authentication dll from Exchange
- server = String.Format("{0}://{1}/{2}", uri.Scheme, uri.DnsSafeHost, "CookieAuth.dll?Logon");
- // Initiate a new WebRequest to the given URI.
- request = (HttpWebRequest)System.Net.WebRequest.Create(server);
- request.Method = "POST";
- request.CookieContainer = new CookieContainer();
- request.ContentType = "application/x-www-form-urlencoded";
- // Prepare the body of the request
- body = Encoding.UTF8.GetBytes(string.Format("curl=Z2F&flags=0&forcedownlevel=0&formdir=6&trusted=4&username={1}\\{2}&password={3}",
- uri, credential.Domain, credential.UserName, credential.Password));
- request.ContentLength = body.Length;
- // Send the request to the server
- stream = request.GetRequestStream();
- stream.Write(body, 0, body.Length);
- stream.Close();
- // Get the response
- response = (HttpWebResponse)request.GetResponse();
- // Check if the login was successful
- if (response.Cookies.Count < 1) throw
- new AuthenticationException("Failed to login to ISA. Be sure your user name, domain name and password are correct.");
- return response.Cookies;
- }
- catch (AuthenticationException ex)
- {
- throw;
- }
- catch (Exception ex)
- {
- throw new Exception("Failed to login to OWA. The following error occured: " + ex.Message, ex);
- }
- }
You can see that I have included several form parameter beyond the credentials when assembling the request body. These parameters are hidden HTTP form fields or other form input fields from the source of the authentication page. You can verify that if you try to access the page using a browser and checking its source.
To add some really SharePoint specific content to this post either, you might need similar tricks when accessing SharePoint web services (like the other EWS: Excel Web Services) on a FBA site. In this case you have to use the Login method of the Authentication web service to get the cookies you need as illustrated in this article:
Hi,
Do I need to enable persistent cookies in the ISA server for it to work? I tried the above code but instead of accessing an exchange server service Im trying to access a Sharepoint 2010 web service. On fiddler, only one cookie is returned, and I assume its a sharepoint cookie. What I need is the cookie used by ISA server.
Thanks!
Comment by Ernie Balbaligo — February 14, 2011 @ 09:35
Hi Ernie,
A few weeks after posting my code it began to malfunction, so I’ve updated it to handle the new situation. I’ve tried to find the reason, but got not too much info from the Exchange / ISA admins. Might be that persisten cookies were allowed for a short time, and later they changed this setting, as it is usually not a very secure solution. As my time allows I will post the updated solution that will hopefully help you.
Peter
Comment by Peter Holpar — February 16, 2011 @ 19:30
Hi Ernie,
Sorry for the late answer! I’ve checked my updated code, and found the only significant difference is in the DoFormbasedAuthentication method. To get the cookie from the ISA, I had to include the following line after preparing the request:
request.AllowAutoRedirect = false;
Let me know if it works for you.
Peter
Comment by Peter Holpar — May 3, 2011 @ 07:59
Hi Peter,
first of all thanks for this, if I actually manage to make it work it will save me a great deal of work and dirty workarounds.
I tried this piece of code but I get an error code 400 “Bad Request”.
Here’s the body I used:
curl=Z2FOWAZ2F&flags=0&forcedownlevel=0&formdir=3&trusted=0&username={1}\{2}&password={3}&SubmitCreds=Log+On
Do you have any idea why am I getting this error?
Thanks,
Roberto.
Comment by Roberto Santoro — May 2, 2011 @ 20:22
Hi Roberto,
I’ve got the same result (400 – Bad Request) when using curl=Z2FOWAZ2F, try simply curl=Z2F.
Hope it helps.
Peter
Comment by Peter Holpar — May 3, 2011 @ 07:52
P.S.: don’t worry about the trailing “SubmitCreds=Log+On”, I tried with and without it and I get the same error.
Comment by Roberto Santoro — May 2, 2011 @ 20:24
The solution was the answer you already gave to Ernie:
request.AllowAutoRedirect = false
Thanks a lot for your help
Roberto.
Comment by Roberto Santoro — May 3, 2011 @ 15:09
Hi Roberto,
I’m glad that my other answer helped (actually it was posted a few minutes later I replied your question), however, missing that line throws a different exception for me: 403 – Forbidden.
Peter
Comment by Peter Holpar — May 4, 2011 @ 11:17
Yeah, I was getting both 403 and 400 depending on how I configured the body of the request.
I don’t know why I was convinced the body that was getting 403 was not the correct one and I was trying to correct the 400 instead.
Thanks a lot for this, it really helped me a lot (the alternative was to get ISA to treat EWS differently from the other folders which I don’t even know whether is possible).
Comment by Roberto Santoro — May 4, 2011 @ 15:56
Hello,
how often do you think we can check for emails without risking to overload the exchange server?
Thanks,
Roberto.
Comment by Roberto Santoro — May 7, 2011 @ 08:01
Hi Roberto,
I assume it depends on the performance and other load / traffic on of your exchange server and the size of the mail storage. You should make some experiments while monitoring performance to get the answer for your question.
Peter
Comment by Peter Holpar — May 7, 2011 @ 21:44
Hi,
I am trying to do Form Based Authentication from Java. So I am trying the same way. I am using Exchange server 2010 for testing.
But I am not able find “CookieAuth.dll”. If I access “http:///Cookie/Auth.dll”, I am getting 404 “Not Found” Error.
Is there something that I should in the Exchange server to to make CookieAuth.dll available?
But if I access, the URL “http:///owa/auth/owaauth.dll”, then I am getting some Cookies back in the response.
If I use owaauth.dll, I have to pass “destination”, “username” and “Password” as the form parameters in the POST request.
What will be the value of “destination” in Exchange server 2010? Will it be “http:///owa”? Or some thing else?
Once I have the cookies, I should send them along with EWS request. So I send a request with the cookie data like :
POST /owa HTTP/1.1
Content-type: text/xml; charset=utf-8
User-Agent: ExchangeServicesClient/0.0.0.0
Accept: text/xml
Keep-Alive: 300
Connection: Keep-Alive
Accept-Encoding: gzip,deflate
Cookie: OutlookSession=c9748798e2f840b49e5e2df4ae611e06; path=/; HttpOnly, sessionid=9c3446f0-9e46-4867-9ad1-3cb73944f2be; path=/, cadata=”1aJ0Vpf3Aux0aoFhNQdA1gvzZtd1Td9ZptRU7LHL8yEYWaKoox9z99IeN3no/xlGzwdq03WoSft1BCkkVrf9vsbao+CrFwc2JcY6xvXx6uRLFf5QqSHpVaHy/o2EyHo5D”; HttpOnly; path=/, OutlookSession=c9748798e2f840b49e5e2df4ae611e06; path=/; HttpOnly, sessionid=9c3446f0-9e46-4867-9ad1-3cb73944f2be; path=/; path=/
Host: 10.192.37.30:1234
Content-Length: 630
AllProperties
But the response returned is
HTTP/1.1 301 Moved Permanently
Content-Length: 0
Location: /owa/
Server: Microsoft-IIS/7.5
X-Powered-By: ASP.NET
X-UA-Compatible: IE=EmulateIE7
Date: Thu, 15 Sep 2011 22:33:46 GMT
Connection: close
Could you please tell me what I am doing wrong.
Thanks,
Paul
Comment by Paul — November 18, 2011 @ 14:58