using System.Security.Claims; using Microsoft.AspNetCore.Authentication; using Microsoft.AspNetCore.Authentication.Cookies; using Microsoft.AspNetCore.Http; using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Logging; using Microsoft.Extensions.Options; using Umbraco.Cms.Core; using Umbraco.Cms.Core.Preview; using Umbraco.Cms.Core.Security; using Umbraco.Cms.Core.Services; using Umbraco.Cms.Web.Common.AspNetCore; using Umbraco.Cms.Web.Common.Security; using Umbraco.Extensions; namespace Umbraco.Cms.Web.Common.Middleware; /// /// Ensures that preview pages (front-end routed) are authenticated with the back office identity appended to the /// principal alongside any default authentication that takes place /// public class PreviewAuthenticationMiddleware : IMiddleware { private readonly ILogger _logger; private readonly IPreviewTokenGenerator _previewTokenGenerator; private readonly IPreviewService _previewService; public PreviewAuthenticationMiddleware( ILogger logger, IPreviewTokenGenerator previewTokenGenerator, IPreviewService previewService) { _logger = logger; _previewTokenGenerator = previewTokenGenerator; _previewService = previewService; } /// public async Task InvokeAsync(HttpContext context, RequestDelegate next) { HttpRequest request = context.Request; // do not process if client-side request if (request.IsClientSideRequest()) { await next(context); return; } try { var isPreview = request.HasPreviewCookie() && !request.IsBackOfficeRequest(); if (isPreview) { Attempt backOfficeIdentityAttempt = await _previewService.TryGetPreviewClaimsIdentityAsync(); if (backOfficeIdentityAttempt.Success) { // Ok, we've got a real ticket, now we can add this ticket's identity to the current // Principal, this means we'll have 2 identities assigned to the principal which we can // use to authorize the preview and allow for a back office User. context.User.AddIdentity(backOfficeIdentityAttempt.Result!); } else { _logger.LogDebug("Could not transform previewCookie value into a claimsIdentity"); } } } catch (Exception ex) { // log any errors and continue the request without preview _logger.LogError("Unable to perform preview authentication: {message}", ex.Message); } finally { await next(context); } } }