Add member auth to the Delivery API (#14730)

* Refactor OpenIddict for shared usage between APIs + implement member authentication and handling within the Delivery API

* Make SwaggerRouteTemplatePipelineFilter UI config overridable

* Enable token revocation + rename logout endpoint to signout

* Add default implementation of SwaggerGenOptions configuration for enabling Delivery API member auth in Swagger

* Correct notification handling when (un)protecting content

* Fixing integration test framework

* Cleanup test to not execute some composers twice

* Update paths to match docs

* Return Forbidden when a member is authorized but not allowed to access the requested resource

* Cleanup

* Rename RequestMemberService to RequestMemberAccessService

* Rename badly named variable

* Review comments

* Hide the auth controller from Swagger

* Remove semaphore

* Add security requirements for content API operations in Swagger

* Hide the back-office auth endpoints from Swagger

* Fix merge

* Update back-office API auth endpoint paths + add revoke and sign-out endpoints (as of now they do not exist, a separate task will fix that)

* Swap endpoint order to maintain backwards compat with the current login screen for new back-office (will be swapped back again to ensure correct .well-known endpoints, see FIXME comment)

* Make "items by IDs" endpoint support member auth

* Add 401 and 403 to "items by IDs" endpoint responses

---------

Co-authored-by: Bjarke Berg <mail@bergmania.dk>
Co-authored-by: Elitsa <elm@umbraco.dk>
This commit is contained in:
Kenn Jacobsen
2023-09-26 09:22:45 +02:00
committed by GitHub
parent 624f9a0508
commit 83321a8fad
50 changed files with 1521 additions and 276 deletions

View File

@@ -1,3 +1,4 @@
using System.Security.Claims;
using Microsoft.AspNetCore.Authentication;
using Microsoft.AspNetCore.Identity;
using Umbraco.Cms.Core.Security;
@@ -13,7 +14,7 @@ public interface IMemberSignInManager
Task SignOutAsync();
AuthenticationProperties ConfigureExternalAuthenticationProperties(string provider, string redirectUrl, string? userId = null);
AuthenticationProperties ConfigureExternalAuthenticationProperties(string? provider, string? redirectUrl, string? userId = null);
Task<ExternalLoginInfo?> GetExternalLoginInfoAsync(string? expectedXsrf = null);
@@ -24,4 +25,6 @@ public interface IMemberSignInManager
Task<MemberIdentityUser?> GetTwoFactorAuthenticationUserAsync();
Task<SignInResult> TwoFactorSignInAsync(string provider, string code, bool isPersistent, bool rememberClient);
Task<ClaimsPrincipal> CreateUserPrincipalAsync(MemberIdentityUser user) => Task.FromResult(new ClaimsPrincipal());
}

View File

@@ -1,3 +1,4 @@
using System.Security.Claims;
using Microsoft.AspNetCore.Http;
using Microsoft.Extensions.DependencyInjection;
using Umbraco.Cms.Core.Security;
@@ -19,16 +20,25 @@ public class PublicAccessChecker : IPublicAccessChecker
_contentService = contentService;
}
/// <inheritdoc/>
public async Task<PublicAccessStatus> HasMemberAccessToContentAsync(int publishedContentId)
=> await HasMemberAccessToContentAsync(publishedContentId, httpContext => httpContext.User);
/// <inheritdoc/>
public async Task<PublicAccessStatus> HasMemberAccessToContentAsync(int publishedContentId, ClaimsPrincipal claimsPrincipal)
=> await HasMemberAccessToContentAsync(publishedContentId, _ => claimsPrincipal);
private async Task<PublicAccessStatus> HasMemberAccessToContentAsync(int publishedContentId, Func<HttpContext, ClaimsPrincipal> getClaimsPrincipal)
{
HttpContext httpContext = _httpContextAccessor.GetRequiredHttpContext();
IMemberManager memberManager = httpContext.RequestServices.GetRequiredService<IMemberManager>();
if (httpContext.User.Identity == null || !httpContext.User.Identity.IsAuthenticated)
ClaimsPrincipal claimsPrincipal = getClaimsPrincipal(httpContext);
if (claimsPrincipal.Identity is not { IsAuthenticated: true })
{
return PublicAccessStatus.NotLoggedIn;
}
MemberIdentityUser? currentMember = await memberManager.GetUserAsync(httpContext.User);
MemberIdentityUser? currentMember = await memberManager.GetUserAsync(claimsPrincipal);
if (currentMember == null)
{
return PublicAccessStatus.NotLoggedIn;