Search code examples
c#asp.net-core.net-coreasp.net-core-identity

Authorize only controller allows anonymous access in .net core


I have setup identity in a .net core web app, and marked a certain controller as authorize like this..

[Authorize(Roles = "Partner")]
public class ClaimsController : Controller
{
    [Authorize(Roles = "Partner")]
    public IActionResult Index()
    {
        var authenticated = User.Identity.IsAuthenticated;
        //authenticated is false - but this view still loads?!
        return View();          
    }
}

So only users in the partner role should have access.. However someone not logged in at all can load and view the Index view on the claims controller.. I could check if someone is logged in and check the role user explicitly with the user manager but surely these attributes should do something?

Is there something extra I need in startup.cs in core 3? This is my startup.cs file..

public class Startup
{
    private readonly IConfiguration _config;

    public Startup(IConfiguration config)
    {
        _config = config;
    }

    // This method gets called by the runtime. Use this method to add services to the container.
    // For more information on how to configure your application, visit https://go.microsoft.com/fwlink/?LinkID=398940
    public void ConfigureServices(IServiceCollection services)
    {
        var connstring = _config.GetConnectionString("HP_RBS_Database");

        //we can create our own role and derive from IdentityRole
        services.AddIdentity<UserLogin, IdentityRole>(x =>
        {
            x.User.RequireUniqueEmail = true;
            //set password rules in here..
        })  //specify where we store identity data
        .AddEntityFrameworkStores<HP_RBS_Context>();

        services.AddMvc();          
        services.AddRazorPages();
        services.AddControllersWithViews().AddRazorRuntimeCompilation();
        services.AddDbContext<HP_RBS_Context>(x =>
            {
                x.UseSqlServer(connstring);
            });

        services.AddTransient<HPPartnerPortalSeeder>();
        services.AddScoped<IHP_RBS_Repository, HP_RBS_Repository>();
        services.AddAuthentication();
        services.AddAuthorization();


    }

    // This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
    public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
    {
        if (env.IsDevelopment())
        {
            app.UseDeveloperExceptionPage();
        }

        app.UseStaticFiles();
        app.UseAuthentication();
        app.UseAuthorization();
        app.UseRouting();
        app.UseEndpoints(x =>
        {
            x.MapControllerRoute("Default",
                "{controller}/{action}/{id?}",
                new { controller = "Home", action = "Index" });
        });
    }
}

Solution

  • The calls to UseAuthentication and UseAuthorization must be placed between UseRouting and UseEndpoints:

    app.UseRouting();
    app.UseAuthentication();
    app.UseAuthorization();
    app.UseEndpoints(x =>
    {
        x.MapControllerRoute("Default",
            "{controller}/{action}/{id?}",
            new { controller = "Home", action = "Index" });
    });
    

    When these calls are placed before UseRouting, the UseAuthorization call is somewhat of a no-op. It checks to see whether an endpoint has been selected, but this hasn't happened yet. The selection process is performed courtesy of the UseRouting call that runs next, which is too late.

    Unfortunately, this means that the MVC endpoint runs as though authorisation succeeded, eventhough it wasn't performed at all. This is a known issue in the 3.0.0 release of ASP.NET Core, which has been fixed in the 3.0.1 release.