Search code examples
identityserver4

IProfileService - Claims in tokens


I am currently implementing an Identity Server solution and I needed some help with the IProfileService and how it works exactly.

It exposes a method called "GetProfileDataAsync". I understand that this is called when IS4 returns a token. So that means the person gets to the login screen, inputs his details, and then before IS4 returns an Identity token and an Access token, this method will get called to add additional claims.

I am currently trying to figure out the best way to implement roles and permissions based authorization. Currently I need to have access to both the permissions and roles that the user has assigned as that is what our existing code does and we are just switching our authentication model to IS4 but keeping the User Management to be as it currently is.

Questions then...

  1. How best do I implement it? I currently have an ApplicationUser class which implements IIdentity. So should I add a list of roles in there and then a list of permissions, and then populate it when I go get it from the DB when the user does a LogIn?

E.G. In this method ApplicationUser user = await _userRepo.FindByUsername(model.Username);

The alternative is to add each role and each permission as a claim in my UserProfileService, specifically in the method below
public virtual async Task GetProfileDataAsync(ProfileDataRequestContext context)

  1. I read the following

    Often IdentityServer requires identity information about users when creating tokens or when handling requests to the userinfo or introspection endpoints. By default, IdentityServer only has the claims in the authentication cookie to draw upon for this identity data. It is impractical to put all of the possible claims needed for users into the cookie, so IdentityServer defines an extensibility point for allowing claims to be dynamically loaded as needed for a user. This extensibility point is the IProfileService and it is common for a developer to implement this interface to access a custom database or API that contains the identity data for users.

With the above situation, as I have implement the IProfileService, does that mean that all claims that are loaded will be automatically returned and put into the Identity/Access token? Does that mean that for every request that is made to the API, my application will be sending in a token (in the cookie) which could get quite big with these claims that include roles and permissions? What is the alternative as the above statement from the IS4 website mentions it is impractical


Solution

  • How best do I implement it? I currently have an ApplicationUser class which implements IIdentity. So should I add a list of roles in there and then a list of permissions, and then populate it when I go get it from the DB when the user does a LogIn?

    There is two kind of things here, Roles and Permissions. Roles are data and you can add them to the token and pass to clients and APIs. you can save the Roles in DB any how which fits your design. To have the roles in the token you need to fetch them in ProfileService and add to token. Sth like this:

    public async Task GetProfileDataAsync(ProfileDataRequestContext context)
        {
            context.IssuedClaims.AddRange(context.Subject.Claims);
    
            var user = await _userManager.GetUserAsync(context.Subject);
    
            var roles = await _userManager.GetRolesAsync(user);
    
            foreach (var role in roles)
            {
                context.IssuedClaims.Add(new Claim(JwtClaimTypes.Role, role));
            }
        }
    

    After doing this your token should contains the roles. make sure to verify the token on https://jwt.ms/

    But Permissions are more of real time calculation things.We need to decide about permissions on API based on the user info, user's role or any other data available. For example a user may have the role as delete (means user can delete things). If this user call order API and tries to delete some one else's order it has to be declined. Means delete permission MUST be calculated based on user's ID + user's role + Order's owner ID.

    With the above situation, as I have implement the IProfileService, does that mean that all claims that are loaded will be automatically returned and put into the Identity/Access token? Does that mean that for every request that is made to the API, my application will be sending in a token (in the cookie) which could get quite big with these claims that include roles and permissions? What is the alternative as the above statement from the IS4 website mentions it is impractical

    Yes the profile service is called whenever IDS4 needs to return claims about a user to a client applications. If you request an identity and access token - it will get called twice (since you might be putting different claims into each token type).

    What is the alternative as the above statement from the IS4 website mentions it is impractical

    You should just fetch the data that you need - not extra. As I mentioned above permissions should be calculated on the fly and not be in the token. Also you can use cache in the ProfileService. But if you use cache you are the one responsible to manage in your code.