Merge branch 'v13/dev' into v13/contrib

This commit is contained in:
Andy Butland
2025-02-04 12:35:49 +01:00
9 changed files with 143 additions and 33 deletions

View File

@@ -62,9 +62,16 @@ public static class HttpContextExtensions
// Update the HttpContext's user with the authenticated user's principal to ensure
// that subsequent requests within the same context will recognize the user
// as authenticated.
if (result.Succeeded)
if (result is { Succeeded: true, Principal.Identity: not null })
{
httpContext.User = result.Principal;
// We need to get existing identities that are not the backoffice kind and flow them to the new identity
// Otherwise we can't log in as both a member and a backoffice user
// For instance if you've enabled basic auth.
ClaimsPrincipal? authenticatedPrincipal = result.Principal;
IEnumerable<ClaimsIdentity> existingIdentities = httpContext.User.Identities.Where(x => x.IsAuthenticated && x.AuthenticationType != authenticatedPrincipal.Identity.AuthenticationType);
authenticatedPrincipal.AddIdentities(existingIdentities);
httpContext.User = authenticatedPrincipal;
}
return result;

View File

@@ -0,0 +1,18 @@
using System.Security.Claims;
using Microsoft.AspNetCore.Identity;
namespace Umbraco.Extensions;
public static class MemberClaimsPrincipalExtensions
{
/// <summary>
/// Tries to get specifically the member identity from the ClaimsPrincipal
/// </summary>
/// <remarks>
/// The identity returned is the one with default authentication type.
/// </remarks>
/// <param name="principal">The principal to find the identity in.</param>
/// <returns>The default authenticated authentication type identity.</returns>
public static ClaimsIdentity? GetMemberIdentity(this ClaimsPrincipal principal)
=> principal.Identities.FirstOrDefault(x => x.AuthenticationType == IdentityConstants.ApplicationScheme);
}

View File

@@ -1,4 +1,5 @@
using System.Globalization;
using System.Security.Claims;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Identity;
using Microsoft.Extensions.Logging;
@@ -124,8 +125,11 @@ public class MemberManager : UmbracoUserManager<MemberIdentityUser, MemberPasswo
/// <inheritdoc />
public virtual bool IsLoggedIn()
{
HttpContext? httpContext = _httpContextAccessor.HttpContext;
return httpContext?.User.Identity?.IsAuthenticated ?? false;
// We have to try and specifically find the member identity, it's entirely possible for there to be both backoffice and member.
ClaimsIdentity? memberIdentity = _httpContextAccessor.HttpContext?.User.GetMemberIdentity();
return memberIdentity is not null &&
memberIdentity.IsAuthenticated;
}
/// <inheritdoc />
@@ -181,23 +185,27 @@ public class MemberManager : UmbracoUserManager<MemberIdentityUser, MemberPasswo
/// <inheritdoc />
public virtual async Task<MemberIdentityUser?> GetCurrentMemberAsync()
{
if (_currentMember == null)
if (_currentMember is not null)
{
if (!IsLoggedIn())
{
return null;
}
_currentMember = await GetUserAsync(_httpContextAccessor.HttpContext?.User!);
return _currentMember;
}
if (IsLoggedIn() is false)
{
return null;
}
// Create a principal the represents the member security context.
var memberPrincipal = new ClaimsPrincipal(_httpContextAccessor.HttpContext?.User.GetMemberIdentity()!);
_currentMember = await GetUserAsync(memberPrincipal);
return _currentMember;
}
public virtual IPublishedContent? AsPublishedMember(MemberIdentityUser user) => _store.GetPublishedMember(user);
/// <summary>
/// This will check if the member has access to this path
/// This will check if the member has access to this path.
/// </summary>
/// <param name="path"></param>
/// <returns></returns>