I need to be able to support "auxiliary" content on a MVC (4) web site -- images, PDFs, videos, etc. Most of this content is provided by the customer using the site so it should not be accessible to unauthenticated users.
(We're using Forms Authentication to generate an authentication ticket. Note, we are not necessarily using the built-in membership/identity providers, as some of our customers want to use authenticate via other means e.g. a federated identity service.)
I've secured the static content by setting up the site to route/authorize all requests through ASP.NET (via runAllManagedModulesForAllRequests
) as described here.
I found that I needed to allow anonymous access to the Content
and Scripts
folders (via location
) ... but this configuration works for most content types.
It does not however work for video content, specifically in Internet Explorer. Media Player comes up but reports that it cannot play the file. "The player might not support the file type or coded that was used to create the file."
(Interestingly enough, there's no error in Chrome or Firefox. The video plays in a new tab.)
I'm fairly certain it's Media Player that is the issue. If I add a location
element and allow anonymous access to the video, it plays just fine. And we've had similar problems in the past where the root cause ultimately turned out to be security- / authentication- related.
But obviously I can't change Media Player. And I have to support IE. So ... does anyone know of a way to work around this issue programmatically in ASP.NET MVC? Any help would be appreciated.
I seem to recall seeing an SO post about sending the authentication information not just with HTTP requests. I can't locate that post now. But according to Fiddler the request is in fact an HTTP GET. And we're not sending an authentication header, but we are sending an authentication cookie.
Update:
I thought I would be able to adapt the answer to this question.
It involves appending a flag and the authorization cookie value to the content URL, and hooking in to the Application_BeginRequest
event.
In the view:
var asset = ...;
var authToken = Request.Cookies[FormsAuthentication.FormsCookieName].Value;
var assetUri = string.Format("~/Media/{0}?requireAuthSync=true&token={1}",
asset.ID, Url.Encode(authToken));
Then in Global.asax.cs
:
protected void Application_BeginRequest()
{
if (!string.IsNullOrEmpty(Context.Request["requireAuthSync"]))
{
AuthCookieSync();
}
}
private void AuthCookieSync()
{
var authParamName = "token";
var authCookieName = FormsAuthentication.FormsCookieName;
try
{
if (!string.IsNullOrEmpty(Context.Request[authParamName]))
{
var authCookieValue = Context.Request.QueryString[authParamName];
var authCookie = Context.Request.Cookies.Get(authCookieName)
?? new HttpCookie(authCookieName);
authCookie.Value = authCookieValue;
Context.Request.Cookies.Set(authCookie);
}
}
catch
{
}
}
Using this approach, the video does in fact play in IE.
However, it's now possible to bookmark the video (or other content) and use the bookmark to access the content without being authenticated ... so the site security is effectively broken.
So it seems, in order to preserve site security, I shouldn't be creating the cookie if it doesn't already exist. But with Media Player, creating the cookie is the only way to get the video to play. I appear to be stuck in a Catch-22.
Any insight would be much appreciated.