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.

30 Comments leave one →
  1. Ron's avatar
    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.

    • brockallen's avatar
      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's avatar
    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's avatar
    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's avatar
    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.

    • brockallen's avatar
      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's avatar
    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.

    • brockallen's avatar
      January 8, 2013 7:06 pm

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

      • MarkusR's avatar
        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.

    • Brent Arias's avatar
      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. Jolly's avatar
    February 18, 2013 7:05 am

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

  7. Jolly's avatar
    February 19, 2013 1:19 am

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

    • brockallen's avatar
      February 19, 2013 7:36 am

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

      • Jolly's avatar
        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's avatar
    Jolly permalink
    February 21, 2013 6:06 am

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

  9. Matt Johnson-Pint's avatar
    mj1856 permalink
    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!

    • brockallen's avatar
      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's avatar
    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

    • brockallen's avatar
      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. John Ripper's avatar
    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. Nik Gupta's avatar
    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's avatar
    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…

    • brockallen's avatar
      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's avatar
        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.

        • brockallen's avatar
          October 29, 2013 4:49 am

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

  14. ionescuvictor's avatar
    ionescuvictor permalink
    January 31, 2015 1:02 pm

    where does OAuthWebSecurity.VerifyAuthentication get its parameters from.What are its parameters exactly.

    • brockallen's avatar
      February 2, 2015 5:25 pm

      Is has some internal values it uses to validate the response. It doesn’t really matter — I’d strongly encourage you to use the newer Katana authentication middleware to do social logins. It’s a more robust approach and is supported by Microsoft.

Leave a comment