2021-01-08 17:21:35 +11:00
using System ;
using System.Threading.Tasks ;
2020-12-08 16:33:50 +11:00
using Microsoft.AspNetCore.Authentication.Cookies ;
2020-08-07 00:48:32 +10:00
using Microsoft.AspNetCore.Http ;
using Microsoft.Extensions.DependencyInjection ;
2021-02-19 11:45:58 +01:00
using Microsoft.Extensions.Logging ;
2020-08-07 00:48:32 +10:00
using Microsoft.Extensions.Options ;
using Umbraco.Extensions ;
2021-02-19 11:45:58 +01:00
namespace Umbraco.Cms.Web.Common.Middleware
2020-08-07 00:48:32 +10:00
{
/// <summary>
/// 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
/// </summary>
public class PreviewAuthenticationMiddleware : IMiddleware
{
2021-02-19 11:45:58 +01:00
private readonly ILogger < PreviewAuthenticationMiddleware > _logger ;
public PreviewAuthenticationMiddleware ( ILogger < PreviewAuthenticationMiddleware > logger ) = > _logger = logger ;
2021-01-08 17:21:35 +11:00
/// <inheritdoc/>
2020-08-07 00:48:32 +10:00
public async Task InvokeAsync ( HttpContext context , RequestDelegate next )
{
var request = context . Request ;
2021-02-19 11:45:58 +01:00
// do not process if client-side request
if ( request . IsClientSideRequest ( ) )
{
await next ( context ) ;
return ;
}
try
2020-08-07 00:48:32 +10:00
{
var isPreview = request . HasPreviewCookie ( )
2021-02-19 11:45:58 +01:00
& & context . User ! = null
& & ! request . IsBackOfficeRequest ( ) ;
2020-08-07 00:48:32 +10:00
if ( isPreview )
{
var cookieOptions = context . RequestServices . GetRequiredService < IOptionsSnapshot < CookieAuthenticationOptions > > ( )
2021-02-19 11:45:58 +01:00
. Get ( Core . Constants . Security . BackOfficeAuthenticationType ) ;
2020-08-07 00:48:32 +10:00
if ( cookieOptions = = null )
2021-01-08 17:21:35 +11:00
{
2021-02-19 11:45:58 +01:00
throw new InvalidOperationException ( "No cookie options found with name " + Core . Constants . Security . BackOfficeAuthenticationType ) ;
2021-01-08 17:21:35 +11:00
}
2020-08-07 00:48:32 +10:00
2020-12-08 16:33:50 +11:00
// If we've gotten this far it means a preview cookie has been set and a front-end umbraco document request is executing.
2020-08-07 00:48:32 +10:00
// In this case, authentication will not have occurred for an Umbraco back office User, however we need to perform the authentication
// for the user here so that the preview capability can be authorized otherwise only the non-preview page will be rendered.
2022-03-29 13:44:21 +02:00
if ( cookieOptions . Cookie . Name is not null & & request . Cookies . TryGetValue ( cookieOptions . Cookie . Name , out var cookie ) )
2020-08-07 00:48:32 +10:00
{
var unprotected = cookieOptions . TicketDataFormat . Unprotect ( cookie ) ;
2021-02-19 11:45:58 +01:00
var backOfficeIdentity = unprotected ? . Principal . GetUmbracoIdentity ( ) ;
if ( backOfficeIdentity ! = null )
2020-08-07 00:48:32 +10:00
{
2021-02-19 11:45:58 +01:00
// 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.
2022-03-29 13:44:21 +02:00
context . User ? . AddIdentity ( backOfficeIdentity ) ;
2020-08-07 00:48:32 +10:00
}
}
}
}
2021-02-19 11:45:58 +01:00
catch ( Exception ex )
{
// log any errors and continue the request without preview
_logger . LogError ( $"Unable to perform preview authentication: {ex.Message}" ) ;
}
finally
{
await next ( context ) ;
}
2020-08-07 00:48:32 +10:00
}
}
}