Search code examples
asp.net-web-apiasp.net-identityasp.net-web-api2owinasp.net-identity-2

Where to filter Identity 2.0 claim ticket in a WebAPI app?


ASP.NET apps using OWIN permit multiple Identity sources (Facebook, Google, etc.). Most of the provider-specifc information those sources provide is irrelevant to my app, potentially even large, and I don't want it in my cookies all session. My app is primarily WebAPI, but I suspect the question applies equally to MVC and WebForms.

For now, all I need is an integer account ID. Where/when should I reconstruct the identity, after external authentication?

For example, here is one way I could filter claims:

public ReplaceExistingClaims(ClaimsIdentity identity) {
{
    Claim customClaim = GetCustomClaimFromDbForIdentity(identity);
    foreach (Claim claim in ClaimsIdentity.Claims) ClaimsIdentity.RemoveClaim(claim);
    ClaimsIdentity.AddClaim(customClaim);
}

And following are two different places I could inject those claims changes:

var facebookAuthenticationOptions = new FacebookAuthenticationOptions
{
    Provider = new FacebookAuthenticationProvider
    {
        OnAuthenticated = context =>
        {
            ReplaceExistingClaims(context.Identity);
            return Task.FromResult(0);
        }
    }
};

Above, I know I can hook an individual provider from Startup IF it provides an Authenticated event. I have two conceptual problems with this. One: it requires me to write and wire up my code separately for each provider I plug in. Two: there is no requirement for providers to provide this event. Both of these make me feel like there must be a different intended insertion point for my code.

public ActionResult ExternalLoginCallback(string returnUrl)
{
    ReplaceExistingClaims((ClaimsIdentity)User.Identity);
    new RedirectResult(returnUrl);
}

Above, I know I can put code in ExternalLoginCallback. But this happens too late for two reasons. One: The user has already been issued a ticket I consider invalid, but the default [Authorized] considers valid because it's signed by me, and now they are making requests to my site with it. There could even be race conditions here. Two: There is no guarantee the browser will visit this redirect, and I'd prefer from a design perspective if it didn't have to, e.g. to simplify my WebAPI client code.

To the best of my knowledge, the best solution will meet these requirements:

  1. same code applies to all providers
  2. client receives my custom ticket from my server (e.g. without image claims)
  3. client never receives another ticket format from my server
  4. the authentication process requires the minimum possible HTTP round-trips
  5. token-refresh and other core identity features are still available
  6. once a user is [Authorize]d, no further account transformation is necessary
  7. database/repository access is feasible during ticket generation

Some pages I'm researching, for my own notes:


Solution

  • The ClaimsAuthenticationManager class is specifically for this.

    https://msdn.microsoft.com/en-us/library/system.security.claims.claimsauthenticationmanager(v=vs.110).aspx

    Code sample from that reference:

    class SimpleClaimsAuthenticatonManager : ClaimsAuthenticationManager
    {
        public override ClaimsPrincipal Authenticate(string resourceName, ClaimsPrincipal incomingPrincipal)
        {
            if (incomingPrincipal != null && incomingPrincipal.Identity.IsAuthenticated == true)
            {
                ((ClaimsIdentity)incomingPrincipal.Identity).AddClaim(new Claim(ClaimTypes.Role, "User"));
            }
            return incomingPrincipal; 
        }
    }