Forum

PureSourceCode.com Forum
Share:
Notifications
Clear all

Protect Static Files with Authentication on ASP.NET Core

1 Posts
1 Users
0 Likes
1,496 Views
(@enrico)
Member Admin
Joined: 4 years ago
Posts: 151
Topic starter  

In my previous question, I asked a generic question how to add permission for static content. Here I want to be more precise.

In my project I added a folder under wwwroot to simplify the code, where I save the html files I want to protect. This folder is called infographics.

The properties for each file are:

  • Build Action: Content
  • Copy to Output Directory: Do not copy

Follow the instruction from the Microsoft documentation, I changed the Startup.cs

public class Startup
{
    public Startup(IConfiguration configuration)
    {
        Configuration = configuration;
    }

    public IConfiguration Configuration { get; }

    // This method gets called by the runtime. Use this method to add services to the container.
    public void ConfigureServices(IServiceCollection services)
    {
        services.AddControllersWithViews();
        services.Configure<IdentityServerConfiguration>(Configuration.GetSection("IdentityServerConfiguration"));

        services.AddDistributedMemoryCache();

        services.AddSession(options =>
        {
            options.Cookie.Name = ".my.Session";
            options.IdleTimeout = TimeSpan.FromHours(12);
        });

        services.AddAuthentication(options =>
        {
            options.DefaultScheme = CookieAuthenticationDefaults.AuthenticationScheme;
            options.DefaultChallengeScheme = "oidc";
        })
        .AddCookie(options =>
        {
            options.ExpireTimeSpan = TimeSpan.FromMinutes(30);
            options.Cookie.Name = "my.dashboard";
        })
        .AddOpenIdConnect("oidc", options =>
        {
            IdentityServerConfiguration idsrv = Configuration.GetSection("IdentityServerConfiguration")
                                                .Get<IdentityServerConfiguration>();
            options.Authority = idsrv.Url;
            options.ClientId = idsrv.ClientId;
            options.ClientSecret = idsrv.ClientSecret;

            #if DEBUG
            options.RequireHttpsMetadata = false;
            #else
            options.RequireHttpsMetadata = true;
            #endif

            options.ResponseType = "code";

            options.Scope.Clear();
            options.Scope.Add("openid");
            options.Scope.Add("profile");
            options.Scope.Add("email");
            options.Scope.Add("roles");
            options.Scope.Add("offline_access");

            options.ClaimActions.MapJsonKey("role", "role", "role");

            options.GetClaimsFromUserInfoEndpoint = true;
            options.SaveTokens = true;

            options.SignedOutRedirectUri = "/";

            options.TokenValidationParameters = new TokenValidationParameters
            {
                NameClaimType = JwtClaimTypes.Name,
                RoleClaimType = JwtClaimTypes.Role,
            };
        });
    }

    public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
    {
        if (env.IsDevelopment())
        {
            app.UseDeveloperExceptionPage();
        }
        else
        {
            app.UseExceptionHandler("/Home/Error");
            app.UseHsts();
        }
        app.UseHttpsRedirection();

        app.UseAuthentication();

        app.UseStaticFiles(new StaticFileOptions
        {
            OnPrepareResponse = ctx =>
            {
                if (ctx.Context.Request.Path.StartsWithSegments("/infographics"))
                {
                    ctx.Context.Response.Headers.Add("Cache-Control", "no-store");

                    if (!ctx.Context.User.Identity.IsAuthenticated)
                    {
                        // respond HTTP 401 Unauthorized with empty body.
                        ctx.Context.Response.StatusCode = (int)HttpStatusCode.Unauthorized;
                        ctx.Context.Response.ContentLength = 0;
                        ctx.Context.Response.Body = Stream.Null;

                        // - or, redirect to another page. -
                        // ctx.Context.Response.Redirect("/");
                    }
                }
            }
        });

        app.UseRouting();

        app.UseAuthorization();

        app.UseCookiePolicy();
        app.UseSession();

        app.UseEndpoints(endpoints =>
        {
            endpoints.MapControllerRoute(
                name: "default",
                pattern: "{controller=Home}/{action=Index}/{id?}");
        });
    }
}

In my previous question, I asked a generic question how to add permission for static content. Here I want to be more precise.

In my project I added a folder under wwwroot to simplify the code, where I save the html files I want to protect. This folder is called infographics.

enter image description here

The properties for each file are:

  • Build Action: Content
  • Copy to Output Directory: Do not copy

enter image description here

Follow the instruction from the Microsoft documentation, I changed the Startup.cs

public class Startup
{
    public Startup(IConfiguration configuration)
    {
        Configuration = configuration;
    }

    public IConfiguration Configuration { get; }

    // This method gets called by the runtime. Use this method to add services to the container.
    public void ConfigureServices(IServiceCollection services)
    {
        services.AddControllersWithViews();
        services.Configure<IdentityServerConfiguration>(Configuration.GetSection("IdentityServerConfiguration"));

        services.AddDistributedMemoryCache();

        services.AddSession(options =>
        {
            options.Cookie.Name = ".my.Session";
            options.IdleTimeout = TimeSpan.FromHours(12);
        });

        services.AddAuthentication(options =>
        {
            options.DefaultScheme = CookieAuthenticationDefaults.AuthenticationScheme;
            options.DefaultChallengeScheme = "oidc";
        })
        .AddCookie(options =>
        {
            options.ExpireTimeSpan = TimeSpan.FromMinutes(30);
            options.Cookie.Name = "my.dashboard";
        })
        .AddOpenIdConnect("oidc", options =>
        {
            IdentityServerConfiguration idsrv = Configuration.GetSection("IdentityServerConfiguration")
                                                .Get<IdentityServerConfiguration>();
            options.Authority = idsrv.Url;
            options.ClientId = idsrv.ClientId;
            options.ClientSecret = idsrv.ClientSecret;

            #if DEBUG
            options.RequireHttpsMetadata = false;
            #else
            options.RequireHttpsMetadata = true;
            #endif

            options.ResponseType = "code";

            options.Scope.Clear();
            options.Scope.Add("openid");
            options.Scope.Add("profile");
            options.Scope.Add("email");
            options.Scope.Add("roles");
            options.Scope.Add("offline_access");

            options.ClaimActions.MapJsonKey("role", "role", "role");

            options.GetClaimsFromUserInfoEndpoint = true;
            options.SaveTokens = true;

            options.SignedOutRedirectUri = "/";

            options.TokenValidationParameters = new TokenValidationParameters
            {
                NameClaimType = JwtClaimTypes.Name,
                RoleClaimType = JwtClaimTypes.Role,
            };
        });
    }

    public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
    {
        if (env.IsDevelopment())
        {
            app.UseDeveloperExceptionPage();
        }
        else
        {
            app.UseExceptionHandler("/Home/Error");
            app.UseHsts();
        }
        app.UseHttpsRedirection();

        app.UseAuthentication();

        app.UseStaticFiles(new StaticFileOptions
        {
            OnPrepareResponse = ctx =>
            {
                if (ctx.Context.Request.Path.StartsWithSegments("/infographics"))
                {
                    ctx.Context.Response.Headers.Add("Cache-Control", "no-store");

                    if (!ctx.Context.User.Identity.IsAuthenticated)
                    {
                        // respond HTTP 401 Unauthorized with empty body.
                        ctx.Context.Response.StatusCode = (int)HttpStatusCode.Unauthorized;
                        ctx.Context.Response.ContentLength = 0;
                        ctx.Context.Response.Body = Stream.Null;

                        // - or, redirect to another page. -
                        // ctx.Context.Response.Redirect("/");
                    }
                }
            }
        });

        app.UseRouting();

        app.UseAuthorization();

        app.UseCookiePolicy();
        app.UseSession();

        app.UseEndpoints(endpoints =>
        {
            endpoints.MapControllerRoute(
                name: "default",
                pattern: "{controller=Home}/{action=Index}/{id?}");
        });
    }
}

What I expect is when the user asks for /infographics the OnPrepareResponse verifies the request and if the user is authenticated sees the page. But, after a lot of code changing, the result is always the same (on my local machine):

This localhost page can’t be found

I tried to add this code to map the folder html in the root of the project as infographics but without success.

app.UseStaticFiles(new StaticFileOptions
{
    FileProvider = new PhysicalFileProvider(
        Path.Combine(env.ContentRootPath, "html")),
    RequestPath = "/infographics"
});

Any ideas?

Update

This is working with not HTML files. I think the problem comes from the HTML file because they are static content for ASP.NET.

I put a breakpoint on the OnPrepareResponse and call the page infographics/index.html. The page is displayed (red arrow) and then the application stops on the breakpoint (blue arrow).


   
Quote
Share: