diff --git a/modules/identity/src/Volo.Abp.Identity.AspNetCore/Volo/Abp/Identity/AspNetCore/AbpSecurityStampValidatorCallback.cs b/modules/identity/src/Volo.Abp.Identity.AspNetCore/Volo/Abp/Identity/AspNetCore/AbpSecurityStampValidatorCallback.cs
new file mode 100644
index 0000000000..2adc65d497
--- /dev/null
+++ b/modules/identity/src/Volo.Abp.Identity.AspNetCore/Volo/Abp/Identity/AspNetCore/AbpSecurityStampValidatorCallback.cs
@@ -0,0 +1,33 @@
+using System.Linq;
+using System.Threading.Tasks;
+using Microsoft.AspNetCore.Identity;
+
+namespace Volo.Abp.Identity.AspNetCore
+{
+ public class AbpSecurityStampValidatorCallback
+ {
+ ///
+ /// Implements callback for SecurityStampValidator's OnRefreshingPrincipal event.
+ /// https://github.com/IdentityServer/IdentityServer4/blob/main/src/AspNetIdentity/src/SecurityStampValidatorCallback.cs
+ ///
+ public class SecurityStampValidatorCallback
+ {
+ ///
+ /// Maintains the claims captured at login time that are not being created by ASP.NET Identity.
+ /// This is needed to preserve claims such as idp, auth_time, amr.
+ ///
+ /// The context.
+ ///
+ public static Task UpdatePrincipal(SecurityStampRefreshingPrincipalContext context)
+ {
+ var newClaimTypes = context.NewPrincipal.Claims.Select(x => x.Type).ToArray();
+ var currentClaimsToKeep = context.CurrentPrincipal.Claims.Where(x => !newClaimTypes.Contains(x.Type)).ToArray();
+
+ var id = context.NewPrincipal.Identities.First();
+ id.AddClaims(currentClaimsToKeep);
+
+ return Task.CompletedTask;
+ }
+ }
+ }
+}