7

Starting with Visual Studio's "Web API" project template, I am attempting to add custom claims to the token created by the /Api/Account/ExternalLogin endpoint. I add these via the FacebookAuthenticationProvider.OnAuthenticated callback, but they do not persist through to the OAuthAuthorizationServerProvider.AuthorizationEndpointResponse().

Note: I am using a similar approach as documented by Rahul Nath in his article ASP.NET Web API and External Login - Authenticating with Social Networks

Code

In my Startup.Auth.cs class's ConfigureAuth() method (which is called from the OwinStartup class's Configuration() method) I added a callback function to the OnAuthenticated property in order to set a single claim, foo, with the value bar:

var facebookAuthenticationProvider = new FacebookAuthenticationProvider() 
{
    OnAuthenticated = (context) => 
    {
        context.Identity.AddClaim(new Claim("foo", "bar"));
        return Task.FromResult(0);
    }
};

I then add the FacebookAuthenticationProvider instance to a new FacebookAuthenticationOptions object:

var facebookAuthenticationOptions = new FacebookAuthenticationOptions() 
{
    AppId = "XXXX",
    AppSecret = "YYYY",
    Provider = facebookAuthenticationProvider
};

And pass that onto OWIN's UseFacebookAuthentication() method:

app.UseFacebookAuthentication(facebookAuthenticationOptions);

Results

If I put a breakpoint in the OnAuthenticated callback I can see that my custom claim is being added, as are a number of other claims (including a couple from the urn:facebook namespace). So far so good.

When I examine my claims via the AuthorizationEndpointResponse() method of my OAuthAuthorizationServerProvider class after a Facebook authentication, however, there are only two claims available in the context.Identity.Claims collection:

All of the urn:facebook claims have been removed, as has my custom foo claim. I'm assuming some other location in the pipeline is recreating the identity with a barebones set of claims, but I am not sure where.

Thoughts?

Jeremy Caney
  • 7,102
  • 69
  • 48
  • 77

2 Answers2

0

I have following code for accessing custom Claims:

 public class AppUser : ClaimsPrincipal{
    public AppUser(ClaimsPrincipal principal): base(principal){}
    public string Role{
        get{
            return this.FindFirst(ClaimTypes.Role).Value;
        }
    }  

    public string ProfileID{
        get{
            return this.FindFirst("ProfileID").Value;
        }
    }
}
Zoran P.
  • 870
  • 1
  • 13
  • 16
0

You probably need to edit the ExternalLoginData private class to include the additional claims you want to pass along through the flow. In the default VS2013 template class files, this private class can be found in the AccountController.cs file.

I was having a similar problem, not able to pass email from Google claims, and this solved the problem (note the added "Email" variable and references to it in both methods:

private class ExternalLoginData
{
    public string LoginProvider { get; set; }
    public string ProviderKey { get; set; }
    public string UserName { get; set; }
    public string Email { get; set; }

    public IList<Claim> GetClaims()
    {
        IList<Claim> claims = new List<Claim>();
        claims.Add(new Claim(ClaimTypes.NameIdentifier, ProviderKey, null, LoginProvider));

        if (UserName != null)
        {
            claims.Add(new Claim(ClaimTypes.Name, UserName, null, LoginProvider));
        }

        if (Email != null)
        {
            claims.Add(new Claim(ClaimTypes.Email, Email, null, LoginProvider));
        }

        return claims;
    }

    public static ExternalLoginData FromIdentity(ClaimsIdentity identity)
    {
        if (identity == null)
        {
            return null;
        }

        Claim providerKeyClaim = identity.FindFirst(ClaimTypes.NameIdentifier);

        if (providerKeyClaim == null || String.IsNullOrEmpty(providerKeyClaim.Issuer)
            || String.IsNullOrEmpty(providerKeyClaim.Value))
        {
            return null;
        }

        if (providerKeyClaim.Issuer == ClaimsIdentity.DefaultIssuer)
        {
            return null;
        }

        return new ExternalLoginData
        {
            LoginProvider = providerKeyClaim.Issuer,
            ProviderKey = providerKeyClaim.Value,
            UserName = identity.FindFirstValue(ClaimTypes.Name),
            Email = identity.FindFirstValue(ClaimTypes.Email)
        };
    }
}