Identityserver4: Docs improvement idea: explain claim mapping

Created on 27 Jun 2017  路  5Comments  路  Source: IdentityServer/IdentityServer4

I wanted to propose an enhancement to the docs based on my experience reading them to learn about IdentityServer4. I'm happy to work on a draft, but I wanted to run the idea by you guys first--let me know what you think!

The docs on the UserInfo endpoint contain the following statement:

Depending on the granted scopes, the UserInfo endpoint will return the mapped claims (at least the openid scope is required).

I had set up a basic IdentityServer where my users were stored in a database managed through ASP.NET identity, was attempting to read the docs to figure out how to get my UserInfo endpoint to return claims beyond "sub" and "name." For example, I wanted to include "email" (which is built-in to IdentityUser) and a custom "birthday" claim (which I had added by creating an ApplicationUser class that extended IdentityUser). But these weren't getting included in "mapped claims," and it wasn't clear from the docs how I could fix this.

After lots of googling, randomly tried following the instructions on this blog post (which didnt' mention IdentityServer at all) to add a custom UserClaimsPrincipalFactory to my Services pipeline that creates an "email" and "birthdate" claim from User.Email and User.Birthday, and it seemed to do the trick.

public class AppClaimsPrincipalFactory : UserClaimsPrincipalFactory<ApplicationUser, IdentityRole>
    {
        public AppClaimsPrincipalFactory(
            UserManager<ApplicationUser> userManager,
            RoleManager<IdentityRole> roleManager,
            IOptions<IdentityOptions> optionsAccessor)
        : base(userManager, roleManager, optionsAccessor)
        { }

        public async override Task<ClaimsPrincipal> CreateAsync(ApplicationUser user)
        {
            var principal = await base.CreateAsync(user);


            if (!string.IsNullOrWhiteSpace(user.Email))
            {
                ((ClaimsIdentity)principal.Identity).AddClaims(new[] {
                    new Claim("email", user.Email),
                });
            }

            if (!string.IsNullOrWhiteSpace(user.Birthday.ToString()))
            {
                ((ClaimsIdentity)principal.Identity).AddClaims(new[] {
                    new Claim("birthdate", user.Birthday.ToString()),
                });
            }

            return principal;
        }
    }

...and adding this class to DI as follows:

services.AddScoped<Microsoft.AspNetCore.Identity.IUserClaimsPrincipalFactory<ApplicationUser>, AppClaimsPrincipalFactory>();

Two questions:

  1. Is this the right way to accomplish my goals?
  2. Would you be opening to a change to enhance the UserInfo documentation (or on a separate page) that unpacks what "mapped claims" are and how to control them? That way other people trying to do the same thing will find something helpful.
docs

Most helpful comment

I'd added some more docs on this to hopefully close the loop on this.

All 5 comments

If you add the IdentityServer4.AspNetIdentity package, there is a service extension called AddIdentityServerUserClaimsPrincipalFactory which adds a UserClaimFactory to the pipeline.

It supports username, email and phone number, saved using JWT claim types.

@Genbox thanks for pointing that out! I tried it out by adding this line to my ConfigureServices:

services.AddIdentityServerUserClaimsPrincipalFactory<ApplicationUser, IdentityRole>();

That does seem to have the effect of adding the preferred_username, email, and email_verified fields to the UserInfo response. Interestingly, it doesn't seem to add phone number, but I don't know why.

I also stumbled across this example of how to implement an IProfileService to accomplish a similar goal.

So it looks like there are at least 3 ways to control the claims coming from the UserInfo endpoint:

  1. create a custom UserClaimsPrincipalFactory (my original post)
  2. user the AddIdentityServerUserClaimsPrincipalFactory extension method (from @Genbox)
  3. Implement an iProfileService (which I found later by chance)

@brockallen and @leastprivilege - is this a complete list? Is one of the techniques recommended over others? Are they documented anywhere? If not, would you like me to take a stab at an article that explains them in the context of trying to control the output of UserInfo in IdentityServer (I think I have a handle on the UserPrincipleClaimsFactory method, but I still have questions about AddIdentityServerUserClaimsPrincipalFactory and iProfileService, so I might not be able to do as good a job about those).

I recently (and coincidentally) added docs for the profile service: http://docs.identityserver.io/en/release/reference/profileservice.html. How claims come back in the id_token vs. userinfo, IMO, are not really our responsibility to fully document, since those are spec concerns, and, IMO, consumers of IdentityServer are expected to know (at least generally) how the specs work.

Some of the other things you mention are ASP.NET Identity specific, and were also recently (and coincidentally) documented: http://docs.identityserver.io/en/release/reference/aspnet_identity.html. How the internals of ASP.NET Identity claim mapping, IMO, are not our responsibility to document. That's a Microsoft product and if they don't do it, then it's on them.

Having said all of the above, and given the docs I added related to these concepts I think I'm inclined to close this issue. If you feel that there is more to add to the docs I listed, then we'd love contributions via a PR. If you wanted to write up a separate blog post with more info and feel like doing the education on the specs and/or ASP.NET Identity, then feel free and we can link to it from our docs.

I'd added some more docs on this to hopefully close the loop on this.

This thread has been automatically locked since there has not been any recent activity after it was closed. Please open a new issue for related bugs.

Was this page helpful?
0 / 5 - 0 ratings