Over a million developers have joined DZone.
{{announcement.body}}
{{announcement.title}}

Building a Twitter client for Windows Phone - PIN Authentication

DZone's Guide to

Building a Twitter client for Windows Phone - PIN Authentication

· Mobile Zone
Free Resource

Launching an app doesn’t need to be daunting. Whether you’re just getting started or need a refresher on mobile app testing best practices, this guide is your resource! Brought to you in partnership with Perfecto

Now that we have the basic infrastructure ready, it is time to test the authentication mechanism itself. So we have the basic screen, that asks the user to acquire the PIN. Ultimately, we need to store two values - access token and access token secret, both obtained through the authentication API. As a secondary identifier, we might as well store the Twitter ID that is linked with the application.

For the Acquire PIN button we can easily invoke the OAuthClient-based PerformRequest method. However, we need to introduce some modifications in that mechanism because of the fact that there are multiple requests going through the same method, and we need different asynchronous handling depending on the data we receive.

Inside OAuthClient, create a new enum, called RequestType:

public enum RequestType
{
    InitialToken
}

The main PerformRequest method all of a sudden suffers a change as well - instead of the byte type at the end, I am using RequestType, so the method declaration now looks like this:

public void PerformRequest(Dictionary<string, string> parameters, string url, string consumerSecret, string token, RequestType type)

The GetToken method can now either remain with this name, because it is indeed going to be used to obtain the token, or generalized to GetResponse - all Twitter operations will return a string anyway. So to do some re-using of the existing components, I am going to rename it.

There now appears to be another problem - how exactly am I going to pass the request type to the async callback? After all, the general requirement for a new instance of AsyncCallback is to pass a method with the signature of void (IAsyncResult). Don't forget that you are also passing the AsyncState object - in this case, it is simply the request, but we can make it an object array. Just like this:

request.BeginGetResponse(new AsyncCallback(GetResponse), new object[] { request, type });

Now, the GetResponse itself should handle the data correctly, considering the incoming objects.

static void GetResponse(IAsyncResult result)
{
    HttpWebRequest request = (HttpWebRequest)(((object[])result.AsyncState)[0]);
    HttpWebResponse response = (HttpWebResponse)request.EndGetResponse(result);

    using (StreamReader reader = new StreamReader(response.GetResponseStream()))
    {
        RequestType currentType = (RequestType)(((object[])result.AsyncState)[1]);
        string completeString = reader.ReadToEnd();

        switch (currentType)
        {
            case RequestType.InitialToken:
                {
                    string[] data = completeString.Split(new char[] { '&' });
                    int index = data[0].IndexOf("=");
                    string token = data[0].Substring(index + 1, data[0].Length - index - 1);
                    Debug.WriteLine("TOKEN OBTAINED");

                    WebBrowserTask task = new WebBrowserTask();
                    task.Uri = new Uri("http://api.twitter.com/oauth/authorize?oauth_token=" + token);
                    task.Show();
                    break;
                }
        }
    }
}

Notice that now I am taking the array passed as the state and assign proper objects considering the index passed at the very beginning. The InitialToken type is the request to obtain the token and show the PIN code.

We're now ready to go back to the UI. We need to perform the request for the initial token and show the pin. So here it is:

private void btnAcquire_Click(object sender, RoutedEventArgs e)
{
    Dictionary<string, string> parameters = new Dictionary<string, string>();
    parameters.Add("oauth_consumer_key", AuthConstants.ConsumerKey);
    parameters.Add("oauth_callback", "oob");
    parameters.Add("oauth_signature_method", "HMAC-SHA1");
    parameters.Add("oauth_timestamp", StringHelper.UNIXTimestamp);
    parameters.Add("oauth_nonce", Guid.NewGuid().ToString().Replace("-", ""));
    parameters.Add("oauth_version", "1.0");

    OAuthClient.PerformRequest(parameters, "http://api.twitter.com/oauth/request_token", AuthConstants.ConsumerSecret, string.Empty,OAuthClient.RequestType.InitialToken);
}

According to the OAuth documentation (coupled with what Twitter states), we need the parameters I specified above to request a unique app token. The callback is set to oob - this will ultimately force the PIN screen to show up, since that's the only way MangoTree will authenticate the user.

The timestamp must be in the UNIX time format, so I created a property in the StringHelper class that will always return the correct timestamp.

public static string UNIXTimestamp
{
    get
    {
        return Convert.ToString((int)(DateTime.UtcNow - new DateTime(1970, 1, 1, 0, 0, 0)).TotalSeconds);
    }
}

