0

I am trying to deny access to a folder or resources when not logged in (prevent leeching). In the folder I have my

Web.config: (/Media)

<?xml version="1.0"?>
<configuration>
  <system.web>
    <authorization>
      <deny users="?"/>
      <allow users="*" />
    </authorization>
  </system.web>
</configuration>

The code I am calling:

Index:

@Video.MediaPlayer(
    path: "~/Media/Tree Felling2.wmv",
    width: "600",
    height: "400",
    autoStart: false,
    playCount: 1,
    uiMode:  "full",
    stretchToFit: true,
    enableContextMenu: true,
    mute: false,
    volume: 75)

@Video.Flash(path: "~/Media/sample.swf",
             width: "80%",
             //height: "600",
             play: true,
             loop: false,
             menu:  true,
             bgColor: "red",
             quality: "medium",
             //scale: "showall",
             windowMode: "transparent")

When logged out: flash is not shown. Media player wont connect to media. ( AS EXPECTED )

When logged in: flash is shown. But media player still wont connect to media.

Where am I going wrong?..

IAmGroot
  • 13,760
  • 18
  • 84
  • 154
  • Does this work if you remove the authentication completely? Could be a problem with the code/file for that and it has nothing to do with the authentication. – ChrisBint Aug 15 '12 at 09:33
  • @ChrisBint If i remove the authentication they both play yes. Im unsure why Flash is in the title. Since its media/files in general. – IAmGroot Aug 15 '12 at 09:38
  • 1
    @Doomsknight People like to assume there opinion is the only one that matters. On a more pressing matter, does the wmv file play if you only have the media player attribute on the page? – ChrisBint Aug 15 '12 at 09:43
  • @ChrisBint. Nope. It doesn't play. Its suppose to Deny unknown, then allow everyone else. (On a first match basis). And I dont get why the flash is able to access it while media player cannot. So annoying. – IAmGroot Aug 15 '12 at 09:49
  • 3
    Have you some kind of HTTP debugger (like Fiddler or the net tab built in to Firebug/Chrome dev tools) to view the HTTP status codes returned by the server when the media player requests the wmv file? When logged out it should give a 401 unauthorized, and when logged in should give a 200. What is the status code returned for the wmv when logged in? – Ian Routledge Aug 15 '12 at 09:50
  • @IanRoutledge Good suggestion. I would perhaps try another file as well in the same folder. Should not make a difference, but more evidence. – ChrisBint Aug 15 '12 at 09:54
  • @IanRoutledge Ive tried changing the file name. Hence the 2, and yea, it doesnt help. Ok, so I have (302: MediaPlayer - Login/Logout) (200: Flash - Login. 302: Flash - Logout) – IAmGroot Aug 15 '12 at 09:57
  • @IanRoutledge Actually I get 200 on page load, but when I press play, it is trying to connect again? with constant 302. – IAmGroot Aug 15 '12 at 10:01
  • You could try removing the attribute in the config. The should cover your requirements and there should not be a need to explicitly allow on top of that. – ChrisBint Aug 15 '12 at 10:03
  • @ChrisBint. Doesn't seem to make a difference, but yes, isnt needed. – IAmGroot Aug 15 '12 at 10:04
  • @Doomsknight Strange indeed. Sounds like the inner workings of MediaPlayer do not comply with the authentication! Have you tried an absolute path (remove ~)? – ChrisBint Aug 15 '12 at 10:09
  • Is this running in IIS or the web server built into Visual Studio? IIS might be sending a cache header whereas Visual Studio doesn't (I have seen odd things like this), although that wouldn't explain why the flash works but the wmv doesn't. Look for a cache-control/expires header on the initial wmv response – Ian Routledge Aug 15 '12 at 10:10
  • @IanRoutledge `Cache-Control: max-age=0` Which I assume is infinite. Published to IIS with same effect :| . Direct path is same result. – IAmGroot Aug 15 '12 at 10:20
  • 1
    Sorry, strange indeed and I am unable to suggest anything else. Good luck. – ChrisBint Aug 15 '12 at 10:31
  • @ChrisBint, IanRoutledge. Thanks anyway guys. I've just used a JS Player, that supports Mp4. I guess its either stick to that format ONLY. Or Change the way I authenticate access to my media (to prevent leeching). – IAmGroot Aug 15 '12 at 10:35
  • Strange indeed. Maybe try cache-control: no-cache instead of max-age=0? Which browser is this? Have you tried both IE and Chrome? I've had countless problems with IE caching in the past. Apart from that, like @ChrisBint, I'm running out of ideas but you could try re-engineering it to server the videos via controller actions (FileResult), with an Authorize attribute on them to do the check, that way you have more control and can debug the requests more easily. I'd also suggest using MP4s via an HTML5 player with Flash fallback so you get more coverage across different devices. – Ian Routledge Aug 15 '12 at 10:36
  • Also, if you're going to go down that route, I'd suggest using this: http://mvcresumingactions.codeplex.com/ which responds to byte range requests which makes streaming video work (better), especially on devices. Good luck! – Ian Routledge Aug 15 '12 at 10:38

1 Answers1

5

Unfortunately this is a known bug with the Windows Media Player for FF. It will work in IE.

The reason for this not working is pretty simple: the plugin doesn't send the authentication cookie along with the request so it is as if you are not authenticated.

