Protect media items

One of my last projects required some media to be protected under a login, to ensure authorized access to confidential resources. Umbraco (v7.13) does not offer this feature built-in, but only as a commercial plugin. Since I didn’t want to use the provided plugin I decided to create my own custom auth logic.

In this scenario media items (files, folders and images) can only either be protected or public, since I didn’t need role based access. If this is your case, you can easily extend this approach. After protection, if an unauthorized user tries to access a protected media it gets redirected to the login screen.

Updating the media document type

As first action you need to update the document types of folders, files and images to include a checkbox indicating if that media should be protected or not (if you need role-based auth here you can add a role picker). Just go in Settings -> Media Types -> [File | Folder | Image] and update each of doc types or, better, create a new document type named ProtectedMedia that has a single boolean property named isAuthRequired and make other document types inherit from it, with the build-in composition tab.

Overriding default media controller

Now we need to intercept every media request, which are routed under /media/{id}/{filename}, in order to check if the requested media is protected with authentication or not.

To catch this route we need to override the Umbraco default media controller with our own custom controller and tell Umbraco to route each media request against our new controller.

Create the new controller

The IsProtectedMedia() is a custom extension method the simply checks if the media has the isAuthRequired property defined in the above section setted to true. In my case I also inserted in that method some logic to protect a folder and all its children without the need of checking explicitly each child. So I assume that if the isAuthRequired property is checked in a folder, also all its content is protected.

Route each media request to our new controller

Issues with ImageProcessor

If you are using ImageProcessingModule, which is used by default if your are using the image cropper, images request will not be routed to the media controller because they are handled differently. To catch also these request we need to register another handler for the ImageProcessingModule.ValidatingRequest() hook.

Video media items

This should be enough to protect media items, but it introduces an issue with the streaming of video. The FileStreamResult that we used above to return the media does not support by default the HTTP Range requests. From the docs:

HTTP range requests allow to send only a portion of an HTTP message from a server to a client. Partial requests are useful for large media or downloading files with pause and resume functions, for example.

In order to be able to stream a video in iOS device you are asked to support these partial requests, or the video won’t simply work on these i-device (Safari desktop included!). To add support to partial requests you need to intercept video request (eg: from file extension) in the MediaController before returning the FileStreamResult and construct a partial response with the requested bytes. You can use this solution, taken from here:

Call it from the MediaController before returning the FileStreamResult, and pass the current HttpContext and the mediaPath:

Conclusion

This completes the protection of media items in Umbraco. This solution works also in Umbraco Cloud (tested in v7.13).