[{"data":1,"prerenderedAt":120},["ShallowReactive",2],{"/2025/11/24-identityserver4-custom-claims-and-services":3},{"id":4,"title":5,"body":6,"date":111,"description":80,"extension":112,"meta":113,"navigation":114,"path":115,"robots":116,"seo":117,"stem":118,"__hash__":119},"posts/2025/11/24 IdentityServer4-custom-claims-and-services.md","24 IdentityServer4 Custom Claims And Services",{"type":7,"value":8,"toc":108},"minimark",[9,18,25,28,31,33,45,47,50,63,65,68,81,84,86,89,97,100],[10,11,13],"post-title",{":date":12},"date",[14,15,17],"h1",{"id":16},"identityserver4-custom-claims-and-services","IdentityServer4 Custom Claims and Services",[19,20,21],"notes",{},[22,23,24],"p",{},"This is a repost from my old blog. First posted in 3/28/2020.",[26,27],"br",{},[22,29,30],{},"It is all started from my intention to add \"iat\" claim to access token. IssuedAt (iat) claim is optional so it takes a bit of searching to figure out how to do that. Add that to my unfamiliarity with IdentityServer4, it becomes quite a task. At this point, I am using IdentityServer4 version 3.0.2.0.",[26,32],{},[22,34,35,36,44],{},"First, I found out that you might be able to add custom claim by extending ",[37,38,43],"code",{"className":39},[40,41,42],"bg-gray-200","p-2","rounded","IProfileService",". It works well for some random claim, but not \"iat\". Strange, it must be filtered somewhere then.",[26,46],{},[22,48,49],{},"Then browsing the source code in github, I found out that it was indeed filtered by FilterProtocolClaims method in DefaultClaimService:",[22,51,52],{},[53,54,57],"span",{"className":55},[56],"text-blue-600",[58,59,60],"a",{"href":60,"rel":61},"https://github.com/IdentityServer/IdentityServer4/blob/master/src/IdentityServer4/src/Services/Default/DefaultClaimsService.cs",[62],"nofollow",[26,64],{},[22,66,67],{},"Ok, so I think I can extend DefaultClaimsService. I tried by adding a custom class in the StartUp using the following code:",[69,70,71],"code-block",{},[72,73,78],"pre",{"className":74,"code":76,"language":77},[75],"language-text","services.AddTransient\u003CIClaimsService,CustomClaimsService>();\n","text",[37,79,76],{"__ignoreMap":80},"",[22,82,83],{},"Sadly, it didn't work.",[26,85],{},[22,87,88],{},"I then learn that you can add the service under builder.Services, so I tried the following:",[69,90,91],{},[72,92,95],{"className":93,"code":94,"language":77},[75],"services.AddIdentityServer()\n      .... (removed for brevity)\n      .Services.AddTransient\u003CIClaimsService, CustomClaimsService>();\n",[37,96,94],{"__ignoreMap":80},[22,98,99],{},"That works! My \"iat\" claim is included in the access token. In my case, I choose to overwrite GetStandardSubjectClaims method because that is where \"auth_time\" claim is set and \"iat\" claim has the same value as \"auth_time\" claim using code like the following:",[69,101,102],{},[72,103,106],{"className":104,"code":105,"language":77},[75],"var authTime = claims.FirstOrDefault(c => c.Type == JwtClaimTypes.AuthenticationTime);\nif (authTime != null)\n{\n   outputClaims.Add(new Claim(JwtClaimTypes.IssuedAt, authTime.Value, ClaimValueTypes.Integer));\n}\n",[37,107,105],{"__ignoreMap":80},{"title":80,"searchDepth":109,"depth":109,"links":110},2,[],"2025-11-24T00:00:00.000Z","md",{},true,"/2025/11/24-identityserver4-custom-claims-and-services",null,{"title":5,"description":80},"2025/11/24 IdentityServer4-custom-claims-and-services","XkCPbWuYdR8wjI7TfwxVagqZq6cQ7SW7uZH1eF-spgI",1785167452439]