white keyboard with gold and silver lock sitting on top

How to Use a Twitter-Based ASP.NET MVC Security Principal

Authenticate web app users via Twitter and save secrets to ASP.NET MVC cookies

In "Lazy Authentication with Twitter," I presented a simple but effective strategy to add Twitter authentication to your website. Instead of creating and maintaining your users' data store, you can rely on a third-party service to handle open authentication. In that article, and described in more detail in Programming Microsoft ASP.NET MVC, Second Edition (Microsoft Press), I used the DotNetOpenAuth library to deal with the intricacies of the OAuth protocol (used by most popular social networks) and discussed how to authenticate users and create a plain ASP.NET authentication cookie for any successive access. To be precise, the DotNetOpenAuth library comes with an extension method that helps in creating the cookie.

More recently, I embarked on a large project that uses website functionality to provide data feeds to a variety of mobile front ends and social networks. The social part of the project addresses a slightly different issue that the aforementioned article doesn't cover: sending automatic updates to a few given Twitter accounts. Although this isn't really a brand-new feature that no one has ever implemented, surprisingly I couldn't find a well done end-to-end example that doesn't require a deep understanding of the Twitter implementation of the OAuth protocol -- thus, my inspiration for the topic of this article. Here I will show you a sample ASP.NET MVC application that allows users to log in with their Twitter account and then post updates.

Enter TweetSharp

For the purposes of this demo, I'm not using DotNetOpenAuth to deal with the Twitter API. Instead I'm using the TweetSharp library, which I added to my project via NuGet. Note that the library comes with a couple of dependencies (Hammock for REST operations and Newtonsoft.Json for JSON parsing), so adding the TweetSharp library through NuGet simplifies management a bit. TweetSharp has many more ready-made methods to authenticate users, verify credentials, and post updates.

The main benefit of TweetSharp is that it reduces the task of sending tweets automatically to just a few lines of self-explanatory code. Figure 1 shows the user interface of the sample application, webTWEETing.

Figure 1: The sample application in action ready to connect to Twitter
Figure 1: The sample application in action ready to connect to Twitter

Let's assume that your application is registered with Twitter and fully configured. Pay attention to requesting write access to the accounts; otherwise, you won't be able to post updates programmatically. Registering the application with Twitter gives you a couple of essential strings—the consumer key and the secret. These strings should be passed to the TweetSharp API before attempting to access the Twitter API.

Connecting to Twitter

As you can guess from Figure 1, the sample ASP.NET MVC application has a TwitterController class with a Login method. When users invoke this action, they actually trigger the process that displays an authentication Twitter page, shown in Figure 2. On this page, users enter their Twitter credentials and explicitly authorize your application to operate on their account.

Figure 2: User authorization for the webTWEETing sample application
Figure 2: User authorization for the webTWEETing sample application

Let's examine what it takes to move from the page of Figure 1 to the page of Figure 2. Figure 3 shows the implementation of the Login method.

public ActionResult Login()
{
// Access the Twitter service via TweetSharp
  var service = new TwitterService(consumerKey, consumerSecret);

// Get a request token from Twitter
  var callbackUrl = "http://.../twitter/success";
  var requestToken = service.GetRequestToken(callbackUrl);

  // Present the request token so that Twitter will redirect to the authorization page
  var uri = service.GetAuthorizationUri(requestToken);
  return new RedirectResult(uri.ToString());
}

The TwitterService class that you see in Figure 3 belongs to the TweetSharp library and represents the entry point in the library. You get an instance of the service by providing your application's consumer key and secret. The Login method only triggers the process; another controller method will be responsible for finalizing the process. And yet another method will be required to post an update. For each interaction, you need to use an instance of the TwitterService class. For this reason, you might want to store the consumer key and secret strings in a place that is easy to reach programmatically.

You should be able to understand the OAuth workflow quite easily from Figure 3. First, the code places a request to Twitter to get a request token. The request token incorporates the callback URL, namely where Twitter will redirect the browser after the user has entered credentials. The callback URL is where you want the browser to go after the user clicks the Authorize app button shown in Figure 2. In Figure 3, the callback URL refers to the Success method on the Twitter controller class, shown in Figure 4.

