ASP.NET: Integrating Claims and OAuthWebSecurity

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>
}

Advertisements

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