Skip to content

Using OAuthWebSecurity without SimpleMembership

September 4, 2012

I’ve been researching the new support in ASP.NET for OAuth and OpenID authentication. It provides a nice and easy to use wrapper on DotNetOpenAuth. The main APIs are on the OAuthWebSecurity class and they provide methods to authenticate against your OAuth and OpenID providers as well as associate those OAuth and OpenID accounts to an account with your local membership provider (and strictly speaking your simple membership provider). Personally, I dislike the coupling between the authentication piece and the persistence piece. Fortunately this API can still be used without using providers.

The main APIs to be aware of are:

  • OAuthWebSecurity.RegisterXxxClient
  • OAuthWebSecurity.RegisteredClientData
  • OAuthWebSecurity.RequestAuthentication
  • OAuthWebSecurity.VerifyAuthentication

RegisterXxxClient (where Xxx is: Microsoft, Twitter, Facebook, Google, Yahoo or LinkedIn)

The various Register APIs allow you to initialize which OAuth/OpenID identity providers you want to use (many of these require passing your application identifier and secret).

RegisteredClientData

This API provides the list of the registered identity providers. This is necessary for the ProviderName property when requesting authentication.

RequestAuthentication

This is the API to invoke to trigger a login with one of the identity providers. The parameters are the identity provider name (so one of the ProviderName values from the RegisteredClientData collection) and the return URL where you will receive the authentication token from the identity provider. Internally it does a Response.Redirect to take the user to the identity provider consent screen.

VerifyAuthentication

This API validates the authentication token from the identity provider and returns the results. You can then use these results to log the user into your own application (typically with Forms Authentication). There is also additional data returned from the identity provider such as email, gender, etc. Different identity providers return different information.

So to trigger the authentication, this is all you need to do:

[AllowAnonymous]
public void LoginWithProvider(string provider)
{
    OAuthWebSecurity.RequestAuthentication(provider, Url.Action("AuthenticationCallback"));
}

[AllowAnonymous]
public ActionResult AuthenticationCallback()
{
    var result = OAuthWebSecurity.VerifyAuthentication();
    if (result.IsSuccessful)
    {
        // name of the provider we just used
        var provider = result.Provider;
        // provider's unique ID for the user
        var uniqueUserID = result.ProviderUserId;
        // since we might use multiple identity providers, then 
        // our app uniquely identifies the user by combination of 
        // provider name and provider user id
        var uniqueID = provider + "/" + uniqueUserID;

        // we then log the user into our application
        // we could have done a database lookup for a 
        // more user-friendly username for our app
        FormsAuthentication.SetAuthCookie(uniqueID, false);

        // dictionary of values from identity provider
        var userDataFromProvider = result.ExtraData;
        var email = userDataFromProvider["email"];
        var gender = userDataFromProvider["gender"];

        return View("LoggedIn", result);
    }
    return View("Error", result.Error);
}

It’s really up to you how to identify the username when you’re logging them in with FormsAuthentication, but it’s important that to uniquely identify your users your must use both the provider name and the provider user id. In the above snippet we’re just logging the user in with that, but you could built your own back-end database to allow users to manage their own username for your application. This mapping is in essence what the new SimpleMembership system is doing, and if you don’t need or want to control the database schema then it’s a fine solution.

Also notice the extra data from the authentication result. This contains additional user information the identity provider has returned to the application. From what I can tell, here is the breakdown (beyond ProviderUserId) of what data each provider returns (these are the actual keys used in the ExtraData dictionary):

Google: email, country, firstName, lastName
Microsoft: name, link, gender, firstname, lastname
Facebook: username (which is really an email address), name, link (URL to their facebook page), gender, birthday
Twitter: name, location, description, url (URL to their Twitter page)
Yahoo:
email, fullName
LinkedIn: name, headline, summary, industry

Microsoft did a nice job making it easy to use DotNetOpenAuth.

