I created a web app from scratch using the default .Net template for MVC (ASP.NET Core web app) using Auth with "Individual Accounts" option and SDK 8, and my problem is I'm not able to authenticate a user, at least SignInManager.IsSignedIn(User)
always returns false.
Program.cs
:
var builder = WebApplication.CreateBuilder(args);
// Add services to the container.
var connectionString = builder.Configuration.GetConnectionString("DefaultConnection") ?? throw new InvalidOperationException("Connection string 'DefaultConnection' not found.");
builder.Services.AddDbContext<ApplicationDbContext>(options =>
options.UseSqlServer(connectionString));
builder.Services.AddDatabaseDeveloperPageExceptionFilter();
builder.Services.AddDefaultIdentity<IdentityUser>()
.AddEntityFrameworkStores<ApplicationDbContext>();
builder.Services.AddControllersWithViews();
var app = builder.Build();
// Configure the HTTP request pipeline.
if (app.Environment.IsDevelopment())
{
app.UseMigrationsEndPoint();
}
else
{
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.UseAuthentication();
app.MapControllerRoute(
name: "default",
pattern: "{controller=Home}/{action=Index}/{id?}");
app.MapRazorPages();
app.Run();
Login.cshtml.cs
:
public async Task<IActionResult> OnPostAsync(string returnUrl = null)
{
returnUrl ??= Url.Content("~/");
ExternalLogins = (await _signInManager.GetExternalAuthenticationSchemesAsync()).ToList();
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);
var result = SignInResult.Failed;
AWS_Manager aws = new AWS_Manager(TestController.access_id, TestController.secret_key);
UserModel oUser = await Task.Run(() => aws.getUser(Input.Email));
if (oUser != null)
{
if (oUser.Password == Crypto.GetSHA1(Input.Password)))
{
SetUser(Input.Email);
result = SignInResult.Success;
}
}
if (result.Succeeded)
{
_logger.LogInformation("User logged in.");
return LocalRedirect(returnUrl);
}
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();
}
SetUser
:
private void SetUser(string email)
{
var claims = new ClaimsIdentity(
new[] {
// adding following 2 claim just for supporting default antiforgery provider
new Claim(ClaimTypes.NameIdentifier, email),
new Claim("http://schemas.microsoft.com/accesscontrolservice/2010/07/claims/identityprovider",
"ASP.NET Identity", "http://www.w3.org/2001/XMLSchema#string"),
new Claim(ClaimTypes.Name, email),
// optionally you could add roles if any
// new Claim(ClaimTypes.Role, "RoleName"),
// new Claim(ClaimTypes.Role, "AnotherRole"),
}, IdentityConstants.ApplicationScheme);
var claim = new ClaimsPrincipal(claims);
HttpContext.SignInAsync(claim);
//This next line is for testing and returns true
var isLoggenIn = claim.Identities != null && claim.Identities.Any(i => i.AuthenticationType == IdentityConstants.ApplicationScheme); //=> Returns true
}
_LoginPartial.cshtml
:
@using Microsoft.AspNetCore.Identity
@inject SignInManager<IdentityUser> SignInManager
@inject UserManager<IdentityUser> UserManager
<ul class="navbar-nav">
@if (SignInManager.IsSignedIn(User)) //=> Always returns false
{
<li class="nav-item">
<a class="nav-link text-dark" asp-area="Identity" asp-page="/Account/Manage/Index" title="Manage">Hello @User.Identity?.Name!</a>
</li>
<li class="nav-item">
<form class="form-inline" asp-area="Identity" asp-page="/Account/Logout" asp-route-returnUrl="@Url.Action("Index", "Home", new { area = "" })">
<button type="submit" class="nav-link btn btn-link text-dark">Logout</button>
</form>
</li>
}
else
{
<li class="nav-item">
<a class="nav-link text-dark" asp-area="Identity" asp-page="/Account/Register">Register</a>
</li>
<li class="nav-item">
<a class="nav-link text-dark" asp-area="Identity" asp-page="/Account/Login">Login</a>
</li>
}
</ul>
As per all the threads / posts I've already read I believe my problem is in SetUser
method, and most precisely in setting the AuthenticationType
in the claims, but not completely sure, and I don't know how to do it.
What can I try next?
I'm quite sure I should add the AuthenticationType at the end of the
ClaimsIdentity constructor, like this (for example):
var claims = new ClaimsIdentity(
new[] {
// adding following 2 claim just for supporting default antiforgery provider
new Claim(ClaimTypes.NameIdentifier, email),
new Claim("http://schemas.microsoft.com/accesscontrolservice/2010/07/claims/identityprovider",
"ASP.NET Identity", "http://www.w3.org/2001/XMLSchema#string"),
new Claim(ClaimTypes.Name, email),
// optionally you could add roles if any
// new Claim(ClaimTypes.Role, "RoleName"),
// new Claim(ClaimTypes.Role, "AnotherRole"),
}, "UserAuthenticated");
But then I don't know where / how to specify the "UserAuthenticated" AuthenticationType.
I'll answer myself and paste the relevant parts of code.
Program.cs:
builder.Services.AddAuthentication(CookieAuthenticationDefaults.AuthenticationScheme)
.AddCookie(options =>
{
options.AccessDeniedPath = "/login";
options.LoginPath = "/";
});
Login.cshtml.cs
AWS_Manager aws = new AWS_Manager(TestController.access_id, TestController.secret_key);
UserModel oUser = await Task.Run(() => aws.getUser(Input.Email));
if (oUser != null)
{
if (oUser.Password == Crypto.GetSHA1(Input.Password)) // && oUser.testloader_enabled == true)
{
//SetUser(Input.Email);
//result = SignInResult.Success;
//A claim is a statement about a subject by an issuer and
//represent attributes of the subject that are useful in the context of authentication and authorization operations.
var claims = new List<Claim>() {
// adding following 2 claim just for supporting default antiforgery provider
new Claim(ClaimTypes.NameIdentifier, Input.Email),
new Claim("http://schemas.microsoft.com/accesscontrolservice/2010/07/claims/identityprovider",
"ASP.NET Identity", "http://www.w3.org/2001/XMLSchema#string"),
new Claim(ClaimTypes.Name, Input.Email),
// optionally you could add roles if any
// new Claim(ClaimTypes.Role, "RoleName"),
// new Claim(ClaimTypes.Role, "AnotherRole"),
};
//Initialize a new instance of the ClaimsIdentity with the claims and authentication scheme
var identity = new ClaimsIdentity(claims, CookieAuthenticationDefaults.AuthenticationScheme);
//Initialize a new instance of the ClaimsPrincipal with ClaimsIdentity
var principal = new ClaimsPrincipal(identity);
//SignInAsync is a Extension method for Sign in a principal for the specified scheme.
await HttpContext.SignInAsync(CookieAuthenticationDefaults.AuthenticationScheme, principal, new AuthenticationProperties()
{
IsPersistent = Input.RememberMe
});
return LocalRedirect(returnUrl);
}
}
_LoginPartial.cshtml:
@if (User.Identity != null && User.Identity.IsAuthenticated)
{
...
Now it's working.