Search code examples
c#jwtclaims-based-identitythinktecture-ident-server

Add a claim to JWT as an array?


Using thinktecture JWT authentication resource owner flow, I use the claims part of JWT for client consumption.

My question is that if it's possible to add a claim in identity server and decode it as an array in client.

There is no ClaimTypeValues for array type.

As a workaround:

var user = IdentityServerPrincipal.Create(response.UserName, response.UserName);
user.Identities.First()
    .AddClaims(
        new List<Claim>()
        {
             new Claim(ClaimTypes.Name, response.UserName),
             new Claim(ClaimTypes.Email, response.Email),
             new Claim(FullName, response.FullName),
             new Claim(AuthorizedCompanies,JsonConvert.SerializeObject(response.AuthorizedCompanies))
        });

return new AuthenticateResult(user);

I add claim as json array to claim for AuthorizedCompanies and parse it in client side.What is the design pattern here if any ?


Solution

  • Speaking from personal experience, it is easier to inter-op with claim stores when the ValueType is always type "String". Although it may seem counter intuitive when you know you are dealing with a complex type, it is at least simple to understand.

    The way I have approached this need for an array is to have my application code expect multiple claims to be present for the claim type in question, and keep each claim value of a simple type.

    Examp:

    var authorizeCompanies = identity.FindAll(AuthorizedCompanies).Select(c => c.Value);
    

    And of course, you also add them that way:

    identity.AddClaim(ClaimTypes.Name, response.UserName);
    identity.AddClaim(AuthorizedCompanies, "CompanyX");
    identity.AddClaim(AuthorizedCompanies, "CompanyY");
    identity.AddClaim(AuthorizedCompanies, "CompanyZ");
    

    IdentityServer supports this model out of the box. When generating a token for an identity such as this, it automatically writes the values for that claim out as an array.

    {
        "aud": "Identity Server example/resources", 
        "iss": "Identity Server example", 
        "exp": 1417718816, 
        "sub": "1234",
        "scope": ["read", "write"], // <-- HERE
        "foo": ["bar", "baz"],      // <-- HERE TOO!
        "nbf": 1417632416
    }
    

    This approach to claims is in contrast to assuming all claims are a one-to-one mapping of type -> value.