V13: Fix members while using basic auth. (#18206)
* Flow additional identities to new principal * Add extension to more easily get member identity * Ensure the member is used instead of the backoffice user in `MemberManager` * Update snippet * Fix the comment that I broke * Update src/Umbraco.Web.Common/Extensions/MemberClaimsPrincipalExtensions.cs Co-authored-by: Andy Butland <abutland73@gmail.com> --------- Co-authored-by: Andy Butland <abutland73@gmail.com>
This commit is contained in:
@@ -5,7 +5,7 @@
|
||||
@using Umbraco.Extensions
|
||||
|
||||
@{
|
||||
var isLoggedIn = Context.User?.Identity?.IsAuthenticated ?? false;
|
||||
var isLoggedIn = Context.User.GetMemberIdentity()?.IsAuthenticated ?? false;
|
||||
var logoutModel = new PostRedirectModel();
|
||||
// You can modify this to redirect to a different URL instead of the current one
|
||||
logoutModel.RedirectUrl = null;
|
||||
@@ -15,7 +15,7 @@
|
||||
{
|
||||
<div class="login-status">
|
||||
|
||||
<p>Welcome back <strong>@Context?.User?.Identity?.Name</strong>!</p>
|
||||
<p>Welcome back <strong>@Context.User?.GetMemberIdentity()?.Name</strong>!</p>
|
||||
|
||||
@using (Html.BeginUmbracoForm<UmbLoginStatusController>("HandleLogout", new { RedirectUrl = logoutModel.RedirectUrl }))
|
||||
{
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
@@ -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>
|
||||
|
||||
Reference in New Issue
Block a user