28 Comments leave one →
  1. Ron permalink
    September 6, 2012 2:18 am

    What does it take to add more providers than those that are pre-built. According to the asp.net site it’s “easy” to add your own providers. Since OAuth is a common WebService to WebService communication channel it would be great to see an example where new Clients are added.

    • September 6, 2012 7:06 am

      Good question. The extensibility model is just that of DotNetOpenAuth, so I imagine there are already examples. I’ll look into it and if I can’t find any then that would make for another good post.

  2. Chris permalink
    September 18, 2012 7:44 am

    This did a better job of explaining the API than the bloody Microsoft docs. Thanks mate.

  3. Nobody Special permalink
    December 16, 2012 8:26 am

    This. is. golden.

    Thank you kindly and so much for taking the time to share this information. Can’t begin to describe how helpful this is.

  4. Stu permalink
    December 20, 2012 11:43 am

    Nice. But the Facebook username returned is not always an email address, so you can’t rely on that. Indeed this has to be separately requested somehow, but I can’t figure out how this is possible using this new OAuth wrapper.

    • December 20, 2012 2:53 pm

      Stu, thanks for the info. I’ll have to go confirm and change the code as necessary. To get the email as its own field the email scope needs to be requested. I don’t recall if DotNetOpenAuth has a setting to also request it (more confirmation needed).

  5. MarkusR permalink
    January 8, 2013 5:42 pm

    How does one logout? For instance I allow the user to login via openid, once authenticated I make a REST call to further authenticate within my system. If the REST call returns an unsatisfactory result I want to “unauthenticate” the user and redirect back to login page. The redirect works but when I click on the yahoo login it thinks I am already authenticated.

    • January 8, 2013 7:06 pm

      I don’t follow. Logout of which system — your web app or Google/Yahoo/Facebook?

      • MarkusR permalink
        February 13, 2013 7:48 pm

        Let’s say they log in to my site using a yahoo account. Then the session times out and I force them back to the log in page. All they have to do is click on yahoo and they are immediately in my application again. I would like to force them to log in again through yahoo.

    • February 18, 2013 7:56 am

      Any IDP that you log into likely will create its own cookie, independent of your application. Thus if you decide the user is logged-out of your app, it has no bearing on the IDP cookie still available to them. That cookie will not be readily accessible by you or the DotNetOpenAuth library. Indeed the stackoverflow site exhibits the same problem. If you log into stackoverflow with google, then explicitly log out, then log in again with google, you will not be asked for credentials a second time.

  6. February 18, 2013 7:05 am

    How can I use OAuthWebSecurity to do authentication for Single Sign On using SQl Server Database

  7. February 19, 2013 1:19 am

    Then what is the purpose of the methods OAuthWebSecurity.RegisterClient method

    • February 19, 2013 7:36 am

      Looks like it allows you to register a custom OAuth client.

      • Jolly permalink
        February 20, 2013 1:41 am

        Can you guide me how to crate custom OAuth client. Because on creating custom OAuth client it gives me error.

  8. Jolly permalink
    February 21, 2013 6:06 am

    Can i use oAuth to authenticate using the DB of Third Party?

  9. February 24, 2013 7:33 pm

    Hi Brock. Thanks for the sample code. But it doesn’t work as-is with the Microsoft (LiveID) provider. A return URL must be passed in the VerifyAuthentication method. Please see http://stackoverflow.com/a/15058420/634824 and update your sample. Thanks!

    • June 5, 2013 9:39 am

      IIRC, live won’t allow you to register localhost as a callback URL, so that’s why that won’t work. You’d need to deploy to a real host (or use DNS tricks to work with localhost).

  10. Wai permalink
    June 4, 2013 7:29 am

    Hi

    This example is great. I can login a user without using membership now.

    Do you know of a way I can check whether the web browser currently has a logged in facebook user using server side code before calling the requestauthentication call? I want to check if someone is already logged into facebook, if so maybe prompt user and ask if this is them before doing the requestverification call.

    Many thanks

    • June 5, 2013 9:41 am

      There are tricks/hacks out there for knowing if the user on your site is currently logged into facebook — search for it.

  11. August 9, 2013 12:15 pm

    Thanks Brock. Really helpful article. I’ve look carefully and the default simplemembership implementation and the OAuth implementation you get with the MVC4 website template. It is messy to say the least. It’s hard to decouple and hard to follow the flow logic at times.

  12. September 22, 2013 8:07 am

    This is a brilliant article and just what I have been looking for. Thank you so much for writing this mate. Cheers!

  13. Mark permalink
    October 28, 2013 8:38 am

    This is working for me when I’m running the application directly from VS. If I’m setting it in IIS , throwing forbidden Error. Please help me…

    • October 28, 2013 10:00 am

      Sorry Mark — that’s not really enough to go on. Perhaps post on the ASP.NET forums, as more people will look at your question and might be able to help. http://forums.asp.net/

      • Mark permalink
        October 29, 2013 4:44 am

        Ok Brockallen. With the same code are you able to host in IIS & able to run in VS? in var result = OAuthWebSecurity.VerifyAuthentication();
        line Exception is throwing . The remote server returned an error: (403) Forbidden.

        • October 29, 2013 4:49 am

          Sounds like maybe your client id and/or secret are being denied? Not sure, without seeing/debugging it.

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s

%d bloggers like this: