Rather than migrating an existing ASP.NET Core 2.1 project to 3.1 I've created a new project. In the new project the SignIn(...) controller method executes without error but then it fails to redirect to the specified action. Instead it is redirecting to AccessDeniedPath.
My environment is Win10 VS 2019 IIS Express using Chrome.
Can anyone advise what could be causing this? Is there something I'm missing or doing wrong in 3.1?
PROGRAM.CS
public static void Main(string[] args)
{
CreateHostBuilder(args).Build().Run();
}
public static IHostBuilder CreateHostBuilder(string[] args) =>
Host.CreateDefaultBuilder(args)
.ConfigureWebHostDefaults(webBuilder =>
{
webBuilder.UseStartup<Startup>();
//webBuilder.UseIISIntegration();
//webBuilder.UseKestrel();
//webBuilder.CaptureStartupErrors(true);
//webBuilder.UseEnvironment(Environments.Development);
});
STARTUP.CS
public void ConfigureServices(IServiceCollection services)
{
try
{
services.AddRazorPages()
.AddRazorRuntimeCompilation();
services.AddControllersWithViews();
services.AddAuthentication(CookieAuthenticationDefaults.AuthenticationScheme)
.AddCookie(CookieAuthenticationDefaults.AuthenticationScheme, options =>
{
options.ExpireTimeSpan = new TimeSpan(30, 0, 0, 0);
options.LoginPath = new PathString("/Home/Index/");
options.AccessDeniedPath = new PathString("/Home/Index/");
options.LogoutPath = new PathString("/Home/Index/");
options.Validate();
});
services.Configure<Microsoft.AspNetCore.Identity.IdentityOptions>(options =>
{
options.Password.RequireDigit = true;
options.Password.RequireLowercase = true;
options.Password.RequireNonAlphanumeric = true;
options.Password.RequireUppercase = true;
options.Password.RequiredLength = 8;
options.Password.RequiredUniqueChars = 1;
options.Lockout.DefaultLockoutTimeSpan = TimeSpan.FromMinutes(5);
options.Lockout.MaxFailedAccessAttempts = 5;
options.Lockout.AllowedForNewUsers = true;
options.User.AllowedUserNameCharacters = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789-._@+";
});
services.AddDetectionCore()
.AddDevice();
services.AddMvc();
services.AddAntiforgery();
services.Configure<MvcOptions>(options =>
{
options.Filters.Add(new RequireHttpsAttribute());
});
}
catch (Exception ex)
{
gFunc.ProcessError(ex);
}
}
public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
try
{
if (env.IsDevelopment())
{
app.UseDeveloperExceptionPage();
}
else
{
app.UseExceptionHandler("/Home/Error");
app.UseHsts();
}
app.UseStaticFiles();
app.UseHttpsRedirection();
app.UseRouting();
app.UseAuthorization();
app.UseAuthentication();
app.UseEndpoints(endpoints =>
{
endpoints.MapControllerRoute(
name: "default",
pattern: "{controller=Home}/{action=Index}/{id?}");
});
}
catch (Exception ex)
{
gFunc.ProcessError(ex);
}
}
CONTROLLER.CS
[HttpPost()]
[AllowAnonymous]
[ValidateAntiForgeryToken]
public async Task<ActionResult> SignIn(SignIn sin)
{
bool is_err = false;
try
{
// check data
if (string.IsNullOrEmpty(sin.EmailAddress))
{
is_err = true;
ModelState.AddModelError("Samadhi", "Missing email address.");
}
if (string.IsNullOrEmpty(sin.Password))
{
is_err = true;
ModelState.AddModelError("Samadhi", "Missing password.");
}
// check authorisation
if (ModelState.IsValid && !is_err)
{
sin = await RepoSamadhi.ShopSignIn(sin);
if (sin.ShopID == 0 || sin.IsValidationFail || string.IsNullOrEmpty(sin.ShopToken))
{
is_err = true;
ModelState.AddModelError("Samadhi", "Account not found. Check your credentials.");
}
}
// check model state
if (!ModelState.IsValid || is_err)
{
sin.IsSignInFailed = true;
return View("SignIn", sin);
}
// create claims
var claims = new List<Claim>
{
new Claim(ClaimTypes.Sid, sin.ShopToken),
new Claim(ClaimTypes.NameIdentifier, sin.ShopID.ToString()),
new Claim(ClaimTypes.Email, sin.EmailAddress.ToLower()),
new Claim(ClaimTypes.Role, "SamadhiShop")
};
// create identity
var identity = new ClaimsIdentity(claims, CookieAuthenticationDefaults.AuthenticationScheme);
// create principal
ClaimsPrincipal principal = new ClaimsPrincipal(new ClaimsIdentity(claims, CookieAuthenticationDefaults.AuthenticationScheme));
var authProperties = new AuthenticationProperties
{
IsPersistent = true // sin.RememberMe
};
// sign-in
await HttpContext.SignInAsync(scheme: CookieAuthenticationDefaults.AuthenticationScheme, principal: principal, properties: authProperties);
}
catch (Exception ex)
{
gFunc.ProcessError(ex);
}
return RedirectToAction("Console", new { date = DateTime.Today.ToString("d MMM yyyy"), timer = false });
}
[HttpGet]
[Authorize]
public async Task<ActionResult> Console(string date, bool timer)
{
int shop_id = int.Parse(User.FindFirst(ClaimTypes.NameIdentifier).Value);
string token = User.FindFirst(ClaimTypes.Sid).Value;
DateTime dt = DateTime.Parse(date);
Shop ss = await RepoSamadhi.GetShop(shop_id, token, dt);
ss.IsTimerOn = timer;
return View((_device.Type == DeviceType.Mobile) ? "ConsoleM" : "ConsoleM", ss);
}
The order of your middleware is wrong, the correct order should be
app.UseAuthentication();
app.UseAuthorization();