Posted by: craigtech | March 20, 2010

Simple OAuth integration for Twitter in ASP.NET MVC

Speaking to @JacoPretorius earlier in the week the samples that come with the DotNetOpenAuth (http://www.dotnetopenauth.net/) libraries weren’t clear.  To be honest I couldn’t even get the samples to run, firstly because the project types weren’t up to date & also because the references to the MVC libraries were wrong.  I also wanted to get it running in VS2010.  Rather than labour with getting it running in VS2008 I had a quick scan of the source code & set to work figuring it out.  The article that follows describes what needed to be done to get the MVC sample application integrating with Twitters OAuth mechanism.

I’m not going to detail the OAuth process here, there are plenty of articles around that do that already.  I will summarise it from a dev perspective though – you redirect the user to a 3rd part auth provider (e.g. twitter, google, and many others) using a standard http redirect.  The details at the auth provider end will vary, but may include the user authenticating themselves & authorizing the calling application (i.e. your app) to access there identity.  The auth provider then redirects back to the calling application passing an encrypted string, which contains the users identity.  The details here will vary depending on the auth provider.

I do have a couple of gripes with the implementation of the OAuth protocol based on what I’ve seen with twitter & google (this may not apply to all OAuth providers); it seems you need to register with each provider you want to support (I couldn’t even register with google because it’s integrated into the google webmaster tools & needs to verify against a real site), you then need to store credentials for each of those providers in your application, finally it doesn’t seem like there’s a standard set of attributes in the identity that gets returned to allow you to extract info such as user ID.  You basically get a set of name value pairs in an "Additional parameters" collection, on twitter the username is in a property called screen_name.

The code below is just to illustrate be refactored as I add more providers but for now

Onto the real work…

I’ve started with the basic asp.net MVC starter site:

ASP.NET MVC Starter site

First you need to set yourself up as an oath client with twitter at http://twitter.com/oauth_clients/, the details here don’t seem to matter – I used a website URL that isn’t live yet & it was fine – when you’re debugging locally the redirect back from twitter after authenticating

Then reference the correct assemblies – you’ll need to find them in the samples, and put them wherever you’d normally put dependency libs:

DotNetOpenAuth
DotNetOpenAuth.ApplicationBlock

I then setup the references I need:

using DotNetOpenAuth.ApplicationBlock;
using DotNetOpenAuth.OAuth;
using DotNetOpenAuth.OpenId;

And a static field & property to store the twitter credentials:

private static InMemoryTokenManager _tokenManager = new InMemoryTokenManager("Twitter Consumer key", "Twitter Consumer secret");

private InMemoryTokenManager TokenManager
{
    get
    {
        return _tokenManager;
    }
}

Then modify the login view views/account/logon.aspxto include a "login via twitter" option as a simple link (I used an action called TwitterOAuth on the Account controller to wrap up my logic for preparing the request to twitter):

<a href="<%= Url.Action("TwitterOAuth") %>"><img src="../../Content/images/oauth/twitter/twitter_button.gif" alt="twitter oauth button" /></a>

It may look something like this:

Screenshot of login page

The server side component consists of two requests, one when the user clicks the button & one when the auth provider redirects the user back.  I have a TwitterOAuth action & a OAuth action.

In the TwitterOAuth action I use the dotnetauth library to construct a wrapper around the twitter OAuth API, prepare the URL that I want the user to be redirected back to  & send the request, which redirects the user to twitter to authenticate.

public ActionResult TwitterOAuth()
{
    var twitter = new WebConsumer(TwitterConsumer.ServiceDescription, this.TokenManager);
    
    //Create the URL that we want Twitter to redirect the user to
    var oAuthUrl = new Uri(Request.Url.Scheme + "://" + Request.Url.Authority + "/Account/OAuth");
    
    // If we don't yet have access, immediately request it.
    twitter.Channel.Send(twitter.PrepareRequestUserAuthorization(oAuthUrl, null, null));
    
    //This shouldn't happen!!
    
    return null;
}

In the OAuth action I’m going to process the response from Twitter and if successfully authenticated store the identity in the ASP.NET forms authentication cookie & redirect to the home page:

public ActionResult OAuth()
{
    var twitter = new WebConsumer(TwitterConsumer.ServiceDescription, this.TokenManager);

    // Process the response
    var accessTokenResponse = twitter.ProcessUserAuthorization();

    // Is Twitter calling back with authorization?
    if (accessTokenResponse != null)
    {

        //Extract the access token & username for use throughout the site
        string accessToken = accessTokenResponse.AccessToken;
        string username = accessTokenResponse.ExtraData["screen_name"];

        CreateAuthCookie(username, accessToken);
    }
    else
    {
        //If the request doesn't come with an auth token redirect to the login page
        return RedirectToAction("Login");
    }

    //Authentication successful, redirect to the home page
    return RedirectToAction("Index", "Home");
}

private void CreateAuthCookie(string username, string token)
{
    //Get ASP.NET to create a forms authentication cookie (based on settings in web.config)~
    HttpCookie cookie = FormsAuthentication.GetAuthCookie(username, false);

    //Decrypt the cookie
    FormsAuthenticationTicket ticket = FormsAuthentication.Decrypt(cookie.Value);

    //Create a new ticket using the details from the generated cookie, but store the username &
    //token passed in from the authentication method
    FormsAuthenticationTicket newticket = new FormsAuthenticationTicket(
    ticket.Version, ticket.Name, ticket.IssueDate, ticket.Expiration,
    ticket.IsPersistent, token);

    // Encrypt the ticket & store in the cookie
    cookie.Value = FormsAuthentication.Encrypt(newticket);
    
    // Update the outgoing cookies collection.
    Response.Cookies.Set(cookie);
}

That’s it! You should now see the logged in user in the top right corner & the logout method will work as expected:

Screenshot of user logged in

Responses

  1. Curious what problem you were seeing with the references that you say needed to be fixed up. They worked for me straight out of the download .zip file.

    Anyway, thanks for sharing your experience.

    • I’m going to package the source code into a demo app next week so I’ll take another look then & let you know.

  2. Could you kindly release the source code project for this? Thanks!

    • Will try to release the source code next week.

  3. Thanks dude – awesome post. Do you know if it’s possible to do OAuth the way StackOverflow does – it allows you to choose a provider OR just type in your URL…?

  4. Ok it seems I’m confusing OAuth with OpenID…

  5. That was really helpful. You solved my problem and gave me a fresh way to deal with Oath.

    Thanks
    Michelle

  6. Geat articel, any news on the source code?

  7. It sounds like the provider you’re integrating isn’t using OAuth, which provider is it, I’ll take a look.

  8. Nice article.

    Can you explain the benefit of your CreateAuthCookie method. Why use that as opposed to FormsAuthentication.SetAuthCookie

    Thanks!

    • Hi Andrew,

      It stores the users twitter username and auth token, encrypted, in the cookie. You could store it in session but then you’d need to maintain the session for a long time.

      Hope that makes sense.

      Craig

  9. christ, the dotnetauth guys don’t make things easy do they?!

    I can’t find a reference to ChannelElements.IOpenIdOAuthTokenManager now.

    not knocking this tutorial by the way, it has been very helpful in understanding a very complicated and unexplained dotnetopenauth library.

  10. Mark, the interface you can’t find is defined in this NuGet package: http://nuget.org/packages/dotnetopenauth.openidoauth

  11. I entered the command line into my package manager console and it still does not recognize the library

  12. I had to add the ApplicationBlock as an existing project, and then I had to add the DotNetOpenAuth dll to my main project, because the Nuget Command line hadn’t added that for me


Leave a reply to craigtech Cancel reply

Categories