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 10 posts 43 karma points
    Aug 15, 2021 @ 03:01
    Gunnar Már Óttarsson
    0

    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?

  • Paulius Putna 87 posts 165 karma points
    Oct 11, 2021 @ 07:02
    Paulius Putna
    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 58 posts 207 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 44 posts 180 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 4789 karma points MVP ∞ admin hq 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 58 posts 207 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 4789 karma points MVP ∞ admin hq c-trib
    Oct 21, 2021 @ 07:41
    Warren Buckley
    0

    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 10 posts 43 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 1741 posts 5030 karma points c-trib
    27 days ago
    Markus Johansson
    103

    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 478 posts 2588 karma points MVP 4x c-trib
    26 days ago
    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 1741 posts 5030 karma points c-trib
    25 days ago
    Markus Johansson
    0

    Just do it! =D

  • Gunnar Már Óttarsson 10 posts 43 karma points
    26 days ago
    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 78 karma points
    1 week ago
    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 58 posts 207 karma points
    1 week ago
    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 78 karma points
    1 week ago
    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 58 posts 207 karma points
    1 week ago
    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 478 posts 2588 karma points MVP 4x c-trib
    1 week ago
    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 1741 posts 5030 karma points c-trib
    1 week ago
    Markus Johansson
    1

    Great stuff!

  • Peter Dyhrberg 7 posts 78 karma points
    1 week ago
    Peter Dyhrberg
    1

    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 478 posts 2588 karma points MVP 4x c-trib
    1 week ago
    Paul Seal
    1

    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

Please Sign in or register to post replies

Write your reply to:

Draft