The nonce should be a unique string, and for now I decided to use a GUID - ultimately, we will change it for security purposes, but for now it's a great unique value to experiment with.

When the request is performed, I am obtaining three values:

  • oauth_token
  • oath_token_secret
  • oauth_callback_confirmed 

I am not as much interested in the callback, as I am interested in the token and the token secret, that will be used to sign future requests. Before I am going to talk about storing these values, let's see what happens when the user proceeds with the operation.

This is where Twitter will tell the user what my intension are with the application. The moment the user clicks on Authorize app with the correct credentials entered, the application will receive all permissions outlined above.

Once done, the PIN will appear on the screen.

The user has to store this code manually - currently, there is no way for me to access the content in IE9. Ultimately, I could use my own WebBrowser component in the app itself, but that will create another unnecessary layer. Once the user copies this PIN, it will be used to perform another request, this time to get the access token.

So the scenario in this case most likely will go like this - the user will get the PIN code and then press the Back button to navigate back to the application and enter the code he just got. All this time, the token and the token secret should be preserved, because we will need them now.

If the application is automatically tombstoned and then it is activated via the back button, we don't need to do anything about the existing values, since those will remain where they were.

What I am going to do is store these values in the App class for now:

That being said, some small modifications should be added to the GetResponse method, to set the correct values. Look at the changes:

case RequestType.InitialToken:
    {
        string[] data = completeString.Split(new char[] { '&' });
        int index = data[0].IndexOf("=");
        App.Token = data[0].Substring(index + 1, data[0].Length - index - 1);
        index = data[1].IndexOf("=");
App.TokenSecret = data[1].Substring(index + 1, data[1].Length - index - 1); WebBrowserTask task = new WebBrowserTask(); task.Uri = new Uri("http://api.twitter.com/oauth/authorize?oauth_token=" + App.Token); task.Show(); break; }

Once I get this data, let's assume that the user entered the PIN. Time for another request.

private void btnConfirm_Click(object sender, RoutedEventArgs e)
{
    Dictionary<string, string> parameters = new Dictionary<string, string>();
    parameters.Add("oauth_consumer_key", AuthConstants.ConsumerKey);
    parameters.Add("oauth_signature_method", "HMAC-SHA1");
    parameters.Add("oauth_timestamp", StringHelper.UNIXTimestamp);
    parameters.Add("oauth_nonce", Guid.NewGuid().ToString().Replace("-", ""));
    parameters.Add("oauth_version", "1.0");
    parameters.Add("oauth_verifier", txtPIN.Text);
    parameters.Add("oauth_token", App.Token);

     OAuthClient.PerformRequest(parameters, "https://api.twitter.com/oauth/access_token", AuthConstants.ConsumerKey, App.Token, OAuthClient.RequestType.AccessToken);
}

There are several things I have to mention here. First and foremost, the verifier parameter is the PIN code obtained by the user. You might've also noticed that I am using RequestType.AccessToken - a new addition to the enum we have in OAuthClient.

Now I simply need to add a custom async callback behavior based on the new type.

case RequestType.AccessToken:
    {
        string[] data = completeString.Split(new char[] { '&' });
        int index = data[0].IndexOf("=");
        App.Token = data[0].Substring(index + 1, data[0].Length - index - 1);
        index = data[1].IndexOf("=");
        App.TokenSecret = data[1].Substring(index + 1, data[1].Length - index - 1);
        index = data[2].IndexOf("=");
        App.UserID = data[2].Substring(index + 1, data[2].Length - index - 1);
        index = data[3].IndexOf("=");
        App.UserName = data[3].Substring(index + 1, data[3].Length - index - 1);
        break;
    }

Notice that I replaced the existing token and token secret with the new ones - these will be the core elements as we go. Also, I added two more static string fields - UserID and UserName, to temporarily store the user identifier (numeric) and his Twitter handle. Once the request completes, with the correct PIN, you are ready to execute any other requests on the Twitter API - the authentication circle is complete.

Keep up with the latest DevTest Jargon with the latest Mobile DevTest Dictionary. Brought to you in partnership with Perfecto.

Topics:

Opinions expressed by DZone contributors are their own.

THE DZONE NEWSLETTER

Dev Resources & Solutions Straight to Your Inbox

Thanks for subscribing!

Awesome! Check your inbox to verify your email so you can start receiving the latest in tech news and resources.

X

{{ parent.title || parent.header.title}}

{{ parent.tldr }}

{{ parent.urlSource.name }}