Home > Enterprise >  how do I password protect all pages except login page in ASP.NET Core 6 MVC app?
how do I password protect all pages except login page in ASP.NET Core 6 MVC app?

Time:11-04

I have created a asp.net core 6 mvc web application. I did not add user authorization from start but added ASP.NET Core Identity afterwards instead. My question is how do I make all pages password protected except the actual login page that the users shoud be redirected to if they havent logged in yet? Is that something I configure in program.cs or how do i do that?

here is my program.cs file...

        var builder = WebApplication.CreateBuilder(args);

        builder.Services.Configure<IdentityOptions>(options =>
        {
            // Password settings.
            options.Password.RequireDigit = true;
            options.Password.RequireLowercase = true;
            options.Password.RequireNonAlphanumeric = true;
            options.Password.RequireUppercase = true;
            options.Password.RequiredLength = 6;
            options.Password.RequiredUniqueChars = 1;

            // Lockout settings.
            options.Lockout.DefaultLockoutTimeSpan = TimeSpan.FromMinutes(5);
            options.Lockout.MaxFailedAccessAttempts = 5;
            options.Lockout.AllowedForNewUsers = true;

            // User settings.
            options.User.AllowedUserNameCharacters =
                "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789-._@ ";
            options.User.RequireUniqueEmail = false;
        });

        
        // Add services to the container.
        builder.Services.AddControllersWithViews();

        
        var connectionString = builder.Configuration.GetConnectionString("AppDb");
        builder.Services.AddDbContext<ApplicationDbContext>(x => x.UseSqlServer(connectionString));

         
        builder.Services.AddIdentityCore<ApplicationUser>().AddEntityFrameworkStores<ApplicationDbContext>();


        var app = builder.Build();


        // Configure the HTTP request pipeline.
        if (!app.Environment.IsDevelopment())
        {
            app.UseExceptionHandler("/Home/Error");
            // The default HSTS value is 30 days. You may want to change this for production scenarios, see https://aka.ms/aspnetcore-hsts.
            app.UseHsts();
        }



        app.UseHttpsRedirection();
        app.UseStaticFiles();

        app.UseRouting();

        app.UseAuthorization();
        app.UseAuthenticationCheck();

        //app.UseAuthentication();

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

        app.Run();

Update I added this middleware to check if user is authenticated, if not he need to login on the login page. But it does not work beacuse I get a error telling me "localhost redirected you too many times."

Here is my middleware..

    public class AuthenticationCheck
{
    private readonly RequestDelegate _next;

    public AuthenticationCheck(RequestDelegate next, ILoggerFactory logFactory)
    {
        _next = next;
    }

    public async Task Invoke(HttpContext httpContext)
    {
        if (httpContext.User.Identity != null || !httpContext.User.Identity.IsAuthenticated)
        {
            httpContext.Response.Redirect("Login");
        }
        else
        {
            await _next(httpContext);
        }
    }
}

// Extension method used to add the middleware to the HTTP request pipeline.
public static class AuthenticationCheckExtensions
{
    public static IApplicationBuilder UseAuthenticationCheck(this IApplicationBuilder builder)
    {
        return builder.UseMiddleware<AuthenticationCheck>();
    }
}

What am I missing here...?

CodePudding user response:

To achieve this goal you can use the Authorized attribute.

If you want to apply this rule to all routes of your application and don't repeat the attribute for each controller/action you have, you can define it in middleware configuration. to do that, update your routing like the code below

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

These enable Authorization for all of your routes.

The RequireAuthorization method applicable in UseEndpoints, MapRazorPages, and any other approach. after changing this, for work this mechanism you must add this code block after the UseRouting middleware.

app.UseAuthentication();
app.UseAuthorization();

this enables you to handle Authentication and your Authorization Policies.

after all of that, for excluding some routes (like the login page) from Authentication you must add AllowAnonymous attribute on your controller/action

CodePudding user response:

Yes I added a middleware now but that one is something wrong with since I get error in browser "localhost redirected you too many times." I have updated my code above. Can you see what I am doing wrong?

Yes its quite obvious to fall into infinite loop because it will check the authentication and kept failing and keep redirecting. I am wondering you are so closed and the scenario was out of the box which is not common case and other contributor doesn't understand your requirement at all. Our plan was to not using Authorize attribute hence, will restrict the user and finally redirect to login page.

Solution:

Middleware Class:

public class ReverseAuthMiddleware
    {
        private readonly RequestDelegate _next;
        public ReverseAuthMiddleware(RequestDelegate next)
        {
            _next = next;
        }
        public async Task InvokeAsync(HttpContext httpContext)
        {
           //bool isAuthorized = true;
           var isAuthorized = httpContext.User.Claims.Any(c => c.Type != null && c.Value != null);
           
           
            if (!isAuthorized && httpContext.Request.Path.Value != "/Login/Index")
            {
                httpContext.Response.Redirect("/Login/Index");
            }

            //If the user authenticated and the Path not login then we can Move forward into the pipeline
            await _next(httpContext);
        }
    }

Note: Let's explain the scenario, we have to consider two scenario, user has to login to access any page.Thus, either he can access login page in case of unauthorized user or can access any page once authenticated. Therefore, we need to check both authentication and which page he is trying to access other than, we will be into forever loop. In addtion. I am checking httpContext.User.Claims however, yours httpContext.User.Identity also fine.

Program.cs:

app.UseMiddleware<ReverseAuthMiddleware>();

Output:

enter image description here

  • Related