Skip to content

Integrating Claims and OAuthWebSecurity

September 5, 2012

As I mentioned yesterday, I have been researching the new OAuth/OpenID support in ASP.NET. One of the interesting aspects of using an external identity provider is that it not only authenticates users but also can provide additional user data (such as email, gender, location, etc.). The one thing I was surprised about is that the project templates don’t take advantage of this data (or at least it’s not pointed out in any way). So I wanted to illustrate how to make this data readily available to an application.

This additional user data is available in the ExternalLoginCallback action method in the AuthenticationResult object returned from the call to OAuthWebSecurity.VerifyAuthentication:

[AllowAnonymous]
public ActionResult ExternalLoginCallback(string returnUrl)
{
    AuthenticationResult result = OAuthWebSecurity.VerifyAuthentication(Url.Action("ExternalLoginCallback", new { ReturnUrl = returnUrl }));
    if (!result.IsSuccessful)
    {
        return RedirectToAction("ExternalLoginFailure");
    }

    IDictionary<string, string> userData = result.ExtraData;

    ...
}

So the next question becomes “what to do with this data”. One idea would be to simply save it to the database and then retrieve it as needed. This would work and would also be useful for offline access to the data (like sending emails). But I wanted a more general purpose mechanism to make this data available to the application. Of course the first thing that came to mind was to use Claims.

Now that WIF and Claims are baked into the .NET framework in 4.5, this is an obvious place to express this user data to the rest of the application. In fact, I am a little surprised there’s nothing built-in that already does this. Since there wasn’t anything built-in, I built my own little framework to map the AuthenticationResult.ExtraData into the ClaimsPrincipal.Current.Claims collection. Since the new SimpleMembership is just using Forms Authentication and since the FormsIdentity now inherits from ClaimsIdentity I was able to add this in with minimal disruption.

I created a helper library called WebSecurityClaimsHelper with a class called OAuthClaims that maps this AuthenticationResult into claims for the current user. I’ve packed it into a NuGet package and made the code available on GitHub. All that’s needed to use it is to reference the assembly and then add one line of code after the call to OAuthWebSecurity.VerifyAuthentication (so in the same action method mentioned above):

[AllowAnonymous]
public ActionResult ExternalLoginCallback(string returnUrl)
{
    AuthenticationResult result = OAuthWebSecurity.VerifyAuthentication(Url.Action("ExternalLoginCallback", new { ReturnUrl = returnUrl }));
    if (!result.IsSuccessful)
    {
        return RedirectToAction("ExternalLoginFailure");
    }

    // maps the ExtraData to Claims for the current user
    OAuthClaims.SetClaimsFromAuthenticationResult(result);

    ...
}

Now the ExtraData comes back as Claims (and are mapped using the standard ClaimTypes) and are accessible where all claims are: ClaimsPrincipal.Current.Claims. Here’s an example of querying for the user’s email:

var principal = System.Security.Claims.ClaimsPrincipal.Current;
var emailClaim = principal.FindFirst(ClaimTypes.Email);
if (emailClaim != null)
{
    var email = emailClaim.Value;
}

And here’s an example of enumerating all of the user’s claims and displaying them in a <table> in Razor:

@{
    var principal = System.Security.Claims.ClaimsPrincipal.Current;
    <h2>Claims</h2>
    <table>
        @foreach (var claim in principal.Claims)
        {
            <tr>
                <td>@claim.Type</td>
                <td>@claim.Value</td>
                <td>@claim.Issuer</td>
            </tr>
        }
    </table>
}

Feel free to provide feedback and enjoy.

10 Comments leave one →
  1. September 26, 2012 6:23 am

    Nice blogpost. I followed a part of this blog post to realize the service part of my blog post, in which I get the claims of a SWT token. If you are interested, you can find it here: http://riccardocorradin.wordpress.com/2012/09/20/claims-based-authentication-with-odata-and-windows-azure-3/
    Any suggestion and/or comments are most welcome :).

  2. Jose Luis permalink
    September 27, 2012 11:29 am

    Hi Brock,
    I`ve have been reading your blog about membership and simplemembership functionality and capabilities, and i´ve been reading Dominick Baier works about Identity and claims so i think “Claims” is a “good thing”.

    But also i´ve been reading Jon Galloway works about SimpleMembership and Membership and i Think SimpleMembership is a “good think”.

    And now i have a conflict; i know both things can live together, that one of them is the repository of users and passwords and the other is the token that lives in the authemticated entity but i don’t know how to manage some oppinions about the convenience or not of the use together of them or write your own.

    I wouldn`t like to have to rework later and i would like to have your opinion about how to implement or how to start with the following situation:

    I´m working on a web portal that can integrate small MVC applications. each Mvc applications have some information attached to the user that have to live in the cookie for performance reasons.

    So i´ve been thinking in a simple repository of users and passwords common to all the applications for those users that want to register with the portal and a common profile users database for all the applications settings for the users that register or for the users that authenticate with a external authority.

    Claims is the way all the aplications and also the login service of the portal should be used, but: can i login a user create a cookie on the portal that later an applications can read, use and include some information on it?

    and now the repository, why not simpleMembership? can i have some utilities with other model that can help me to achive ?

    Tx

  3. cem permalink
    October 1, 2012 5:25 pm

    var emailClaim = principal.FindFirst(ClaimTypes.Email);
    emailClaim always null ? what causes ?

    • October 1, 2012 6:52 pm

      Either the identity provided didn’t send that claim or the identity provider doesn’t even support that claim type.

  4. Steve permalink
    October 17, 2012 7:03 am

    Hi Brock,

    Any chance you can make the nuget package reference .net 4.0 rather than 4.5? (or make a dupe package?)

    My hosting doesn’t support 4.5 yet.

    Love the package BTW.

    Steve.

    • Steve permalink
      October 17, 2012 7:16 am

      Oh, on closer inspection all the Claims stuff in 4.5 only.. disregard..thanks

    • October 18, 2012 11:46 pm

      @Steve — Yea, sorry. It would have been a hassle to do a WIF 3.5 version. It’s a simple/small enough repo onGitHub where you could rework it yourself for WIF 3.5.

  5. jfoegen permalink
    November 9, 2012 12:03 pm

    You show this example to save off the claims in the extraData (I see where you get, but not set), but isn’t that what the SessionAuthenticationModule is for?

    • November 9, 2012 12:10 pm

      I “remember” the values in the call: OAuthClaims.SetClaimsFromAuthenticationResult(result);

      As for the SAM, you’re absolutely right and that’s what I wanted to use, but the barrier to entry is 1) to use the SAM you need to have all of the WIF configuration enabled and this seemed like a hurdle most people wouldn’t want to deal with, and 2) SAM would in effect trump the forms auth cookie plumbing and all of the rest of the OAuthWebSecurity APIs assume forms auth for login/logout/etc.

      So this approach I propose is intended to have the least impact as possible on the rest of the defaults. If you want a more proper approach then check out my other post: http://brockallen.com/2012/10/26/integrating-claims-and-oauth2/

Trackbacks

  1. SimpleMembershipProvider 和 OAuth | 飞扬网络博客

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: