Securing static folders and files for an ASP.NET site

Using an OAuth provider has become incredibly easy on ASP.NET.

Visual Studio provides a wizard if for example you have an Azure AD. If your organization is using Office365 is even possible to easily leverage that authentication mechanism.

You just right click and select the Configure Azure AD Authentication

And then fill out the necessary details

This wizard will generate some entries in the AppSettings

    <add key="ida:ClientId" value="<cliendID guid>" />
    <add key="ida:AADInstance" value="https://login.microsoftonline.com/" />
    <add key="ida:Domain" value="<your company domain>" />
    <add key="ida:TenantId" value="<tenant guid>" />
    <add key="ida:PostLogoutRedirectUri" value="https://localhost:44319/" />

If you are creating an web site for your organization and you need an authentication mechanism just to make sure that only people on your organization can use this website a nice option will be to be able to use Office365.

During this process however I found some issues becuase I had these requirements:

  • There were some static file and directories
  • All static content needs to be authenticated
  • There where some accessed that were relying on the default web server routing that if you browsed to a folder like folder1/folder2 and there is a folder1/folder2/index.html file then the web browser will return index.html for folder1/folder2.

Adding the static files was just a matter of adding the files to the project.
The second requirement was tricky but I found that using:

            routes.RouteExistingFiles = true;

Inside my RouteConfig was enough for authenticating my static resoures.

The last item was a little more tricky and I used a solution a bit hacky. What I did is that forced a routing entry for each of those index files.

            var serverHome = System.Web.Hosting.HostingEnvironment.MapPath("~");
            foreach(var indexhtml in System.IO.Directory.GetFiles(serverHome,
               "index.html",System.IO.SearchOption.AllDirectories))
            {
                var route = indexhtml.Replace(serverHome, "");
                route = route.Replace("\\index.html", "").Trim().Replace("\\", "/");
                if (!string.IsNullOrWhiteSpace(route))
                {
                    routes.Add("StaticRoute" + Guid.NewGuid().ToString(), 
                      new Route(route, new FileRouteHandler()));
                }
            }

I you are wondering what is the FileRouteHandler that is a custom IRouteHandler

    public class FileRouteHandler : IRouteHandler, IHttpHandler
    {
        public bool IsReusable
        {
            get
            {
                return true;
            }
        }

        public IHttpHandler GetHttpHandler(RequestContext requestContext)
        {
            return this;
        }

        public void ProcessRequest(HttpContext context)
        {
            if (!context.Request.IsAuthenticated)
            {
                context.Response.StatusCode = 401;
            }
            else
            {
                var serverHome = System.Web.Hosting.HostingEnvironment.MapPath("~");
                var path = context.Request.FilePath.Replace("/", "\\");
                if (path.StartsWith("\\")) path = path.Substring(1);
                var fullpath = System.IO.Path.Combine(serverHome, path, "index.html");
                if (System.IO.File.Exists(fullpath))
                    context.Response.WriteFile(fullpath);
            }
        }
    }

That is because I also needed to add Authentication validation on this routes to make sure that the user was logged in.

I do not think this is the best solution, but well it works.

An additional note. For some extensions you might need to add some settings to your web.config file for example for .json files: https://blogs.msdn.microsoft.com/africaapps/2013/06/07/how-to-serve-static-json-files-from-a-windows-azure-website/
Thanks Oscar for this tip