Accessing members from UmbracoApiController to secure media files in v8
Hello everyone,
I’m trying to secure media files on my Umbraco v8.18.8 website based on, among other factors, whether or not a member is currently logged in. The method I'm using to achieve this is roughly based on this blog post. I have a handler class that successfully intercepts all requests to /media/*:
The handler is designated in my Web.config file like so: <add name="MediaHandler" path="/media/*" verb="*" type="MembersPortalV8.Handlers.MediaHandler"/>
The API being called in MediaHandler is defined in a separate class, ProtectedMediaApiController, which inherits from UmbracoApiController as such:
Accessing mediaService through DI works as expected, and I am able to check properties on the specified media file. For some reason, however, I cannot get the current member - every approach I have tried so far either returns false or null. Some examples:
Members.IsLoggedIn() (as shown above) - always returns false, even if a member is logged in. Members should be accessible here since it is a property of UmbracoApiControllerBase, which UmbracoApiController inherits from (source).
_membershipHelper.IsLoggedIn() (where a new MembershipHelper is passed in through DI) - always returns false.
_umbracoHelper.MemberIsLoggedOn() (where UmbracoHelper is retrieved through DI) - always returns false.
This forum post discusses the same issue. The OP claims they passed the current member's username as a parameter to their API method; however, the class my API method is being called from inherits HttpTaskAsyncHandler, which, as far as I know, would not allow me to DI a MembershipHelper.
The forum post also mentions using the MemberAuthorize attribute - interestingly, this does work, and logged-out members are not able to call the API method, which means that this functionality is at least possible. Unfortunately, I also need to check members' roles to determine if they can access a given media file. Since other member-related methods do not work (_membershipHelper.GetCurrentLoginStatus() is always null, _membershipHelper.GetCurrentMember() is always null), this would not be a sufficient workaround.
Some other options I have tried include:
Using ApiController instead of UmbracoApiController or attempting to inject Umbraco dependencies into MediaHandler (does not give access to Umbraco services - tried some options discussed in this thread but could not figure out how to get it working for my purpose)
Inheriting UmbracoHttpHandler or UmbracoAuthorizedHttpHandler instead of IHttpHandler or HttpTaskAsyncHandler (seemingly not possible, see this github issue)
According to the github issue I mentioned earlier, it might be due to Umbraco interpreting the request as client-side and therefore not providing an UmbracoContext - unfortunately I cannot change the extension to force a server-side request as suggested, as my handler needs to run for all media files of any extension. Another suggestion was to use MVC route hijacking, which may be the proper way to go about doing this, but I was having some other issues with that (will create a separate thread for it!)
I am still relatively new to Umbraco so there is most likely something obvious I'm missing here. I will post an update to this thread if I find anything, but in the mean time, any help/advice/suggestions would be greatly appreciated!
Thanks for the quick response! Unfortunately, I don't think I can use this package as-is, since it's currently only compatible with Umbraco v7 (see here).
Looking at the source code, it seems as though they're using Context.User.Identity.IsAuthenticated to check if a member is logged in. Attempting to do the same in my MediaHandler by accessing the HttpContext passed in as a parameter to ProcessRequestAsync (context.User.Identity.IsAuthenticated) again always returns false regardless if a member is logged in. I can't figure out exactly how they've implemented the request interception, but considering they are able to get the proper authentication data from the context, I don't think I'm taking the right approach here.
No worries! I really appreciate you taking the time to respond regardless. It does look like they have a v8 version of the package in development, but it's still WIP. I might be able to just fork the package and finish it, but that sounds like a lot of work for something I'm hoping to do in a relatively simple way.
Thanks for the response! Unfortunately, new HttpContextWrapper(HttpContext.Current).User always returns null when called in my handler. My suspicion is that because it's a media link, the HttpContext doesn't contain any data that pertains to Umbraco, but I haven't been able to find a workaround.
The Handler does not know anthing about umbraco, however that should not be why the User is null the new HttpContextWrapper(HttpContext.Current).User is also not directly related to Umbraco other than it should be set when a member logs in, so sounds like you handler may be running to early in the request pipeline.
try adding runAllManagedModulesForAllRequests="True" to the modules element in web.config
You could also try inheriting SessionState in your handler
That makes sense - I already had runAllManagedModulesForAllRequests="True" set in my web.config, but I don't have a custom module - would it be necessary for me to implement one? I did a bit of research but I couldn't find any guidance on what exactly it would look like for this scenario. Unfortunately inheriting IReadOnlySessionState didn't change anything either, unless there's something I need to add to my ProcessRequestAsync method, but I assume not since IReadOnlySessionState is meant to be a marker interface.
I did have it my handler implementing IHttpHandler earlier, before I wanted an async ProcessRequest method, but I tried again and still no luck... I'm pretty much completely out of ideas here, I did try manually getting the cookie from the request (which does work!) but I'm not sure how I would be able to check a member's groups with it.
For now I'm going to focus on upgrading my site to v10 and implementing this functionality there, but if anyone has any more suggestions I'll definitely come back to this!
Accessing members from UmbracoApiController to secure media files in v8
Hello everyone,
I’m trying to secure media files on my Umbraco v8.18.8 website based on, among other factors, whether or not a member is currently logged in. The method I'm using to achieve this is roughly based on this blog post. I have a handler class that successfully intercepts all requests to
/media/*
:The handler is designated in my
Web.config
file like so:<add name="MediaHandler" path="/media/*" verb="*" type="MembersPortalV8.Handlers.MediaHandler"/>
The API being called in
MediaHandler
is defined in a separate class,ProtectedMediaApiController
, which inherits fromUmbracoApiController
as such:Accessing
mediaService
through DI works as expected, and I am able to check properties on the specified media file. For some reason, however, I cannot get the current member - every approach I have tried so far either returnsfalse
ornull
. Some examples:Members.IsLoggedIn()
(as shown above) - always returns false, even if a member is logged in.Members
should be accessible here since it is a property ofUmbracoApiControllerBase
, whichUmbracoApiController
inherits from (source)._membershipHelper.IsLoggedIn()
(where a newMembershipHelper
is passed in through DI) - always returns false._umbracoHelper.MemberIsLoggedOn()
(whereUmbracoHelper
is retrieved through DI) - always returns false.This forum post discusses the same issue. The OP claims they passed the current member's username as a parameter to their API method; however, the class my API method is being called from inherits
HttpTaskAsyncHandler
, which, as far as I know, would not allow me to DI aMembershipHelper
.The forum post also mentions using the
MemberAuthorize
attribute - interestingly, this does work, and logged-out members are not able to call the API method, which means that this functionality is at least possible. Unfortunately, I also need to check members' roles to determine if they can access a given media file. Since other member-related methods do not work (_membershipHelper.GetCurrentLoginStatus()
is always null,_membershipHelper.GetCurrentMember()
is always null), this would not be a sufficient workaround.Some other options I have tried include:
ApiController
instead ofUmbracoApiController
or attempting to inject Umbraco dependencies intoMediaHandler
(does not give access to Umbraco services - tried some options discussed in this thread but could not figure out how to get it working for my purpose)UmbracoHttpHandler
orUmbracoAuthorizedHttpHandler
instead ofIHttpHandler
orHttpTaskAsyncHandler
(seemingly not possible, see this github issue)According to the github issue I mentioned earlier, it might be due to Umbraco interpreting the request as client-side and therefore not providing an
UmbracoContext
- unfortunately I cannot change the extension to force a server-side request as suggested, as my handler needs to run for all media files of any extension. Another suggestion was to use MVC route hijacking, which may be the proper way to go about doing this, but I was having some other issues with that (will create a separate thread for it!)I am still relatively new to Umbraco so there is most likely something obvious I'm missing here. I will post an update to this thread if I find anything, but in the mean time, any help/advice/suggestions would be greatly appreciated!
Hi Nathan
There is an open source package called Our.Shield.MediaProtection that does I think something similar to what you are attempting to achieve.
If you can't use this package and need to build you own, you might be able to glean some insights into how they have implemented this, for example:
https://github.com/JcRichards1991/Our.Shield/blob/master-umbraco-v8/src/Our.Shield.MediaProtection/Models/MediaProtectionApp.cs
has the code in that checks the current member etc
regards
Marc
Hi Marc,
Thanks for the quick response! Unfortunately, I don't think I can use this package as-is, since it's currently only compatible with Umbraco v7 (see here).
Looking at the source code, it seems as though they're using
Context.User.Identity.IsAuthenticated
to check if a member is logged in. Attempting to do the same in myMediaHandler
by accessing theHttpContext
passed in as a parameter toProcessRequestAsync
(context.User.Identity.IsAuthenticated
) again always returns false regardless if a member is logged in. I can't figure out exactly how they've implemented the request interception, but considering they are able to get the proper authentication data from the context, I don't think I'm taking the right approach here.Ahh Sorry Nathan
I just looked at the packages.config and the default branch made me think there was a V8 version :-(
https://github.com/JcRichards1991/Our.Shield/blob/master-umbraco-v8/src/Our.Shield.MediaProtection/packages.config
so hoped it would have something useful in there for you.
regards
marc
No worries! I really appreciate you taking the time to respond regardless. It does look like they have a v8 version of the package in development, but it's still WIP. I might be able to just fork the package and finish it, but that sounds like a lot of work for something I'm hoping to do in a relatively simple way.
Hi Nathan,
You can get the username of the logged in user in your handler and pass it to the UmbracoApi
Hi Huw,
Thanks for the response! Unfortunately,
new HttpContextWrapper(HttpContext.Current).User
always returns null when called in my handler. My suspicion is that because it's a media link, theHttpContext
doesn't contain any data that pertains to Umbraco, but I haven't been able to find a workaround.Hi Nathan,
The Handler does not know anthing about umbraco, however that should not be why the User is null the
new HttpContextWrapper(HttpContext.Current).User
is also not directly related to Umbraco other than it should be set when a member logs in, so sounds like you handler may be running to early in the request pipeline.try adding runAllManagedModulesForAllRequests="True" to the modules element in web.config
You could also try inheriting SessionState in your handler
Hi Huw,
That makes sense - I already had
runAllManagedModulesForAllRequests="True"
set in myweb.config
, but I don't have a custom module - would it be necessary for me to implement one? I did a bit of research but I couldn't find any guidance on what exactly it would look like for this scenario. Unfortunately inheritingIReadOnlySessionState
didn't change anything either, unless there's something I need to add to myProcessRequestAsync
method, but I assume not sinceIReadOnlySessionState
is meant to be a marker interface.maybe it is not working with HttpTaskAsyncHandler mine Implements IHttpHandler
I did have it my handler implementing
IHttpHandler
earlier, before I wanted an asyncProcessRequest
method, but I tried again and still no luck... I'm pretty much completely out of ideas here, I did try manually getting the cookie from the request (which does work!) but I'm not sure how I would be able to check a member's groups with it.For now I'm going to focus on upgrading my site to v10 and implementing this functionality there, but if anyone has any more suggestions I'll definitely come back to this!
Just a thought, as I remember having to do this once before.
in the modules section of webconfig, try adding this at the end
Still no luck, unfortunately. Thank you for all of the suggestions, though!
Sorry it wasn't helpful, just seems to work for me.
For V10 you will require a middleware class registered, I have an example of that if you need one.
No worries! It's a very strange issue. Sure, if you wouldn't mind posting your example that would be super helpful! Thanks again!
is working on a reply...