One of my current areas of interest is Twitter and how it can be integrated into applications to support more socially aware applications. Twitter uses oAuth to authenticate its services, thus along with my interest in Twitter I have also developed an interest in oAuth. This blog entry will describe the method I used with a Twitter app I wrote as an experiment for Windows Phone 7. To assist me in access the REST APIs of Twitter, I used the Hammock REST Client Library.
To begin, any attempt to authenticate requires a Request Token from the Twitter service to ensure you have the rights to request authentication. This is done with the following code.
1: public void RequestToken(string consumerKey, string consumerSecret)
2: {
3: var oauth = GetBaseWorkflow(consumerKey, consumerSecret);
4: oauth.RequestTokenUrl = Constants.REQUEST_TOKEN_URL;
5: oauth.CallbackUrl = Constants.CALLBACK_URL;
6:
7: var info = oauth.BuildRequestTokenInfo(WebMethod.Get);
8: var oAuthQuery = CreateRequestForQueryInfo(info);
9:
10: oAuthQuery.QueryResponse += (ob, ev) =>
11: {
12: var parameters = ev.Response.GetQueryStringParameters();
13: onTokenRequestComplete(parameters["oauth_token"]);
14: };
15:
16: oAuthQuery.RequestAsync(Constants.REQUEST_TOKEN_URL, null);
17: }
The parameters to this function are the consumer key and consumer secret values which are provided by Twitter which identify the application.
The thing to note in this code block is the callback URL. This value will play a significant role in the future code.
What this code block returns is an AuthorizeURL that will allow a user to authorize the application to access their Twitter account. If you notice the QueryResponse portion, this is an event that fires an event which is listened to by the outside code base. The event passes us the URL and we use a WebBrowser control to display the Authorize page.
The updated screen with the URL is shown below within a WebBrowser control.
This way the authentication information can be inputted by the user. As a rule with oAuth we never store passwords, but instead we store a access token and its secret token to authenticate our requests.
At this point, Twitter handles the processing and noting that our application has the rights to access your Twitter account. Once Twitter, completes this process, it will send us to our callback URL, which will be intercepted by our WebBrowser control as shown below:
1: private void webBrowser_Navigated(object sender,
2: NavigationEventArgs e)
3: {
4: progressBar.Visibility = Visibility.Collapsed;
5: if (e.Uri.ToString().Contains(Constants.CALLBACK_URL))
6: {
7: var parameters = e.Uri.Query
8: .GetQueryStringParameters();
9: AuthorizeToken(parameters["oauth_token"],
10: parameters["oauth_verifier"]);
11: webBrowser.Visibility = Visibility.Collapsed;
12: progressBar.Visibility = Visibility.Collapsed;
13: }
14: }
Basically, if we see the callback URL being used we understand the process to be complete and we can move to the next stage. When this is seen we access our Query String and pass the access token (represented by oauth_token) and the verifier (represented b oath_verifier) to our AuthorizeToken function, shown below:
1: private void AuthorizeToken(string accessToken, string verifier)
2: {
3: _authorizationWrapper.ValidateAccessTokenComplete += ev =>
4: Dispatcher.BeginInvoke(() =>
5: {
6: ((App)Application.Current).Context =
7: new AuthorizationResult
8: {
9: AccessToken = ev.Token,
10: AccessTokenSecret = ev.TokenSecret,
11: ScreenName = ev.ScreenName
12: };
13:
14: NavigationService.Navigate(
15: new Uri("/ConfirmAccount.xaml",
16: UriKind.RelativeOrAbsolute));
17: });
18:
19: _authorizationWrapper.ValidateAccessToken(
20: Constants.CONSUMER_KEY, Constants.CONSUMER_SECRET,
21: accessToken, verifier);
22: }
As you can see, we are actually receiving the username from Twitter and not from any input from the user to our application. This ensures that we are upholding the principal points of oAuth which is we never store passwords or personal login information, we are leveraging a third party to handle the authentication.
Once this process is complete, we return to the process of the application itself, through a redirect to ConfirmAccount.xaml. One of the things I am working on is a pattern for easily storing data, either permanently or temporarily, in a place that can be accessed from request to request. In this case we are calling it context and storing the result of authorization. This accessed on the confirmation page by the following code, which saves the data to Isolated Storage through my custom ORM framework:
1: protected override void OnNavigatedTo(NavigationEventArgs e)
2: {
3: var result = GetContext();
4: viewModel.ScreenName = result.ScreenName;
5: viewModel.AccessToken = result.ScreenName;
6: viewModel.AccessTokenSecret = result.AccessTokenSecret;
7: }
8:
9: private void btnConfirm_Click(object sender, RoutedEventArgs e)
10: {
11: viewModel.SaveAccount();
12: NavigationService.Navigate(new Uri("/Accounts.xaml",
13: UriKind.RelativeOrAbsolute));
14: }
This completes the save process and gives us the ability to use oAuth to authenticate requests to the service. While the matter of using my own XAML page to handle authorization is a nice to have, in some ways, directing user to Twitter allows them to feel more at ease when entering this sensitive information. Also, with the way this is designed, I can assure myself that I am not storing any of this sensitive information, which is the benefit of using a third party authorization technique like oAuth.