Keep custom claims that are flowed from autolinking
When auto linking with callbacks such as OnExternalLogin, custom claims can be added to the user which are flowed to the auth ticket/identity. However, when the security stamp validator executes, the identity is re-created manually without any knowledge of those custom claims so they are lost. This ensures that those custom claims flow through to the re-generated identity during the security stamp validation phase.
This commit is contained in:
@@ -169,11 +169,24 @@ namespace Umbraco.Web.Security
|
||||
// Enables the application to validate the security stamp when the user
|
||||
// logs in. This is a security feature which is used when you
|
||||
// change a password or add an external login to your account.
|
||||
OnValidateIdentity = SecurityStampValidator
|
||||
.OnValidateIdentity<BackOfficeUserManager, BackOfficeIdentityUser, int>(
|
||||
TimeSpan.FromMinutes(30),
|
||||
(manager, user) => manager.GenerateUserIdentityAsync(user),
|
||||
identity => identity.GetUserId<int>()),
|
||||
OnValidateIdentity = context =>
|
||||
{
|
||||
var identity = context.Identity;
|
||||
|
||||
return SecurityStampValidator
|
||||
.OnValidateIdentity<BackOfficeUserManager, BackOfficeIdentityUser, int>(
|
||||
TimeSpan.FromMinutes(3),
|
||||
async (manager, user) =>
|
||||
{
|
||||
var regenerated = await manager.GenerateUserIdentityAsync(user);
|
||||
|
||||
// Keep any custom claims from the original identity
|
||||
regenerated.MergeClaimsFromBackOfficeIdentity(identity);
|
||||
|
||||
return regenerated;
|
||||
},
|
||||
identity => identity.GetUserId<int>())(context);
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
|
||||
@@ -27,12 +27,6 @@ namespace Umbraco.Web.Security
|
||||
{
|
||||
var baseIdentity = await base.CreateAsync(manager, user, authenticationType);
|
||||
|
||||
// now we can flow any custom claims that the actual user has currently assigned which could be done in the OnExternalLogin callback
|
||||
foreach (var claim in user.Claims)
|
||||
{
|
||||
baseIdentity.AddClaim(new Claim(claim.ClaimType, claim.ClaimValue));
|
||||
}
|
||||
|
||||
var umbracoIdentity = new UmbracoBackOfficeIdentity(baseIdentity,
|
||||
user.Id,
|
||||
user.UserName,
|
||||
@@ -40,12 +34,16 @@ namespace Umbraco.Web.Security
|
||||
user.CalculatedContentStartNodeIds,
|
||||
user.CalculatedMediaStartNodeIds,
|
||||
user.Culture,
|
||||
//NOTE - there is no session id assigned here, this is just creating the identity, a session id will be generated when the cookie is written
|
||||
// NOTE - there is no session id assigned here, this is just creating the identity, a session id will be generated when the cookie is written
|
||||
Guid.NewGuid().ToString(),
|
||||
user.SecurityStamp,
|
||||
user.AllowedSections,
|
||||
user.Roles.Select(x => x.RoleId).ToArray());
|
||||
|
||||
// now we can flow any custom claims that the actual user has currently
|
||||
// assigned which could be done in the OnExternalLogin callback
|
||||
umbracoIdentity.MergeClaimsFromBackOfficeIdentity(user);
|
||||
|
||||
return umbracoIdentity;
|
||||
}
|
||||
}
|
||||
|
||||
34
src/Umbraco.Web/Security/ClaimsIdentityExtensions.cs
Normal file
34
src/Umbraco.Web/Security/ClaimsIdentityExtensions.cs
Normal file
@@ -0,0 +1,34 @@
|
||||
using System.Linq;
|
||||
using System.Security.Claims;
|
||||
using Umbraco.Core.Models.Identity;
|
||||
using Constants = Umbraco.Core.Constants;
|
||||
|
||||
namespace Umbraco.Web.Security
|
||||
{
|
||||
internal static class ClaimsIdentityExtensions
|
||||
{
|
||||
// Ignore these Claims when merging, these claims are dynamically added whenever the ticket
|
||||
// is re-issued and we don't want to merge old values of these.
|
||||
private static readonly string[] IgnoredClaims = new[] { ClaimTypes.CookiePath, Constants.Security.SessionIdClaimType };
|
||||
|
||||
internal static void MergeClaimsFromBackOfficeIdentity(this ClaimsIdentity destination, ClaimsIdentity source)
|
||||
{
|
||||
foreach (var claim in source.Claims
|
||||
.Where(claim => !IgnoredClaims.Contains(claim.Type))
|
||||
.Where(claim => !destination.HasClaim(claim.Type, claim.Value)))
|
||||
{
|
||||
destination.AddClaim(new Claim(claim.Type, claim.Value));
|
||||
}
|
||||
}
|
||||
|
||||
internal static void MergeClaimsFromBackOfficeIdentity(this ClaimsIdentity destination, BackOfficeIdentityUser source)
|
||||
{
|
||||
foreach (var claim in source.Claims
|
||||
.Where(claim => !IgnoredClaims.Contains(claim.ClaimType))
|
||||
.Where(claim => !destination.HasClaim(claim.ClaimType, claim.ClaimValue)))
|
||||
{
|
||||
destination.AddClaim(new Claim(claim.ClaimType, claim.ClaimValue));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -288,6 +288,7 @@
|
||||
<Compile Include="Security\BackOfficeExternalLoginProviderErrorMiddlware.cs" />
|
||||
<Compile Include="Security\BackOfficeExternalLoginProviderErrors.cs" />
|
||||
<Compile Include="Security\BackOfficeExternalLoginProviderOptions.cs" />
|
||||
<Compile Include="Security\ClaimsIdentityExtensions.cs" />
|
||||
<Compile Include="Security\SignOutAuditEventArgs.cs" />
|
||||
<Compile Include="Security\UserInviteEventArgs.cs" />
|
||||
<Compile Include="Services\DashboardService.cs" />
|
||||
|
||||
Reference in New Issue
Block a user