First, I found out that you might be able to add custom claim by extending IProfileService. It works well for some random claim, but not "iat". Strange, it must be filtered somewhere then.
Then browsing the source code in github, I found out that it was indeed filtered by FilterProtocolClaims method in DefaultClaimService:
https://github.com/IdentityServer/IdentityServer4/blob/master/src/IdentityServer4/src/Services/Default/DefaultClaimsService.cs
Ok, so I think I can extend DefaultClaimsService. I tried by adding a custom class in the StartUp using the following code:
services.AddTransient<IClaimsService,CustomClaimsService>();
Sadly, it didn't work.
Then browsing the source code in github, I found out that it was indeed filtered by FilterProtocolClaims method in DefaultClaimService:
https://github.com/IdentityServer/IdentityServer4/blob/master/src/IdentityServer4/src/Services/Default/DefaultClaimsService.cs
Ok, so I think I can extend DefaultClaimsService. I tried by adding a custom class in the StartUp using the following code:
services.AddTransient<IClaimsService,CustomClaimsService>();
Sadly, it didn't work.
I then learn that you can add the service under builder.Services, so I tried the following:
services.AddIdentityServer()
.... (removed for brevity)
.Services.AddTransient<IClaimsService, CustomClaimsService>();
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:
var authTime = claims.FirstOrDefault(c => c.Type == JwtClaimTypes.AuthenticationTime);
if (authTime != null)
{
outputClaims.Add(new Claim(JwtClaimTypes.IssuedAt, authTime.Value, ClaimValueTypes.Integer));
}