The only way to make this work is to append the cookie value as a query string parameter to the request and then resynchronize the session on the server.

Let's put that into action, shall we?

Unfortunately we cannot use the @Video.MediaPlayer helper because it doesn't allow you to specify query string parameters, it works only with physical files (which kinda sucks). So:

<object classid="clsid:6BF52A52-394A-11D3-B153-00C04F79FAA6" height="400" width="600" >
    <param name="URL" value="@Url.Content("~/media/test.wmv?requireAuthSync=true&token=" + Url.Encode(Request.Cookies[FormsAuthentication.FormsCookieName].Value))" />
    <param name="autoStart" value="False" />
    <param name="uiMode" value="full" />
    <param name="stretchToFit" value="True" />
    <param name="volume" value="75" />
    <embed src="@Url.Content("~/media/test.wmv?requireAuthSync=true&token=" + Url.Encode(Request.Cookies[FormsAuthentication.FormsCookieName].Value))" width="600" height="400" type="application/x-mplayer2" autoStart="False" uiMode="full" stretchToFit="True" volume="75" />
</object>

and inside Global.asax we subscribe to the Application_BeginRequest method and resync up the authentication cookie from the request:

protected void Application_BeginRequest()
{
    if (!string.IsNullOrEmpty(Context.Request["RequireAuthSync"]))
    {
        AuthCookieSync();
    }
}

private void AuthCookieSync()
{
    try
    {
        string authParamName = "token";
        string authCookieName = FormsAuthentication.FormsCookieName;

        if (!string.IsNullOrEmpty(Context.Request[authParamName]))
        {
            UpdateCookie(authCookieName, Context.Request.QueryString[authParamName]);
        }
    }
    catch { }
}

private void UpdateCookie(string cookieName, string cookieValue)
{
    var cookie = Context.Request.Cookies.Get(cookieName);
    if (cookie == null)
    {
        cookie = new HttpCookie(cookieName);
    }
    cookie.Value = cookieValue;
    Context.Request.Cookies.Set(cookie);
}

And that's pretty much it. The only requirement for this to work is to be running in IIS 7 Integrated Pipeline Mode in order for all requests to go through ASP.NET, even those for .wmv files, otherwise the BeginRequest will obviously never trigger for them.

If you are using some legacy web server (such as IIS 6.0) or running in Classic Pipeline mode and don't want to do a wildcard mapping of all requests with ASP.NET you could put all your media files in a secure location (such as ~/App_Data) that cannot be directly accessed by users and then serve them through a controller action decorated with the [Authorize] attribute:

[Authorize]
public ActionResult Media(string file)
{
    var appData = Server.MapPath("~/App_Data");
    var filename = Path.Combine(path, file);
    filename = Path.GetFullPath(filename);
    if (!filename.StartsWith(appData))
    {
        // prevent people from reading arbitrary files from your server
        throw new HttpException(403, "Forbidden");
    }
    return File(filename, "application/octet-stream");
}

and then:

<object classid="clsid:6BF52A52-394A-11D3-B153-00C04F79FAA6" height="400" width="600" >
    <param name="URL" value="@Url.Action("media", "home", new { requireAuthSync = true, token = Request.Cookies[FormsAuthentication.FormsCookieName].Value })" />
    <param name="autoStart" value="False" />
    <param name="uiMode" value="full" />
    <param name="stretchToFit" value="True" />
    <param name="volume" value="75" />
    <embed src="@Url.Action("media", "home", new { requireAuthSync = true, token = Request.Cookies[FormsAuthentication.FormsCookieName].Value })" width="600" height="400" type="application/x-mplayer2" autoStart="False" uiMode="full" stretchToFit="True" volume="75" />
</object>
Tom Chantler
  • 14,753
  • 4
  • 48
  • 53
Darin Dimitrov
  • 1,023,142
  • 271
  • 3,287
  • 2,928
  • Would this App_Data folder be sufficent in preventing leeching by itself? – IAmGroot Aug 15 '12 at 11:00
  • 1
    @Doomsknight, yes it will be sufficient. But of course if you put those files there absolutely nobody from the client side will be able to access them. You will have to write a controller action to serve those files and protect this controller action with the `[Authorize]` attribute in order to comply with your requirement that only authenticated users can access this media. I have illustrated this in the second part of my answer. – Darin Dimitrov Aug 15 '12 at 11:03
  • +1 Good answer. I didn't know anything about any of this before and now I feel I understand it completely. – Tom Chantler Aug 15 '12 at 11:26
  • Okay. So I changed to ` – IAmGroot Aug 15 '12 at 11:48
  • 1
    @Doomsknight, wait a minute - you want to prevent access to this `.wmv` file even if logged in??? That's absolutely impossible. Well, actually it is but how do you expect Media Player to be able to access it then? And, no, you cannot prevent the user from accessing this content. No matter what you do if Media Player can access it, any authenticated user will eventually be able to access it, no matter what you do. So you will have to live with that. Remember that you could scrape videos from youtube the same way, no matter what they try and how difficult they make it. – Darin Dimitrov Aug 15 '12 at 11:50
  • @DarinDimitrov. Cool thanks. As long as an expert like yourself says so. just needed to make sure. :) Delivering tutorials. Making sure they are logged in should be sufficient. Js thought if we could prevent them having easy download, would be useful. But tbh theres so many plugins that would make this too hard as well. – IAmGroot Aug 15 '12 at 11:56