From 2ea6b6d73fa9fab200db017e0bb4cc90bbdf31b1 Mon Sep 17 00:00:00 2001 From: Oleg Zhuk Date: Fri, 13 Oct 2023 12:59:21 +0200 Subject: [PATCH] PT-13919: SSO is reset to Password after some period. (#2708) fix: SSO is reset to Password after some period by implementing SecurityStampValidatorOptions.OnRefreshingPrincipal --- .../ConfigureSecurityStampValidatorOptions.cs | 34 +++++++++++++++++++ .../MergeClaimsIdentityExtensions.cs | 17 ++++++++++ src/VirtoCommerce.Platform.Web/Startup.cs | 2 ++ 3 files changed, 53 insertions(+) create mode 100644 src/VirtoCommerce.Platform.Security/ConfigureSecurityStampValidatorOptions.cs create mode 100644 src/VirtoCommerce.Platform.Security/Extensions/MergeClaimsIdentityExtensions.cs diff --git a/src/VirtoCommerce.Platform.Security/ConfigureSecurityStampValidatorOptions.cs b/src/VirtoCommerce.Platform.Security/ConfigureSecurityStampValidatorOptions.cs new file mode 100644 index 00000000000..36aaf15dfeb --- /dev/null +++ b/src/VirtoCommerce.Platform.Security/ConfigureSecurityStampValidatorOptions.cs @@ -0,0 +1,34 @@ +using System; +using System.Linq; +using System.Threading.Tasks; +using Microsoft.AspNetCore.Identity; +using Microsoft.Extensions.Options; +using VirtoCommerce.Platform.Security.Extensions; + +namespace VirtoCommerce.Platform.Security +{ + public class ConfigureSecurityStampValidatorOptions : IConfigureOptions + { + public void Configure(SecurityStampValidatorOptions options) + { + options.ValidationInterval = TimeSpan.FromMinutes(30); + + // When refreshing the principal, ensure custom claims that + // might have been set with an external identity continue + // to flow through to this new one. + options.OnRefreshingPrincipal = refreshingPrincipal => + { + var newIdentity = refreshingPrincipal.NewPrincipal?.Identities.First(); + var currentIdentity = refreshingPrincipal.CurrentPrincipal?.Identities.First(); + + if (currentIdentity is not null) + { + // Since this is refreshing an existing principal, we want to merge all claims. + newIdentity?.MergeAllClaims(currentIdentity); + } + + return Task.CompletedTask; + }; + } + } +} diff --git a/src/VirtoCommerce.Platform.Security/Extensions/MergeClaimsIdentityExtensions.cs b/src/VirtoCommerce.Platform.Security/Extensions/MergeClaimsIdentityExtensions.cs new file mode 100644 index 00000000000..ec44da1c2f5 --- /dev/null +++ b/src/VirtoCommerce.Platform.Security/Extensions/MergeClaimsIdentityExtensions.cs @@ -0,0 +1,17 @@ +using System.Linq; +using System.Security.Claims; + +namespace VirtoCommerce.Platform.Security.Extensions +{ + public static class MergeClaimsIdentityExtensions + { + public static void MergeAllClaims(this ClaimsIdentity destination, ClaimsIdentity source) + { + foreach (var claim in source.Claims + .Where(claim => !destination.HasClaim(claim.Type, claim.Value))) + { + destination.AddClaim(new Claim(claim.Type, claim.Value)); + } + } + } +} diff --git a/src/VirtoCommerce.Platform.Web/Startup.cs b/src/VirtoCommerce.Platform.Web/Startup.cs index 98e397771d3..d122c2ce419 100644 --- a/src/VirtoCommerce.Platform.Web/Startup.cs +++ b/src/VirtoCommerce.Platform.Web/Startup.cs @@ -264,6 +264,8 @@ public void ConfigureServices(IServiceCollection services) options.ClaimsIdentity.RoleClaimType = OpenIddictConstants.Claims.Role; }); + services.ConfigureOptions(); + // Load server certificate (from DB or file) and register it as a global singleton // to allow the platform hosting under the cert ICertificateLoader certificateLoader;