Login to Box Using a Windows Dialog
This article shows how to login to a cloud storage like Box by entering the credentials in a dialog window rather than in a Web page.
Introduction
Many cloud storage providers like www.box.com use an authentication mechanism that directs you to a WEB page to enter your credential. If you develop a Web based application this is a natural way to do that, but in the case of a desktop application or a windows store App you would want to provide your own login screen that would be a windows form if you are writing a desktop application.
I have searched the forums for a solution to that problem but I failed to find an end to end solution. However some posts guided me to the solution I'm presenting in this article.
The method I describe is valid with the version 1 of the Box API. They have just release a V2 where the authentication mechanism is now using OAuth 2.0, but the current mechanism I describe is still valid.
Remark: You will need to register as a developer with Box.com as this code needs an API_KEY that you obtain after registering. This is a free process. This code uses the discontinued free library BoxSync that implements in C# the Box API v1.
WEB form submission with WebClient
My first solution to enter the credentials in a windows dialog was to put a IE control in a dialog box and pass it the URL of the Box login screen. The drawback of that solution is that once the WEB page displays the successful login, you still need to press the OK button of the dialog box.
The support of Box told me that they don't have any API to
authenticate a user with its username/password, so I looked for a
solution that would be to automate the login by submitting the
authentication form from a C# program. After all, a browser is built
on top of HTTP protocols that are accessible to a program using classes
like WebRequest
or WebClient
.
I could find some useful posts that demonstrate how to submit a form
using the WebClient
client but there is more than just
passing the data
to the submit call, in the case of a real authentication protocol like
the one of Box you need to understand exactly what data they submit to
the server when you click Login.
The final code to submit the login form to Box is the following.
public bool AuthenticateUser(string ticket, string userName, string password, out string requestToken) { bool authSuccess = false; requestToken = string.Empty; string url = string.Format("http://www.box.net/api/1.0/auth/{0}", ticket); // Use a WebRequest to process the login Stream respStream = ExecuteREST_Request(url, Method.GET); StreamReader reader = new StreamReader(respStream); string responseText = reader.ReadToEnd(); requestToken = ExtractRequestToken(responseText); using (WebClient webClient = new WebClient()) { try { NameValueCollection formFields = new NameValueCollection(); formFields.Add("login", userName); formFields.Add("password", password); formFields.Add("_pw_sql", ""); formFields.Add("remember_login", "on"); formFields.Add("__login", "1"); formFields.Add("dologin", "1"); formFields.Add("reg_step", ""); formFields.Add("submit1", "1"); formFields.Add("folder", ""); formFields.Add("skip_framework_login", "1"); formFields.Add("login_or_register_mode", "login"); formFields.Add("new_login_or_register_mode", ""); formFields.Add("request_token", requestToken); webClient.Proxy = null; string actionUrl = string.Format("https://www.box.net/api/1.0/auth/{0}", ticket); byte[] result = webClient.UploadValues(actionUrl, METHOD_POST, formFields); string htmlText = ASCIIEncoding.ASCII.GetString(result); if (CheckAuthenticated(htmlText)) { authSuccess = true; } else { requestToken = ExtractRequestToken(htmlText); authSuccess = false; } } catch (WebException ex) { Trace.WriteLine(ex.Message); } } return authSuccess; }
If you look at this code, and especially at the part that is preparing the parameters for the submit request, you will notice that there are many more parameters than the user name and password.
That's the interesting part of the problem, when you want to submit a
particular form using WebClient
for instance, you need to
know what the
submit request contains exactly when you press the submit button!
Analyzing the submit request
The first step you need to do is to analyze the source of the WEB that contains the Login form. From that source you will extract the URL where to post the data. You'll find this URL in the Action of the form. In the case of Box, the URL is the following one.
https://www.box.net/api/1.0/auth/<ticket>
The ticket is an identification string that you get when initializing the authentication process with Box.
Once you have the URL where to submit the form, you need to identify all the parameters that need to be passed with the request. You can identify them by looking at the source of the page but it may not be easy to rebuild the request and in the case of an authentication, only one bit is enough to fail it.
I used a HTTP debugging tool to see the exact request which is sent to the server. The tool I found is Fiddler Web Debugger that you can download on their web page. This tool proved very useful as it is able to monitor an https request which is what I needed to do as the Box login url is on https.
Using Fiddler configured to monitor the https traffic I could see the exact content of the requested submitted to the Box server for the authentication. I could see that there were many other fields than the user/password and one particularly attracted my attention. This field is request_token and its value is a string token that is different for every login.Fortunately I could easily find in the source of the page where the value was set and it is in a piece of script. I wrote a simple method to extract that value from the login page and used it to build the parameters.
The last step of this authentication method is to check whether the
authentication was successful or not. Once again you need to analyze
the response from the request submission in order to know the outcome.
In the page that is returned when the operation is successful I found
the following string that gives me the proper information: api_auth_success
.
So when I submit the form I simply check if that string is present in the response page and I can decide if it is successful or not.