.net-coreasp.net-core-webapidapperclaims

How to dynamically map ClaimType from string stored in SQL


I have a UserClaims table in a SQL DB. The table stores Id (int), UserId (int - FK), Type (nvarchar), and Value (nvarchar). The Foreign Key maps to the Id on the User table. I am currently using Dapper to pull records out of the DB.

When retrieving the UserClaims for a specific User, I would like to have the code be "future-proofed" so that, if I add already defined UserClaims (based on the ClaimTypes static class in System.Security.Claims), they will map out and can be referred to elsewhere in the code "natively".

The goal would be to have code like this be functional (obviously this isn't working):

foreach(var c in user.UserClaims)
{
    if (!String.IsNullOrEmpty(c.Value) && !String.IsNullOrEmpty(c.Type))
    {
        if (c.Type == "Role")
            authClaims.Add(new Claim(ClaimTypes.Role, c.Value));
        else
        {
            var _type = typeof(ClaimTypes);
            var field = _type?.GetField(c.Type);

            if (field != null)
                authClaims.Add(new Claim(field, c.Value));
            else
                authClaims.Add(new Claim(c.Type, c.Value));
        }
    }
}

In the above code, if I can map the Type coming from the DB to one of the fields in ClaimTypes, then I want to use that field. If not, then I can create a custom Claim Type that I will need to write some handler for.

Role was an easy ClaimType to map, and I could continue that pattern, or possibly implement a switch to map more well-known types, but I was hoping I could leverage the already-existing functionality more cleanly by mapping back to the ClaimTypes based on their name.

Examples:

    authClaims.Add(new Claim(field, c.Value));
    // could be equivalent to something like:               
    authClaims.Add(new Claim(ClaimTypes.Country, "United States"));
    // stored in the UserClaims table as:
    // 5, 1, 'Country', 'United States' -- OR --
    authClaims.Add(new Claim(ClaimTypes.MobilePhone, "+11234567890"));
    // 6, 1, 'MobilePhone', '+11234567890'

Solution

  • Try to change your code

    var field = _type?.GetField(c.Type);
    

    into

     var field = _type?.GetField(c.Type)?.GetValue(null).ToString();
    

    Below is a work demo, you can refer to it:

        var UserClaims = new List<UserClaims>() { 
        new UserClaims() { Type="Country",Value= "United States" },
        new UserClaims() { Type="CC",Value= "C1" }
    };
    var authClaims = new List<Claim>();
    foreach (var c in UserClaims)
    {
        if (!String.IsNullOrEmpty(c.Value) && !String.IsNullOrEmpty(c.Type))
        {
                var _type = typeof(ClaimTypes);
                var field = _type?.GetField(c.Type)?.GetValue(null).ToString();
    
                if (field != null)
                    authClaims.Add(new Claim(field, c.Value));
                else
                    authClaims.Add(new Claim(c.Type, c.Value));
           
        }
    }
    

    result:

    enter image description here