Lazy Authentication with Twitter
Implement single sign-on between Twitter and your ASP.NET MVC website
September 15, 2011
I don't know about you, but for me, the world would be a better place if I could use just one pair of credentials for everything. Of course, using the same username and password over and over again is not a recommended practice, but that doesn't mean I can't wish for it. And I'm not alone. For this reason, more and more websites are offering a one-click sign-in approach. "Lazy authentication" means that a website relies on the response it gets from some external service. When you implement this type of authentication, a user who successfully signs in on the companion service site is automatically authenticated on your site as well. This is an idea that Microsoft attempted to promote years ago, with its Passport initiative. For some reason, the solution didn't live up to expectations and the initiative never caught on. Today, the Passport API is officially obsolete, but its core idea is more alive than ever.
In this article, I'll show how to enable an ASP.NET MVC website to authenticate users via Twitter. Instead of (or in addition to) providing classic membership-based authentication, the site will allow users to authenticate with their Twitter accounts and to connect automatically to the site if they're already logged on to Twitter.
OpenID vs. OAuth
If you're looking for a better (and more open) version of Passport, then consider the OpenID protocol. You can find out all about OpenID at openid.net. This protocol has just one goal: identifying users in the simplest possible way. A website that acts as an OpenID client simply needs a username; the website that acts as an OpenID server holds the username and password only. No other user-specific content or information is supposed to exist or be shared.
The main purpose of OpenID is to make access to a website easier, quicker, and more agreeable for end users. The visitors to sites that support OpenID can sign in by using an existing identity token that another site has issued. An OpenID–enabled website authenticates its users against an external identity provider and doesn't need to store passwords or implement a membership layer.
The advent of social networks has changed the situation. Not only do users want to use a single and familiar identity to connect to multiple sites, but they might agree to grant one website access to some resources that they manage on another site. OAuth (see oauth.net) is another single sign-on (SSO) scheme with more capabilities than OpenID. A website that acts as an OAuth provider operates as an identity provider; when users log in, the site allows them to specify permissions on its resources. A website that uses OAuth authentication acts as the client of an OAuth provider. Two popular OAuth providers are Twitter and Facebook.
The DotNetOpenAuth Library
You can write your own implementation of the OpenID or OAuth protocols, but using a made-to-measure library enhances your productivity. Implementing OpenID or OAuth is mostly a matter of HTTP programming—calling a given URL, preparing a query string, parsing the response, and so on. These tasks are doable but involve many pesky details. Quite a few good libraries exist to help integrate OpenID and OAuth into your websites. A popular library for Microsoft .NET developers is DotNetOpenAuth (DNOA), which is also available from as a NuGet package (as Figure 1 shows).
Figure 1: Getting DotNetOpenAuth from the NuGet manager
The library comes with a companion assembly—the DotNetOpenAuth application block—that contains some useful helper classes, including one that is prepackaged for Twitter. This class basically makes it quick and easy to implement a Twitter-based SSO for every site.
Let Twitter Know About You
An OpenID provider is uniquely identified through a URL, and all you need to do is prepare a call with the proper number of parameters. But when it comes to OAuth, you need to take a preliminary step: registering your website with the OAuth provider application. The details might differ a bit from provider to provider. For example, Facebook requires that you add the Developer block to your personal profile and set up a new application through that block. Registering an application involves providing a name, an icon, a URL, and contact information. For Twitter, the process is simpler. You go to dev.twitter.com, register a new application (site or desktop) and enter a few details, as Figure 2 shows.
Figure 2: Registering an application with Twitter
In particular, Twitter requires you to set the application type (web or desktop) and the default permissions that your application requires on the user resources (i.e., tweets, followers) that Twitter holds. The Twitter page also requires you to indicate a callback URL. This URL is the address to which Twitter returns after successfully authenticating a user. You likely don't want this URL to be constant and might be tempted to leave the field blank. But Facebook requires that any return URL belong to the same domain as the callback URL that you set when you register your application. Twitter is a bit less restrictive. Twitter prefers that you complete the callback URL field but degrades gracefully. In particular, if you leave the field blank because you intend to specify the callback URL on a per-call basis, then Twitter overwrites the application type setting, forcing the setting to desktop. The net effect is that any attempts to authenticate users return unauthorized. Assigning any URL, even one that results in a 404, works. So for web applications, always specify a callback URL.
Registering the application with an OAuth provider returns a pair of strings: the consumer key and the application secret string. These keys must be provided when you attempt a connection.
Go with Authentication
To trigger Twitter authentication, add a link somewhere in the user interface and point that link to a controller method. Figure 3 shows a typical implementation of this logon method. This implementation is based on DNOA and the related application block. Quite a few Twitter-specific URLs are known. The DNOA application block has hard-coded all the URLs in the ServiceDescription property of the TwitterConsumer class. Note that the Send method in Figure 3 places the request only if a valid authentication cookie cannot be found on the client machine. If such a cookie exists, then the user is considered to be logged in. This is in line with the typical behavior of forms authentication in ASP.NET.
Figure 3: Starting a Twitter sign-in
public void TwitterLogOn()
{
var client = new WebConsumer(TwitterConsumer.ServiceDescription, TokenManager);
// Create the callback URL for Twitter
var callbackUri = "/account/authenticated";
// Place the request
client.Channel.Send(
twitterClient.PrepareRequestUserAuthorization(callbackUri, null, null));
}
When the request reaches the Twitter site, the user is redirected to the authorization page, such as the one that Figure 4 shows.
Figure 4: The Twitter authentication form
If the user authorizes the requesting site, then Twitter wraps up information for the site in an access token. The access token contains the username. As the code in Figure 3 shows, the action method that triggers the authentication process only redirects to the Twitter site; by providing a callback URL that maps to another action method, you have your ASP.NET MVC to deal with the Twitter response quite comfortably. Figure 5 shows some ASP.NET MVC code that deals with the Twitter response and actually creates the authentication cookie.
Figure 5: Processing the Twitter response
public ActionResult Authenticated()
{
// Process the response
var client = new WebConsumer(TwitterConsumer.ServiceDescription, TokenManager);
var accessToken = client.ProcessUserAuthorization();
// Is Twitter calling back with authorization?
if (accessToken!= null)
{
// Extract the access token and username for use throughout the site
var cookie = ExtractDataAndCreateAuthCookie(accessToken);
Response.Cookies.Add(cookie);
}
else
{
// If the request doesn't come with an authentication token redirect to the login page
return RedirectToAction("LogOn", "Account");
}
// If the authentication is successful, redirect to the home page
return RedirectToAction("Index", "Home");
}
The ProcessUserAuthorization method gets you the Twitter access token. This token contains the username, and you use this information to create the classic ASP.NET authentication cookie, as Figure 6 shows. You should store the access token in the cookie, to use as long as the user stays connected to the site. To store the access token in the cookie, create and encrypt a custom ticket. Next, add the cookie to the ASP.NET response as usual. That's it: Lazy authentication with Twitter is served in a few minutes.
Figure 6: Creating the authentication cookie
public HttpCookie ExtractDataAndCreateAuthCookie (AuthorizedTokenResponse accessToken)
{
var token = accessToken.AccessToken;
var username = accessToken.ExtraData["screen_name"];
return CreateAuthCookie(username, token, true /* persistent */);
}
private static HttpCookie CreateAuthCookie(String username, String token, Boolean persistent)
{
// Let ASP.NET create a regular auth cookie
var cookie = FormsAuthentication.GetAuthCookie(username, persistent);
// Modify the cookie to add access-token data
var ticket = FormsAuthentication.Decrypt(cookie.Value);
var newTicket = new FormsAuthenticationTicket(ticket.Version, ticket.Name, ticket.IssueDate,
ticket.Expiration, ticket.IsPersistent, token);
cookie.Value = FormsAuthentication.Encrypt(newTicket);
return cookie;
}
More About the Access Token
As long as you use Twitter (or any other OAuth provider) only for authentication purposes, the access token is a string of limited importance. To be precise, you don't strictly need to embed the string in a customized cookie—the username is enough if all you want is authentication. After you've bound the username to the cookie and used the username to retrieve personal information, your site is fine as long as the cookie is valid or until the user logs out. After that, the next attempt to log in will redirect the user to the Twitter site for a new authorization, as in Figure 4.
The access token is fundamental if you want to retrieve additional information about the user: picture, followers, tweets, or whatever else the provider can offer, the application wants, and the user authorizes. If your site requires such information, then preserving the access token in the cookie is key.
More About the Token Manager
In Figure 3, the WebConsumer class—a helper class in the DNOA library—gets a token manager object. As the name suggests, this object manages the access token. The object also stores the application's consumer and secret keys. Passing the application's keys to the token manager is your responsibility. A token manager is an object that implements the IConsumerTokenManager interface, as defined in the DNOA library.
The DNOA application block provides a default implementation of the token manager that stores OAuth request and access tokens in memory. This implementation is fine if you're using the OAuth provider for the sole purpose of authentication. However, if you plan to place additional requests for data to the OAuth provider, then you might want to implement the token manager so that it permanently stores access tokens, which it saves from an explicit authorization step. The access token represents the authorization that you received from the OAuth provider for a given user. If you have that token, then having the user logged in to the Twitter site suffices. If you lack the access token, then the user is prompted with the page that Figure 4 shows, regardless of whether the user is logged in to Twitter. Of course, none of this will happen if the ASP.NET authentication cookie on the client machine is valid.
Retain More Users
For websites that depend on a lot of traffic and a large audience, the OpenID or OAuth authentication scheme is an interesting feature that could help attract and retain more visitors. These schemes save users from needing to remember yet one more account, while giving them a personal set of features on your site. You don't need to forgo classic membership; both approaches can coexist. Finally, note that regardless of the settings that you enter when you register the application, Twitter works just fine with a test application. In other words, you can redirect to a localhost as long as the request originates from the same localhost. However, if you intend to authenticate with Facebook, you'll need to change your settings for the callback URL to localhost, at least during the development phase.
About the Author
You May Also Like