Copied to clipboard

Flag this post as spam?

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


  • David Armitage 509 posts 2079 karma points
    Jun 12, 2021 @ 14:42
    David Armitage
    0

    Umbraco 9 AuthorizationFilter - User Always Null

    Hi All,

    I am trying to create an AuthorizationFilter filter in Umbraco 9.

    public bool Authorize([NotNull] DashboardContext context)
    {
          var httpContext = context.GetHttpContext();
          var authService = httpContext.RequestServices.GetRequiredService<IAuthorizationService>();
          bool success = authService.AuthorizeAsync(httpContext.User, this.policyName).ConfigureAwait(false).GetAwaiter().GetResult().Succeeded;
          return success;
    }
    

    The trouble is the user from the context is always null regardless to if I am logged in to Umbraco or not.

    I am trying to port this code over from Umbraco 8.

    public bool Authorize(DashboardContext context)
    {
           var http = new HttpContextWrapper(HttpContext.Current);
           var ticket = http.GetUmbracoAuthTicket();
           http.AuthenticateCurrentRequest(ticket, true);
    
           var user = Current.UmbracoContext.Security.CurrentUser;
    
           return user != null && user.Groups.Any(g => g.Alias == "admin");
    }
    

    Any ideas - I am guessing I am missing something valuable from the startup.cs.

    Thanks in Advance

    Regards

    David

  • Mathias Hove 11 posts 74 karma points
    Jun 12, 2021 @ 19:55
    Mathias Hove
    1

    HI David.

    You should try

    using Umbraco.Extensions;
    HttpContext.User.GetUmbracoIdentity()
    

    This should give you the Umbraco user instead of a member. I THINK. Not 100% sure :)

  • David Armitage 509 posts 2079 karma points
    Jun 13, 2021 @ 01:52
    David Armitage
    1

    Unfortunately that didn't work.

    It looks like the user isn't in context at that stage. This might not be as easy as first though.

    I think I will get hangfire working without this. I have managed to get it working with authorization disabled. I also got it working using a username and password.

    I just wanted to go one step further and get this working only for logged in users.

    Maybe I will revisit this at a later date.

    enter image description here

  • David Armitage 509 posts 2079 karma points
    Jun 13, 2021 @ 04:36
    David Armitage
    1

    Just adding a bit of context here.

    This is relating to this article I wrote.

    https://www.umbrajobs.com/blog/posts/2021/june/umbraco-9-configuring-hangfire-scheduled-tasks/

    Getting up and running with Hangfire for Umbraco 9.

    I am fully up and running with Hangfire but there is this last issue (related to this task) preventing me from security the Hangfire dashboard based on if the Umbraco user is logged in or not.

    Kind Regards

    David

  • Marc Goodson 2155 posts 14408 karma points MVP 9x c-trib
    Jun 13, 2021 @ 11:15
    Marc Goodson
    3

    Hi David

    Just guessing from the code, but have you tried injecting BackofficeSecurityAccessor?

    I saw this PR here: https://github.com/umbraco/Umbraco-CMS/pull/8871/files

    and on another PR about removing UmbracoBackofficeIdentity

    https://github.com/umbraco/Umbraco-CMS/pull/9833#issuecomment-783593383

    there is the suggestion of

    _backOfficeSecurityAccessor.BackOfficeSecurity.CurrentUser
    

    (after injecting the BackOfficeSecurityAccessor)

    being the new way to access the current backoffice user, but I haven't tried it!

    regards

    marc

  • Sebastiaan Janssen 5060 posts 15522 karma points MVP admin hq
    Jun 14, 2021 @ 13:07
    Sebastiaan Janssen
    1

    I'll relay Shannon's answer to me about this, we haven't figured this out together yet:

    I'll try to explain - this is Authentication (AuthN) vs Authorization (AuthZ)

    In Umbraco, there are specific request paths that are authenticated for the back office and the rest are authenticated as the front-end. This is how it is possible to have both Members and Users working at the same time. Some paths are considered back office and as such, we will execute the AuthN process for the back office (i.e. cookie authentication for the back office). If the AuthN succeeds the User (Principal) is assigned to the HttpContext. If the request is not for the back office, it will run the AuthN for the front (i.e. cookie authentication for the front-end members).If the AuthN succeeds the User (Principal) is assigned to the HttpContext.

    How a back office request is determined is based on one method UmbracoRequestPaths.IsBackOfficeRequest (in v8 this is a little different).

    In both v8 and v9, we need a way to register a non-standard back office request as a back office request. Alternatively, things that need to be Authenticated as a back office request, should be routed as such.

    You can change the route to be a back office route. Most things in /umbraco/* are considered as such. There are a few exceptions like surface controllers and api controllers that are front-end controllers but are also routed under the /umbraco route (which is why we have the [IsBackOffice] attribute. You can have a look at the IsBackOfficeRequest method and tests to see what is a back office request.

    We actually have documentation for this: https://our.umbraco.com/documentation/Reference/Routing/Authorized/

    I tried simply updating the /hangfire route to /umbraco/hangfire but no luck with that. Something "extra" needs to happen and I didn't have the time to work on that yet.

  • Marc Goodson 2155 posts 14408 karma points MVP 9x c-trib
    Jun 14, 2021 @ 19:01
    Marc Goodson
    1

    Hi Seb

    Did you try routing via

    /umbraco/backoffice/hangfire

    ???

    I think it's anything routed via /umbraco/backoffice that is denoted as a backoffice request...

    regards

    marc

  • Sebastiaan Janssen 5060 posts 15522 karma points MVP admin hq
    Jun 14, 2021 @ 19:37
    Sebastiaan Janssen
    1

    Ah, great suggestion, but no luck! My InPrivate window does not ask for any auth! 😅

  • David Armitage 509 posts 2079 karma points
    Jun 14, 2021 @ 12:31
    David Armitage
    1

    Still working on this. I need to figure out how to compose this without injecting it in to see if that will work.

    Anyone any ideas how to compose something without dependency injection using Umbraco 9?

    Like we used to do for 8 inside custom classes or razor views.

  • Gareth Wright 32 posts 101 karma points c-trib
    Jul 02, 2021 @ 14:01
    Gareth Wright
    1

    Is there any update on how this could work?

  • Gunnar Már Óttarsson 11 posts 48 karma points
    Aug 15, 2021 @ 02:59
    Gunnar Már Óttarsson
    0

    Possibly related issue is determining 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.

    Seems pretty impossible right now

  • Grégory Viaene 1 post 74 karma points c-trib
    Sep 29, 2021 @ 09:17
    Grégory Viaene
    2

    Hi,

    Just found a way to restrict access to Hangfire for Members or to Backoffice Users

    To restrict access to Umbraco BackOffice Users, it only works if the hangfire url starts with /umbraco/backoffice/. Did not find a way to make it works with only /hangfire

    Here are the gists

    Umbraco9 & Hangfire with Member access https://gist.github.com/An0d/46d2e5d5fab229f744669c4676adc07b

    Umbraco9 & Hangfire with BackOffice User access https://gist.github.com/An0d/497766f14b452b278c8f4dfa3ac74c16

    For BackOffice Users access I used the BackOfficeAccess Authorization policy provided by Umbraco but you could also define your own policy to add multiple criterias ;-)

    Hope it helps

  • Sebastiaan Janssen 5060 posts 15522 karma points MVP admin hq
    Sep 30, 2021 @ 08:36
    Sebastiaan Janssen
    2

    Think you've found the same thing I did! 👍

    https://cultiv.nl/blog/setting-up-hangfire-in-umbraco-9/

    It's also an Umbraco package now so no need to write the plumbing code: https://our.umbraco.com/packages/developer-tools/cultivhangfire/

    Ah, I didn't notice the member access, that could be useful for some!

  • Marc Goodson 2155 posts 14408 karma points MVP 9x c-trib
    Oct 01, 2021 @ 17:03
    Marc Goodson
    1

    ooh, So my hunch was right?

  • Sebastiaan Janssen 5060 posts 15522 karma points MVP admin hq
    Oct 02, 2021 @ 08:40
    Sebastiaan Janssen
    1

    @Marc yeah looks like we can't secure arbitrary routes they need to be in the /umbraco route (whatever your route is for the backoffice.. and I just remembered that I shouldn't hardcode that, since the route is configurable).

    Please do note that Grégory's code opens the dashboard up to anyone with backoffice access, I am specifically limiting it to backoffice users with access to the Settings section of Umbraco.

  • Kim Jensen 2 posts 73 karma points
    Jan 18, 2022 @ 13:10
    Kim Jensen
    1

    This is great. Only thing missing is changing:

    endpoints.MapHangfireDashboard(pattern: "/umbraco/backoffice/hangfire",options: new DashboardOptions()).RequireAuthorization(Constants.System.HangfireDashboard);
    

    To:

    endpoints.MapHangfireDashboardWithAuthorizationPolicy(Constants.System.HangfireDashboard, "/umbraco/backoffice/hangfire");
    

    Else it does not work on a remote environment. But thank you guys for doing the heavy lifting!

  • Peter van den Dungen 66 posts 365 karma points
    May 18, 2022 @ 20:20
    Peter van den Dungen
    2

    I think I found a way for David's initial issue, the Authorize method like in the old times, Umbraco 8.

    I use this solution to get the current BO user. https://our.umbraco.com/forum/umbraco-9/106857-how-do-i-determine-if-a-backoffice-user-is-logged-in-from-a-razor-view#comment-334423

    Then apply this in the Authorize dashboard authorization.

    public class HangfireAuthorizationFilter : IDashboardAuthorizationFilter
    {
        public bool Authorize([NotNull] DashboardContext context)
        {
            var httpContext = context.GetHttpContext();
            var backofficeUserAccessor = httpContext.RequestServices.GetRequiredService<IBackofficeUserAccessor>();
    
            if (backofficeUserAccessor != null)
            {
                var currentUser = backofficeUserAccessor.BackofficeUser;
                return currentUser.IsAuthenticated;
            }
    
            return false;
    
        }
    }
    

    Note: this is not checking for any user rights, you have to tweak it yourself.

    Edit; here is the BackofficeUserAccessor code including role stuff

    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;
            }
        }
    
        public IEnumerable<string> GetAllRoles()
        {
            if (BackofficeUser == null || !BackofficeUser.IsAuthenticated)
            {
                return new List<string>();
            }
    
            var roles = BackofficeUser?.Claims?.Where(c => c.Type == ClaimTypes.Role).Select(c => c.Value) ?? new List<string>();
    
            return roles;
        }
    
        public bool UserIsAdmin()
        {
            return GetAllRoles()?.Any(x => x.Equals(Models.Constants.Roles.ADMIN)) ?? false;
        }
    
        public bool UserIsHangfireAdmin()
        {
            return GetAllRoles()?.Any(x => x.Equals(Models.Constants.Roles.HANGFIRE_ADMIN)) ?? false;
        }
    }
    

    And you'll have the role check available for the filter

    if (backofficeUserAccessor != null)
            {
                return backofficeUserAccessor.UserIsHangfireAdmin();
            }
    
  • Sebastiaan Janssen 5060 posts 15522 karma points MVP admin hq
    May 22, 2022 @ 08:07
    Sebastiaan Janssen
    1

    Note: this is not checking for any user rights, you have to tweak it yourself.

    ‼️

    Make sure to do tweak this! Or just use my package, which has a proper check in place, which I'd encourage everyone to use in their custom code if they need it. This ensures people can only access the hangfire route if they have access to the Settings section of Umbraco.

    If I recall correctly, I tried something similar to the code above sort of worked for me but wasn't reliable (sorry can't remember why).

  • Sebastiaan Janssen 5060 posts 15522 karma points MVP admin hq
    May 22, 2022 @ 08:09
  • Peter van den Dungen 66 posts 365 karma points
    May 22, 2022 @ 08:53
    Peter van den Dungen
    0

    Thanks Sebastiaan, I am using the dashboard part, works pretty cool within the Umbraco BO. 👍

    However, in order to access the Hangfire tab , the user should have rights to the settings section as well, is this tweakable (f.e role specific)? In some rare cases we don't want certain users to access the settings section.

    Maybe in this case my previous post can be handy, I edited the post and added some role checks.

    What still plays a role is your findings regarding reliability, did you mean the IBackofficeUserAccessor or just the IDashboardAuthorizationFilter part? Also because of the earlier referenced topic https://our.umbraco.com/forum/umbraco-9/106857-how-do-i-determine-if-a-backoffice-user-is-logged-in-from-a-razor-view

  • Sebastiaan Janssen 5060 posts 15522 karma points MVP admin hq
    May 22, 2022 @ 09:04
    Sebastiaan Janssen
    0

    As I said, I have doubts your code is fully secure.

    You can't tweak it with my package yet but in the code I linked to you could change AuthorizationPolicies.SectionAccessSettings to "MySection" - in case you put it in a custom section, or one of the default sections:

    AuthorizationPolicies.SectionAccessContent 
    AuthorizationPolicies.SectionAccessPackages
    AuthorizationPolicies.SectionAccessUsers
    AuthorizationPolicies.SectionAccessMedia
    AuthorizationPolicies.SectionAccessSettings
    AuthorizationPolicies.SectionAccessMembers 
    

    I will need to investigate role-based access again, but that's for the future.

Please Sign in or register to post replies

Write your reply to:

Draft