Umbraco 17 Upgrade Progress Update and Authentication Issue

Hi Team,

As part of the Mastiff application upgrade, we have successfully completed the .NET 10 and Umbraco 17 NuGet package upgrades and resolved all compilation errors.

Current progress:

  • Updated the application to .NET 10 and Umbraco 17.
  • Resolved all build and compilation issues.
  • Application startup is successful.
  • Database migrations are being executed successfully during application startup.

However, we are currently encountering an issue during runtime. The application is failing in the AuthenticationMiddleware class. Specifically, the following code is unable to retrieve the authenticated backoffice user: “var user = backofficeUserAccessor.BackofficeUser;”

While investigating this issue, we also encountered the following error: “The view ‘Login’ was not found. The following locations were searched…”

At the moment, we are analyzing the authentication flow and middleware behavior in Umbraco 17, as it appears to differ from the implementation used in Umbraco 13.

We will continue troubleshooting and provide further updates as we make progress.

AuthenticationMiddleware.cs

namespace MastiffUmbraco.Core.Middlewares
{
[ExcludeFromCodeCoverage]
public class AuthenticationMiddleware
{
private readonly RequestDelegate _next;
public AuthenticationMiddleware(RequestDelegate next) { _next = next; }
public async Task InvokeAsync(HttpContext context, IBackofficeUserAccessor backofficeUserAccessor, IBackOfficeSignInManager backOfficeSignInManager, IBackOfficeUserManager backOfficeUserManager)
{
if (context.Request.Path.Value.Contains(“umbraco/api/offerapi/DeployOfferITG”))
{
await _next.Invoke(context);
return;
}
var user = backofficeUserAccessor.BackofficeUser;
if (!string.IsNullOrWhiteSpace(user?.Name))
{
var sessionId = user?.FindFirstValue(" Blog “);
if (!(await backOfficeUserManager.ValidateSessionIdAsync(user?.GetUserId(), sessionId)))
{
await backOfficeSignInManager.SignOutAsync();
context.Response.Redirect((”/login"));
return;
}
await _next.Invoke(context);
return;
}
else
{
if (context.Request.Path.Value.Contains(“umbraco”))
{
context.Response.Redirect((“/login”));
return;
}
if (context.Request.Path.Value.ContainsAny((new[] { “.js”, “.json”, “.txt” })?.ToList()))
{
context.Response.Redirect((“/login”));
return;
}
}
await _next.Invoke(context);
}
}
}

BackofficeUserAccessor.cs

public ClaimsIdentity BackofficeUser
{
get
{

    var httpContext = \_httpContextAccessor.HttpContext;

    if (httpContext == null)
        return new ClaimsIdentity();


    CookieAuthenticationOptions cookieOptions = \_cookieOptionsSnapshot.Get(Umbraco.Cms.Core.Constants.Security.BackOfficeAuthenticationType);
    string backOfficeCookie = httpContext.Request.Cookies\[cookieOptions.Cookie.Name!\];

    if (string.IsNullOrEmpty(backOfficeCookie))
        return new ClaimsIdentity();

    AuthenticationTicket unprotected = cookieOptions.TicketDataFormat.Unprotect(backOfficeCookie!);
    ClaimsIdentity backOfficeIdentity = unprotected!.Principal?.GetUmbracoIdentity();

    return backOfficeIdentity;
}

}
Umbraco.Cms.Core.Constants.Security.BackOfficeAuthenticationType = “UmbracoBackOffice”
cookieOptions.Cookie.Name = “UMB_UCONTEXT”

Thanks!

Hi @Masood-h11p

Backoffice authentication changed in v14. It now uses bearer tokens rather than cookies.

If you need the current user server side, inject IBackOfficeSecurityAccessor and use BackOfficeSecurity.CurrentUser.

Also, redirecting anything with umbraco in the path to /login will break the backoffice and the management API. That’s probably why you are getting missing Login view errors.

Justin

Hi @justin-nevitech ,

Thank you for your reply and guidance.

I updated the BackofficeUserAccessor implementation to use IBackOfficeSecurityAccessor as suggested. However, I am still unable to retrieve the current backoffice user. The following line is returning null:

var currentUser = _backOfficeSecurityAccessor.BackOfficeSecurity?.CurrentUser;

Code:
public class BackofficeUserAccessor : IBackofficeUserAccessor
{
private readonly IOptionsSnapshot _cookieOptionsSnapshot;
private readonly IHttpContextAccessor _httpContextAccessor;
private readonly IBackOfficeSecurityAccessor _backOfficeSecurityAccessor;

public BackofficeUserAccessor(
    IOptionsSnapshot<CookieAuthenticationOptions> cookieOptionsSnapshot,
    IHttpContextAccessor httpContextAccessor,
    IBackOfficeSecurityAccessor backOfficeSecurityAccessor
)
{
    \_cookieOptionsSnapshot = cookieOptionsSnapshot;
    \_httpContextAccessor = httpContextAccessor;
    \_backOfficeSecurityAccessor = backOfficeSecurityAccessor;
}

public ClaimsIdentity BackofficeUser
{
    get
    {
        var currentUser =
            \_backOfficeSecurityAccessor.BackOfficeSecurity?.CurrentUser;

        if (currentUser == null)
        {
            return new ClaimsIdentity();
        }

        var claims = new List<Claim>
{
    new Claim(ClaimTypes.Name, currentUser.Name),
    new Claim(ClaimTypes.NameIdentifier, currentUser.Key.ToString())
};

        return new ClaimsIdentity(claims, "BackOffice");
    }
}

Hi @Masood-h11p

Where in the pipeline does your middleware run? It needs to come after Umbraco has done it’s own backoffice authentication and populated the backoffice identity, this has to be done after UseBackOffice().

Justin