Copied to clipboard

Flag this post as spam?

This post will be reported to the moderators as potential spam to be looked at


  • Gunnar Már Óttarsson 11 posts 48 karma points
    Aug 15, 2021 @ 03:01
    Gunnar Már Óttarsson
    1

    How do i determine if a backoffice user is logged in from a razor view ?

    User.GetUmbracoIdentity() is always null probably since we haven't Authenticated.

    AuthenticationService.AuthenticateAsync doesn't work since the cookie manager stops the flow if it's not a backoffice route.

    IBackOfficeSecurityAccessor BackofficeSecurityAccessor doesn't seem to help

    Seems pretty impossible right now?

  • Paul 89 posts 167 karma points
    Oct 11, 2021 @ 07:02
    Paul
    0

    Hi Gunnar,

    Did you get anywhere with this? I am trying to check if a back office user is currently logged in to the back office in a INotificationHandler<RoutingRequestNotification>, however this code always returns null:

    var user = _backOfficeSecurityAccessor.Value.BackOfficeSecurity.CurrentUser;
    

    CurrentUser is always null.

  • Mats Stam 66 posts 236 karma points
    Oct 14, 2021 @ 07:31
    Mats Stam
    0

    I've got the same problem using the IBackOfficeSecurity:

    var user = backOfficeSecurity.CurrentUser;
    

    CurrentUser is always null...

  • Keith 76 posts 242 karma points
    Oct 16, 2021 @ 18:08
    Keith
    0

    Do you mean from one of the public websites razor views, you want to know if the person viewing the page is currently a backoffice user logged into the backoffice?

    If so, you might need to authorize the request using the backoffice authentication scheme. This doesn't happen out-of-the box, I don't think.

    Shooting from the hip here, but maybe something like this will work?

    • Override the default rendering controller, so you can decorate it with the authorize attribute:

    .

    [Authorize(AuthenticationSchemes = Umbraco.Cms.Core.Constants.Security.BackOfficeAuthenticationType)]
    [AllowAnonymous]
    public class CustomRenderController : RenderController
    {
        public CustomRenderController(ILogger<RenderController> logger, ICompositeViewEngine compositeViewEngine, IUmbracoContextAccessor umbracoContextAccessor)
            : base(logger, compositeViewEngine, umbracoContextAccessor)
        {
        }
    }
    
    • register it in startup.cs:

          services.Configure<UmbracoRenderingDefaultsOptions>(c =>
          {
              c.DefaultControllerType = typeof(CustomRenderController);
          });
      

    This will try authorize the request with the backoffice scheme, but because we have "AllowAnonymous" it wont force the user to login.

    • In the razor view, you can use:

      var isLoggedIn = Context.User?.Identity?.IsAuthenticated ?? false;
      

    Hmm. This might only be able to work if you are not also using member login.

  • Warren Buckley 2106 posts 4836 karma points MVP 7x admin c-trib
    Oct 20, 2021 @ 08:18
    Warren Buckley
    0

    Hi Gunnar 👋

    I am curious as to what you are trying to achieve by wanting the logged in back office to be available on the front end of the site.

    As with anything in code depending on what your trying to do, there may be one or more ways to how to solve the problem.

    Warren

  • Mats Stam 66 posts 236 karma points
    Oct 20, 2021 @ 19:45
    Mats Stam
    1

    Edit: I took this from Umbracos own preview code...

    Well, here's what I ended up doing in my Middleware:

    private void AddBackofficeIdentityIfAvailable(HttpContext context)
        {
            var cookieOptions = context.RequestServices.GetRequiredService<IOptionsSnapshot<CookieAuthenticationOptions>>()
                .Get(Umbraco.Cms.Core.Constants.Security.BackOfficeAuthenticationType);
    
            if (cookieOptions == null)
            {
                throw new InvalidOperationException("No cookie options found with name " + Umbraco.Cms.Core.Constants.Security.BackOfficeAuthenticationType);
            }
    
            if (context.Request.Cookies.TryGetValue(cookieOptions.Cookie.Name, out var cookie))
            {
                var unprotected = cookieOptions.TicketDataFormat.Unprotect(cookie);
                var backOfficeIdentity = unprotected?.Principal.GetUmbracoIdentity();
                if (backOfficeIdentity != null)
                {
                    // 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 and allow for a back office User.
                    context.User.AddIdentity(backOfficeIdentity);
                }
            }
        }
    

    Then I tried getting the backOfficeIdentity liks this:

    var backOfficeIdentity = context.User.Identities.FirstOrDefault(c => c.AuthenticationType == Umbraco.Cms.Core.Constants.Security.BackOfficeAuthenticationType);
    

    And finally you can check if it's ok and authenticated with

    backOfficeIdentity != null && backOfficeIdentity.IsAuthenticated
    
  • Warren Buckley 2106 posts 4836 karma points MVP 7x admin c-trib
    Oct 21, 2021 @ 07:41
    Warren Buckley
    1

    OK I got a reply internally as was suggested a similar code sample as MatsStam, that might also help you.

    public class DemoController : Controller
    {
        private readonly IOptionsSnapshot<CookieAuthenticationOptions> _snapshot;
    
        public DemoController(IOptionsSnapshot<CookieAuthenticationOptions> snapshot)
        {
            _snapshot = snapshot;
        }
    
        [HttpGet("demo")]
        public async Task<IActionResult> Demo()
        {
            CookieAuthenticationOptions cookieOptions = _snapshot.Get(Core.Constants.Security.BackOfficeAuthenticationType);
    
            string backOfficeCookie = HttpContext.Request.Cookies[cookieOptions.Cookie.Name!];
            AuthenticationTicket unprotected = cookieOptions.TicketDataFormat.Unprotect(backOfficeCookie!);
            ClaimsIdentity backOfficeIdentity = unprotected!.Principal.GetUmbracoIdentity();
    
            return Ok(backOfficeIdentity.Name);
        }
    }
    
  • Gunnar Már Óttarsson 11 posts 48 karma points
    Oct 30, 2021 @ 18:30
    Gunnar Már Óttarsson
    0

    Hey Warren, I am trying to determine if a backoffice user is logged in and possibly what groups the user belongs to outside of backoffice routed requests.

    It does look like your recent code examples provide the required information, thank you MatsStam and Warren for taking the time.

  • Markus Johansson 1938 posts 5866 karma points MVP 2x c-trib
    Nov 02, 2021 @ 15:39
    Markus Johansson
    108

    Thanks everyone for sharing your progress! Code samples works like a charm!

    I figured I'll share my "IBackofficeUserAccessor"-implementation.

    public interface IBackofficeUserAccessor
    {
        ClaimsIdentity BackofficeUser { get; }
    }
    
    public class BackofficeUserAccessor : IBackofficeUserAccessor
    {
        private readonly IOptionsSnapshot<CookieAuthenticationOptions> _cookieOptionsSnapshot;
        private readonly IHttpContextAccessor _httpContextAccessor;
    
        public BackofficeUserAccessor(
            IOptionsSnapshot<CookieAuthenticationOptions> cookieOptionsSnapshot,
            IHttpContextAccessor httpContextAccessor
        )
        {
            _cookieOptionsSnapshot = cookieOptionsSnapshot;
            _httpContextAccessor = httpContextAccessor;
        }
    
    
        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;
            }
    
        }
    }
    

    Feel free to use this in your own projects, both closed and open source. I give my full permissions.

  • Paul Seal 524 posts 2889 karma points MVP 7x c-trib
    Nov 04, 2021 @ 14:59
    Paul Seal
    0

    Thanks for this Markus. Is it ok if I borrow some/all of this to update my edit link package from v8 to v9 please?

  • Markus Johansson 1938 posts 5866 karma points MVP 2x c-trib
    Nov 04, 2021 @ 15:36
    Markus Johansson
    0

    Just do it! =D

  • Sean Maloney 5 posts 79 karma points
    Nov 03, 2022 @ 12:19
    Sean Maloney
    1

    Thanks - found this really useful.

    If the value in the authentication cookie is very large then it's automatically split up into chunks and split over multiple cookies. This was the situation in my case, so I tweaked the code ever so slightly by using ChunkingCookieManager (nom nom) to get it to work:

        public ClaimsIdentity BackOfficeUser
        {
            get
            {
                var httpContext = _httpContextAccessor.HttpContext;
    
                if (httpContext == null)
                    return new ClaimsIdentity();
    
                var cookieOptions = _cookieOptionsSnapshot.Get(Umbraco.Cms.Core.Constants.Security.BackOfficeAuthenticationType);
                var cookieManager = new ChunkingCookieManager();
                var backOfficeCookie = cookieManager.GetRequestCookie(httpContext, cookieOptions.Cookie.Name!);
    
                if (string.IsNullOrEmpty(backOfficeCookie))
                    return new ClaimsIdentity();
    
                var unprotected = cookieOptions.TicketDataFormat.Unprotect(backOfficeCookie!);
                var backOfficeIdentity = unprotected!.Principal.GetUmbracoIdentity();
    
                return backOfficeIdentity ?? new ClaimsIdentity();
            }
        }
    

    Hope that helps somebody else in a similar situation.

  • Philip Hayton 98 posts 435 karma points
    Dec 19, 2022 @ 21:00
    Philip Hayton
    0

    Hi Sean,

    Just wanted you to know that I have bumped into this exact same issue and your solution works a treat. Thanks for sharing #h5yr

  • Steve Morgan 1349 posts 4459 karma points c-trib
    Aug 16, 2023 @ 08:08
    Steve Morgan
    0

    Here again - thanks Markus - we need this merged in IMHO :)

  • Gunnar Már Óttarsson 11 posts 48 karma points
    Nov 03, 2021 @ 23:58
    Gunnar Már Óttarsson
    0

    Will mark as answered to help others despite not having had a chance to test myself, thanks all

  • Peter Dyhrberg 7 posts 81 karma points
    Nov 17, 2021 @ 07:34
    Peter Dyhrberg
    0

    Hi Marcus, thank you so much for sharing your solution. I cannot make it work in Razor-view. Can you advice me how to check (in Razor) if the user is logged in to Backoffice, when using your "IBackofficeUserAccessor"-implementation?

    I am trying something like this: var LoggedIn = new BackofficeUserAccessor().BackofficeUser;

    This way I have checked in Umbraco 8 earlier: var ticket = new System.Web.HttpContextWrapper(System.Web.HttpContext.Current).GetUmbracoAuthTicket();

    Thanks again Marcus. /Best regards Peter

  • Mats Stam 66 posts 236 karma points
    Nov 17, 2021 @ 07:55
    Mats Stam
    0

    Maybe try injecting it into your razor view?

    @inject IBackofficeUserAccessor _backofficeUserAccessor;
    

    Haven't tried it since I'm using my implementation in my Middleware :P But that should make you able to do:

     _backofficeUserAccessor.BackofficeUser;
    
  • Peter Dyhrberg 7 posts 81 karma points
    Nov 17, 2021 @ 08:17
    Peter Dyhrberg
    0

    Hi Mats and Paul, Thank you so much for your answers and effort. I will try and see if I can make it work.

    Mats: I tried injecting it into the razor-view but I get the error:

    InvalidOperationException: No service for type 'Umbraco9Test5.IBackofficeUserAccessor' has been registered.

    Paul: I have exactly the described need: To show an "Edit-icon", if the user is logged in, that links directly to the backoffice-node so administrators quickly can go the the backoffice-page and change the content. I will try out the tag-helper-method and see if I can make it work.

    / Best regards Peter

  • Mats Stam 66 posts 236 karma points
    Nov 17, 2021 @ 08:28
    Mats Stam
    1

    Ah, you need to register it in your startup.cs too :) Otherwise the DI won't work :)

    Something like:

    services.AddTransient<IBackofficeUserAccessor, BackofficeUserAccessor >();
    

    I'm all new to .Net 5 myself, so maybe you don't want to register it as a Transient, but singelton or whatever, depending on your need :)

  • Paul Seal 524 posts 2889 karma points MVP 7x c-trib
    Nov 17, 2021 @ 07:57
    Paul Seal
    0

    Hi There is a community package called Our.Umbraco.TagHelpers.

    https://www.nuget.org/packages/Our.Umbraco.TagHelpers/

    I used Markus’ code to enable me to create a tag helper which renders an edit link on the front end of the user is logged into Umbraco.

    Our next move is to create a tag helper for only showing content if the user is logged into Umbraco. I think it will probably be added this week or next.

    It will probably work like this:

    blah

    If the user is not logged into Umbraco then the div and its contents won’t be rendered.

    Paul

  • Markus Johansson 1938 posts 5866 karma points MVP 2x c-trib
    Nov 17, 2021 @ 09:20
    Markus Johansson
    1

    Great stuff!

  • Peter Dyhrberg 7 posts 81 karma points
    Nov 17, 2021 @ 09:22
    Peter Dyhrberg
    2

    Hi Mats, Thanks a lot. It worked. Here is my solution:

    1. make a new class file in the root of your project and name it "BackofficeUserAccessor.cs" and copy Marcus code suggestion into the file (see above)

    2. open your startup.cs file and insert the following into the ConfigureServices method:

    services.AddTransient<IBackofficeUserAccessor, BackofficeUserAccessor>();
    
    1. open your Razor-view / cshtml-file

      • Insert the following in the top:

      @inject IBackofficeUserAccessor _backofficeUserAccessor

      • insert the following to check if the user is logged into Umbraco backoffice:

    var IsAdmin = _backofficeUserAccessor.BackofficeUser.IsAuthenticated;

    if (IsAdmin == true) { EDIT Page }

    Paul :) I tried to work with the Taghelper and got it to work. In my case I think it is a little overkill to use a taghelper for checking this, when you can use a simple if-statement. Once again I really appreciate your feedback. Feel free to contact me if you need more info...

    Here is the example with the tag-helper:

    <AdminLogin condition="IsAdmin == true"><a href="/umbraco#/content/content/edit/@Model.Id">EDIT Page</a></AdminLogin>
    

    Best regards Peter

  • Paul Seal 524 posts 2889 karma points MVP 7x c-trib
    Nov 17, 2021 @ 09:48
    Paul Seal
    3

    Hi Peter

    It's interesting that you say using a tag helper is overkill. It keeps all of the code out of the view.

    For anyone else interested in this package. Here's how you use it:

    dotnet add package Our.Umbraco.TagHelpers
    

    Then update your /Views/_ViewImports.cshtml file to use this tag helpers package

    @addTagHelper *, Our.Umbraco.TagHelpers
    

    The package does all the registering of the service for you, then it is very simple to use it:

    <our-edit-link>Edit</our-edit-link>
    

    Which outputs the link like this:

    <a href="/umbraco#/content/content/edit/1057">Edit</a>
    

    N.B. The edit link tag helper checks if the user is logged into the backoffice and if they have access to the content section before showing the link.

    Kind regards

    Paul

  • andrew shearer 512 posts 661 karma points
    Feb 03, 2022 @ 19:32
    andrew shearer
    1

    hello - Ive just come across this thread and like Peter Dyhrberg i am looking for a way of determing if there is currently a backoffice user logged in so that on the website frontend a convenient edit link can be provided to the respective content in the CMS. Is this still a case of rolling your own or is there something in 9.2 or forthcoming in 9.3 that helps with this? thanks

Please Sign in or register to post replies

Write your reply to:

Draft