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

Login in Asp.NET Core 2


Question on how to properly login in Asp.NET Core V2. I am using ASP.NET Identity.

My OnPostAsync() method is below. My code successfully takes the user's name and pwd, calls signin manager, and is successfully returning a true. I would think that the proper way to login is to call SigninPasswordAsync. A succeeded result comes back.

    public async Task<IActionResult> OnPostAsync()
    {
        if (!ModelState.IsValid)
        {
            return Page();
        }
        var userName = Request.Form["UserName"];
        var pwd = Request.Form["Password"];
        var appUser = new ApplicationUser() { UserName = userName };
        var signin = await _signInManager.PasswordSignInAsync(userName, pwd, true, false);
        if (signin.Succeeded)
        {
            return RedirectToPage("/Account/LoggedIn");
        }
        else
        {
            return RedirectToPage("/Account/Login");
        }
    }

So, once the redirect happens, it redirects to the LoggedIn razor page. The contents of the PageModel is below. The problem is the using the [Authorize] attribute causes the page to not load and be redirect to the Login page, which is what I would expect if the conditions of the [Authorize] attribute are met. The authorize conditions are not being met. Digging into this seems to indicate that the HttpContext.User doesn't seem to have much/any content in it. I am assuming that I need to call something besides the SigninPasswordAsync method or use a different attribute. Thoughts? Do I need to do something else? I'm lost on what to do at this point, so any thoughts are appreciated. Thanks.

[Authorize]
public class LoggedInModel : PageModel
{
    public void OnGet()
    {
        var use = HttpContext.User;
    }
}

**** Update ****************************


I am adding the following content from my Startup.cs file:

    public static IConfigurationRoot Configuration { get; set; }
    // 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 builder = new ConfigurationBuilder()
       .SetBasePath(System.IO.Directory.GetCurrentDirectory())
       .AddJsonFile("appsettings.json");

        Configuration = builder.Build();
        services.AddDbContext<PooperAppDbContext>(options =>
            options.UseSqlServer(Configuration.GetConnectionString("DefaultConnection")));

        services.AddIdentity<ApplicationUser, ApplicationRole>()
            .AddEntityFrameworkStores<PooperAppDbContext>()
            .AddDefaultTokenProviders();

        services.AddScoped<SignInManager<ApplicationUser>>();

        services.Configure<IdentityOptions>(options =>
        {
            // Password settings
            options.Password.RequireDigit = true;
            options.Password.RequiredLength = 8;
            options.Password.RequireNonAlphanumeric = false;
            options.Password.RequireUppercase = true;
            options.Password.RequireLowercase = false;
            options.Password.RequiredUniqueChars = 6;

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

            // User settings
            options.User.RequireUniqueEmail = true;

        });

        services.ConfigureApplicationCookie(options =>
        {
            // Cookie settings
            options.Cookie.HttpOnly = true;
            options.Cookie.Expiration = TimeSpan.FromDays(150);
            options.LoginPath = "/Account/Login"; // If the LoginPath is not set here, ASP.NET Core will default to /Account/Login
            options.LogoutPath = "/Account/Logout"; // If the LogoutPath is not set here, ASP.NET Core will default to /Account/Logout
            options.AccessDeniedPath = "/Account/AccessDenied"; // If the AccessDeniedPath is not set here, ASP.NET Core will default to /Account/AccessDenied
            options.SlidingExpiration = true;
        });

        services.AddMvc().AddRazorPagesOptions(options =>
        {
            //options.Conventions.AuthorizeFolder("/MembersOnly");
            options.Conventions.AuthorizePage("/Account/Logout");
            options.Conventions.AuthorizePage("/Account/LoggedIn", "PooperBasic, PooperPayer"); // with policy
            //options.Conventions.AllowAnonymousToPage("/Pages/Admin/Login"); // excluded page

            //options.Conventions.AllowAnonymousToFolder("/Public"); // just for completeness
        });

        services.AddAuthorization(options =>
        {
            options.AddPolicy("RequireAdministratorRole", policy => policy.RequireRole("Administrator"));
        });
    }

    // This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
    public void Configure(IApplicationBuilder app, IHostingEnvironment env)
    {
        if (env.IsDevelopment())
        {
            //app.UseDeveloperExceptionPage();
        }
        else
        {
            var options = new RewriteOptions()
                .AddRedirectToHttps();
        }
        app.UseMvc();
        app.UseAuthentication();
    }
}

Solution

  • You need to call UseAuthentication before UseMvc. All middleware runs as part of a pipeline, so in your case the authentication middleware is not being called when you expect.

    Have a look at the docs for a good description of the middleware pipeline.

    Note: You should not need your call to services.AddScoped<SignInManager<ApplicationUser>>(); as this will be handled by the AddIdentity stuff.