KNOWLEDGE BASE

How to Secure and Protect Media in Umbraco Behind a Login


Websites often have a need to incentivise user registration (i.e. creating a login) in order to access premium resources such as downloadable templates, PDF information brochures and more. These details can be very useful for your remarketing efforts. But creating a customer portal with a secure login to access protected media can often become a huge amount of work to get right! What if there was a much simpler and neater way that took advantage of Umbraco CMS to do most of the legwork for us?

As a website owner, ideally you don’t really want one person registering, then sharing the links and goodies with everyone, this would defeat the purpose of putting these resources behind a login! So today’s post will focus on a nice simple way of securing files within the Umbraco CMS media section. To do this, we will:

  • Modify the default Media Type to include a new property which the user can use to specify if this should be a ‘protected resource’
  • Creating a controller to handle the downloading of all media items

Modifying the Media Type

So let’s start with adding a new property to the default media types. In Umbraco CMS, select a media type on which you want to secure, for instance the ‘File’ media type. Select Tabs and create a new tab called Security.

Next select Generic properties and add a new property on the security tab with the following details:

  • Name: Protected Media
  • Alias: protectedMedia
  • Type: True/false
  • Tab: Security

Repeat the steps for any of the other media types you would also like the user to be able to protect.

C# Code

Here is the code which, on startup, adds the custom route to access the media items. You can include this in your project however is best, we have included it here as a nice simple file you might drop into the App_Code folder for convenience: 

using System;
using System.IO;
using System.Web.Mvc;
using System.Web.Routing;
using System.Text;
using Umbraco.Core;

namespace App_Code
{
    public class StartupHandler : IApplicationEventHandler
    {
        public void OnApplicationStarted(UmbracoApplicationBase umbracoApplication, ApplicationContext applicationContext)
        {
            RouteTable.Routes.RouteExistingFiles = true;
            RouteTable.Routes.MapRoute(
                "MediaRoute",
                "Media/{id}/{file}",
                new
                {
                    controller = "Media",
                    action = "Index",
                    id = UrlParameter.Optional,
                    file = UrlParameter.Optional
                }
            );
        }

        public void OnApplicationInitialized(
            UmbracoApplicationBase umbracoApplication,
            ApplicationContext applicationContext)
        {
        }

        public void OnApplicationStarting(
            UmbracoApplicationBase umbracoApplication,
            ApplicationContext applicationContext)
        {
        }
    }
}

Next we need to add a controller that will handle the incoming requests for these media items:

using System;
using System.IO;
using System.Web;
using System.Web.Configuration;
using System.Web.Mvc;
using System.Web.Security;
using Umbraco;
using Umbraco.Core;
using Umbraco.Core.Models;
using Umbraco.Core.Security;
using Umbraco.Core.Services;
using Umbraco.Web;

namespace App_Code.Controllers
{
    public class MediaController : Controller
    {
        public ActionResult Index(string id, string file)
        {
            string mediaPath = "/media/" + id + "/" + file;
            var mediaService = ApplicationContext.Current.Services.MediaService;
            var media = mediaService.GetMediaByPath(mediaPath);

            if (media != null)
            {
                bool requiresMemberLogin = media.GetValue("protectedMedia");

                if (requiresMemberLogin == true)
                {
                    var ticket = new System.Web.HttpContextWrapper(System.Web.HttpContext.Current).GetUmbracoAuthTicket();
                    if (ticket == null)
                    {
                        System.Configuration.Configuration configuration = WebConfigurationManager.OpenWebConfiguration("/");
                        AuthenticationSection authenticationSection = (AuthenticationSection)configuration.GetSection("system.web/authentication");
                        FormsAuthenticationConfiguration formsAuthentication = authenticationSection.Forms;

                        // User is accessing protected media, and is not logged in!
                        // we might get the login node from Umbraco's tree via Linq Queries
                        // or may have a setting for the member login area, but in any case
                        // can do something like:                        
                        //
                        // RedirecToUmbracoPage(nodeId)l
                        // or

                        return Redirect("/login");                            
                    }
                }

                FileStream fileStream = new FileStream(Server.MapPath(mediaPath), FileMode.Open);

                return new FileStreamResult(fileStream, MimeMapping.GetMimeMapping(file));
            }
            else
            {
                return HttpNotFound();
            }
        }
    }
}

And that's really all there is to it! This will ensure logged in users are redirected to log in before accessing protected media without the need to build complex code. This example is working in Umbraco 7.7.3 - the latest version at the time of writing. 

How you want to handle unauthorised access to media is up to you and may depend on the project. You could just as easily do this with members and use a Linq query to obtain the login Url from Umbraco's content tree, but we believe this light weight method of protecting Umbraco's media is quite useful.


Need an Umbraco Master?

Here at Simon Antony, we have an in house certified Umbraco Grand Master available for hire. Got a problem with your site, need architecture advice, give us a call to speak to Simon directly and see how we can help

Contact Simon Today!