public ActionResult Success(String oauth_token, String oauth_verifier)
{
  if (oauth_token == null)
      return View("Error");
  
   // Exchange the Request Token for an Access Token
   var service = new TwitterService(consumerKey, consumerSecret);
   var requestToken = new OAuthRequestToken { Token = oauth_token };
   var accessToken = service.GetAccessToken(requestToken, oauth_verifier);

   // More to do here ...
}

Once the user has entered his or her credentials, you're one step away from completing your effort. Another call is required to exchange the request token with the tool that will unleash the power of Twitter programming: the access token object. The callback receives two strings that you need to present back to Twitter to get your access token. In ASP.NET MVC, you should pay attention to the names of the Success method parameters. If you use a code-productivity extension in Visual Studio, such as JetBrains' ReSharper, you will likely get suggestions to rename oauth_token and oauth_verifier to comply with coding standards. If you do so, then you need to add a Bind attribute to save yourself a Twitter exception on the next access to the framework, like this:

public ActionResult Success(
     [Bind(Prefix="oauth_token"] String oauthToken,
     [Bind(Prefix="oauth_verifier"] String oauthVerifier)

Once the access token has been delivered to the requesting application, the application can use the user account. The use of the account is limited by the permissions that the application initially requested and that the user agreed to concede. In other words, an application that doesn't request permission to write posts will never be able to post updates on behalf of a user. An additional sensible limitation is that no application is enabled to change the user password. Permissions are read-only, read-write, and read-write with direct access to messages.

Dealing with the Access Token

To post an update, or to read tweets, an application needs the information stored in the access token. TweetSharp provides an OAuthAccessToken class with four properties: ScreenName, UserId, Token, and SecretToken. ScreenName is the username as a string, whereas UserId uniquely identifies a Twitter user through a numeric ID. Token and SecretToken are two user-specific strings that Twitter requires to be able to perform any operations.

You obtain an access token every time the user logs in and enters credentials. However, you don't want this interaction to take place for every browser session. To avoid that, you need to save the access token somewhere. For example, you could store the access token in the web application's cache, where the token would survive browser sessions but not application restarts. Alternatively, you may consider storing the token in some persistent store, such as a database table.

You can't simply cache or store the token information without binding it to the username. It turns out that if you opt for a persistent store, you may end up with a table that over time mirrors some Twitter user table. This table will potentially enable you to post updates at any time on behalf of users. It's a sort of corner-case scenario that may happen when your application is open to any Internet users. The point is that capturing data (names and tokens) of Twitter users may be in conflict with the rules of using the Twitter API. (The rules are displayed when you register your application with Twitter.)

Note that a user can revoke access to any applications at any time by simply signing in and editing the Twitter user profile. Under the Applications tab, a user finds listed all the Twitter applications he or she granted access to at some point. Revoking access is just a matter of clicking a button.

Authentication Cookies

In Lazy Authentication with Twitter," I showed how to create an ASP.NET authentication cookie where the username is just the screen name of the Twitter user. If all you need to do is authenticate users, then this technique works well. But what about stuffing the access token in the authentication cookies? By doing this, you can keep user access tokens at hand while saving yourself the burden (and risks) of building a parallel Twitter users table. By creating a customized version of the ASP.NET authentication cookie, you know everything you need to know about a particular user without having to save that information permanently.

Stuffing custom data in an authentication cookie is easy, as Figure 5 demonstrates, showing an extension method for the HttpResponse object that creates and appends the cookie.

public static void AppendTwitterAuthCookie(this HttpResponse response, String username, String accessToken, String accessSecret, Boolean persistent = true)
{
    // Let ASP.NET create a regular auth cookie 
    var cookie = FormsAuthentication.GetAuthCookie(username, persistent);

// Modify the cookie to add access-token data
    var userData = String.Format("{0}:{1}", accessToken, accessSecret);
    var ticket = FormsAuthentication.Decrypt(cookie.Value);
    var newTicket = new FormsAuthenticationTicket(ticket.Version,
    ticket.Name, ticket.IssueDate, ticket.Expiration, ticket.IsPersistent, userData);
    cookie.Value = FormsAuthentication.Encrypt(newTicket);

// Append cookie
    response.AppendCookie(cookie);
}

You place a call to this method at the bottom of the method featured in Figure 4, as follows:

System.Web.HttpContext.Current.Response.AppendTwitterAuthCookie(
   accessToken.ScreenName, accessToken.Token,
   accessToken.TokenSecret);

In ASP.NET, adding user data to an authentication cookie is easy; reading it back, though, requires some extra work.

Creating the TwitterPrincipal Object

In ASP.NET, you access user information via the HttpContext.User property. The User property is defined to be of type GenericPrincipal. All you need is a more specialized class built on top of GenericPrincipal that exposes the extra properties of the Twitter authentication cookie. Figure 6 shows such a class, the TwitterPrincipal class.

public class TwitterPrincipal : GenericPrincipal
{
    public TwitterPrincipal(IIdentity identity, String[] roles) : base(identity, roles)
    {
    }

// Extra properties
    public String AccessToken { get; set; }
    public String SecretToken { get; set; }
}

You need to fill up an instance of this class every time a request is authenticated in your application. This can be done in the PostAuthenticateRequest event of the global.asax file or in a custom HTTP module, as shown in Figure 7.

protected void Application_PostAuthenticateRequest()
{
    // Collect current security information
    var principal = HttpContext.Current.User as GenericPrincipal;
    if (principal == null) return;
    var identity = principal.Identity as FormsIdentity;
    if (identity == null) return;

// Extract user data from the Twitter authentication ticket 
    var userData = identity.Ticket.UserData;
    var tokens = userData.Split(':');

// Build a richer principal object
    var twitterPrincipal = new TwitterPrincipal(identity, null)
                {
                    AccessToken = tokens[0],
                    SecretToken = tokens[1]
                };
    HttpContext.Current.User = twitterPrincipal;
}

To consume the extra data stored in the authentication cookie, you need to cast the User object to TwitterPrincipal. Here's an example of how to display the token strings from within a Razor view:

Token: @((HttpContext.Current.User as TwitterPrincipal).AccessToken)

In the same way, you can access token and token secret strings and post an update. TweetSharp requires that you call the SendTweet method on a TwitterService instance, as shown in Figure 8. Before you tweet, however, you must authenticate with Twitter by passing the token and secret. You use the AuthenticateWith method for this purpose.

public void Post(String status)
{
   var service = GetService();

   // Retrieves Twitter token/secret from the cookie (if any)
   var twitterPrincipal = System.Web.HttpContext.Current.User as TwitterPrincipal;
   if (twitterPrincipal == null) return;
   var token = twitterPrincipal.AccessToken;
   var secret = twitterPrincipal.SecretToken;

   // Authenticate and posts the update
   service.AuthenticateWith(token, secret);
   var actualStatus = String.Format("{0} :{1} #webTWEETing", status, DateTime.Now);
   service.SendTweet(actualStatus);
  }

Improve Your Social Skills

As I see things, a tight integration between websites, mobile front ends, and social networks is of growing relevance in modern application software. More and more companies are realizing the importance of adding a social feed to their output channels. The ability to master the Twitter and Facebook APIs is a key asset for developers, and even more so is mastery of the fundamentals of social programming.

Dino Esposito, author of Programming Microsoft ASP.NET 4 and Programming Microsoft ASP.NET MVC (Microsoft Press), is a trainer and consultant specializing in web and mobile architectures and effective code design principles and practices. Follow Dino on Twitter: @despos.

Hide comments

Comments

  • Allowed HTML tags: <em> <strong> <blockquote> <br> <p>

Plain text

  • No HTML tags allowed.
  • Web page addresses and e-mail addresses turn into links automatically.
  • Lines and paragraphs break automatically.
Publish