I'm trying to get the AspNetCore Authorization to work with a MongoDB as store. The application is based on Razorpages and at the moment I'm strugling on getting a simple redirect to work.
Im using the AspNetCore.Identity.MongoDbCore adapter.
the login seems to be successfull but the redirect doesn't work. does any one know where Im going wrong here?
'XXXX' is a name i don't want to share with the internet :) just to clarify
this is the output I'm getting:
info: Microsoft.AspNetCore.Hosting.Diagnostics[1]
Request starting HTTP/1.1 POST http://localhost:5001/prio/Account/Login?ReturnUrl=%2Fprio%2F application/x-www-form-urlencoded 255
info: Microsoft.AspNetCore.Routing.EndpointMiddleware[0]
Executing endpoint '/Account/Login'
info: Microsoft.AspNetCore.Mvc.RazorPages.Infrastructure.PageActionInvoker[3]
Route matched with {page = "/Account/Login"}. Executing page /Account/Login
info: Microsoft.AspNetCore.Mvc.RazorPages.Infrastructure.PageActionInvoker[101]
Executing handler method XXXX_Prio.Pages.Account.LoginModel.OnPostAsync - ModelState is Valid
info: Microsoft.AspNetCore.Authentication.Cookies.CookieAuthenticationHandler[10]
AuthenticationScheme: Identity.Application signed in.
info: XXXX_Prio.Pages.Account.LoginModel[0]
User logged in.
info: Microsoft.AspNetCore.Mvc.RazorPages.Infrastructure.PageActionInvoker[102]
Executed handler method OnPostAsync, returned result Microsoft.AspNetCore.Mvc.RedirectToPageResult.
info: Microsoft.AspNetCore.Mvc.RedirectToRouteResult[1]
Executing RedirectToPageResult, redirecting to ../PriorityView.
info: Microsoft.AspNetCore.Mvc.RazorPages.Infrastructure.PageActionInvoker[4]
Executed page /Account/Login in 164.33620000000002ms
info: Microsoft.AspNetCore.Routing.EndpointMiddleware[1]
Executed endpoint '/Account/Login'
info: Microsoft.AspNetCore.Hosting.Diagnostics[2]
Request finished in 184.56660000000002ms 302
info: Microsoft.AspNetCore.Hosting.Diagnostics[1]
Request starting HTTP/1.1 GET http://localhost:5001/prio/PriorityView
info: Microsoft.AspNetCore.Authorization.DefaultAuthorizationService[2]
Authorization failed.
info: Microsoft.AspNetCore.Authentication.Cookies.CookieAuthenticationHandler[12]
AuthenticationScheme: Identity.Application was challenged.
info: Microsoft.AspNetCore.Hosting.Diagnostics[2]
Request finished in 5.4919ms 302
These are my files:
Startup.cs:
using System;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;
using AspNetCore.Identity.MongoDbCore.Extensions;
using AspNetCore.Identity.MongoDbCore.Infrastructure;
using XXXX_Prio.Models;
using Microsoft.AspNetCore.Identity;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc.Authorization;
namespace XXXX_Prio
{
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.AddRazorPages();
var mongoDbIdentityConfiguration = new MongoDbIdentityConfiguration
{
MongoDbSettings = new MongoDbSettings
{
ConnectionString = "mongodb://localhost:27017",
DatabaseName = "XXXXPrioWithAuth"
},
IdentityOptionsAction = options =>
{
options.Password.RequireDigit = false;
options.Password.RequiredLength = 5;
options.Password.RequireNonAlphanumeric = false;
options.Password.RequireUppercase = false;
options.Password.RequireLowercase = false;
// Lockout settings
options.Lockout.DefaultLockoutTimeSpan = TimeSpan.FromMinutes(30);
options.Lockout.MaxFailedAccessAttempts = 10;
// ApplicationUser settings
options.User.RequireUniqueEmail = true;
options.User.AllowedUserNameCharacters = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789@.-_";
}
};
services.ConfigureMongoDbIdentity<ApplicationUser, ApplicationRole, Guid>(mongoDbIdentityConfiguration);
services.AddMvc(config =>
{
var policy = new AuthorizationPolicyBuilder()
.RequireAuthenticatedUser()
.Build();
config.Filters.Add(new AuthorizeFilter(policy));
});
var sprov = services.BuildServiceProvider();
InitRoleUserSetup(sprov).Wait();
}
//This method creates the standard roles and master user
public async Task InitRoleUserSetup(IServiceProvider serviceProvider)
{
//var userManager = serviceProvider.GetService<UserManager<IdentityUser>>();
var userManager = serviceProvider.GetService<UserManager<ApplicationUser>>();
var roleManager = serviceProvider.GetService<RoleManager<ApplicationRole>>();
var roleCheck = await roleManager.RoleExistsAsync("Admin");
if (!roleCheck)
{
//create the roles and send them to the database
await roleManager.CreateAsync(new ApplicationRole("Admin"));
}
roleCheck = await roleManager.RoleExistsAsync("Customer");
if (!roleCheck) { await roleManager.CreateAsync(new ApplicationRole("Customer")); }
roleCheck = await roleManager.RoleExistsAsync("Manager");
if (!roleCheck) { await roleManager.CreateAsync(new ApplicationRole("Manager")); }
roleCheck = await roleManager.RoleExistsAsync("Regular");
if (!roleCheck) { await roleManager.CreateAsync(new ApplicationRole("Regular")); }
string userName = "admin@XXXX.ch";
string userPWD = "admin";
var user = new ApplicationUser { UserName = userName, Email = userName };
var result = await userManager.CreateAsync(user, userPWD);
//Add default User to Role Admin
await userManager.AddToRoleAsync(user, "Admin");
}
// 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();
}
else
{
app.UseExceptionHandler("/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.UseEndpoints(endpoints =>
{
endpoints.MapRazorPages();
});
}
}
}
Login.cshtml.cs:
using System;
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;
using System.Linq;
using System.Threading.Tasks;
using XXXX_Prio.Models;
using Microsoft.AspNetCore.Authentication;
using Microsoft.AspNetCore.Identity;
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc.RazorPages;
using Microsoft.Extensions.Logging;
using Microsoft.AspNetCore.Authorization;
namespace XXXX_Prio.Pages.Account
{
[AllowAnonymous]
public class LoginModel : PageModel
{
private readonly SignInManager<ApplicationUser> _signInManager;
private readonly ILogger<LoginModel> _logger;
public LoginModel(SignInManager<ApplicationUser> signInManager, ILogger<LoginModel> logger)
{
_signInManager = signInManager;
_logger = logger;
}
[BindProperty]
public InputModel Input { get; set; }
public IList<AuthenticationScheme> ExternalLogins { get; set; }
public string ReturnUrl { get; set; }
[TempData]
public string ErrorMessage { get; set; }
public class InputModel
{
[Required]
[EmailAddress]
public string Email { get; set; }
[Required]
[DataType(DataType.Password)]
public string Password { get; set; }
[Display(Name = "Remember me?")]
public bool RememberMe { get; set; }
}
public async Task OnGetAsync(string returnUrl = null)
{
if (!string.IsNullOrEmpty(ErrorMessage))
{
ModelState.AddModelError(string.Empty, ErrorMessage);
}
// Clear the existing external cookie to ensure a clean login process
await HttpContext.SignOutAsync(IdentityConstants.ExternalScheme);
ExternalLogins = (await _signInManager.GetExternalAuthenticationSchemesAsync()).ToList();
ReturnUrl = returnUrl;
}
public async Task<IActionResult> OnPostAsync(string returnUrl = null)
{
ReturnUrl = returnUrl;
if (ModelState.IsValid)
{
// This doesn't count login failures towards account lockout
// To enable password failures to trigger account lockout, set lockoutOnFailure: true
var result = await _signInManager.PasswordSignInAsync(Input.Email, Input.Password, Input.RememberMe, lockoutOnFailure: false);
if (result.Succeeded)
{
_logger.LogInformation("User logged in.");
return RedirectToPage("../PriorityView");
}
if (result.RequiresTwoFactor)
{
return RedirectToPage("./LoginWith2fa", new { ReturnUrl = returnUrl, RememberMe = Input.RememberMe });
}
if (result.IsLockedOut)
{
_logger.LogWarning("User account locked out.");
return RedirectToPage("./Lockout");
}
else
{
ModelState.AddModelError(string.Empty, "Invalid login attempt.");
return Page();
}
}
// If we got this far, something failed, redisplay form
return Page();
}
}
}
login.cshtml:
@page
@model XXXX_Prio.Pages.Account.LoginModel
@{
ViewData["Title"] = "Login";
}
<div class="row">
<div class="col-md-4">
<section>
<form method="post">
<h4>@ViewData["Title"]</h4>
<hr />
<div asp-validation-summary="All" class="text-danger"></div>
<div class="form-group">
<label asp-for="Input.Email"></label>
<input asp-for="Input.Email" class="form-control" />
<span asp-validation-for="Input.Email" class="text-danger"></span>
</div>
<div class="form-group">
<label asp-for="Input.Password"></label>
<input asp-for="Input.Password" class="form-control" />
<span asp-validation-for="Input.Password" class="text-danger"></span>
</div>
<div class="form-group">
<div class="checkbox">
<label asp-for="Input.RememberMe">
<input asp-for="Input.RememberMe" />
@Html.DisplayNameFor(m => m.Input.RememberMe)
</label>
</div>
</div>
<div class="form-group">
<button type="submit" class="btn btn-default">Log in</button>
</div>
</form>
</section>
</div>
<div class="col-md-6 col-md-offset-2">
<section>
<img src="~/images/Logo_IKAVA_CYMK_box.png" alt="IKAVA Logo" class="img-responsive" style="margin-top: 20px;" />
</section>
</div>
</div>
@section Scripts {
@await Html.PartialAsync("_ValidationScriptsPartial")
}
PriorityView.cshtml.cs:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc.RazorPages;
namespace AMAG_Prio.Pages
{
[Authorize(Roles = "Admin")]
public class PriorityViewModel : PageModel
{
public void OnGet()
{
}
}
}