Claims Authentication ASP.NET vNext - asp.net-core

When adding a claim to the user, the claims information does not get recorded as a cookie on the page and the information gets lost for all other requests. Why does this happen?
public async Task<IActionResult> Login(LoginViewModel model, string returnUrl = null)
{
if (ModelState.IsValid)
{
try
{
var authReq = new AuthenticationViewModel() { password = model.Password, username = model.UserName };
var userInfo = await _dataService.AuthenticateAsync(authReq);
var claims = new List<Claim>();
claims.Add(new Claim("username", userInfo.user.userName));
claims.Add(new Claim("AddtionalData", userInfo.AddtionalData));
var user = ClaimsPrincipal.Current;
var identity = user.Identities.Where(x => x.AuthenticationType == "Custom").FirstOrDefault();
if (identity == null)
identity = new ClaimsIdentity(claims.ToArray(), "Custom");
user.AddIdentity(identity);
return RedirectToAction("Users", "Index");
}
catch (Exception)
{
ModelState.AddModelError("", "Invalid username or password.");
}
}
// If we got this far, something failed, redisplay form
return View(model);
}

I found the issue, I needed to add the following code:
context.Response.SignIn(new ClaimsIdentity(new[] { new Claim("name", "bob") }, CookieAuthenticationDefaults.AuthenticationType));
also, I changed the AuthenticationType to be CookieAuthenticationDefaults.AuthenticationType.
Please see link with Sample

Related

ArgumentNullException: Value cannot be null. (Parameter 'value')

I am having trouble with loging in from postman to my API. I get the roles back no problem but when they are used as parameters in the tokenservice they just stop working I get the following error :
Error
This is my endpoint :
[HttpPost("login")]
public async Task<IActionResult> Login(LoginUser loginData)
{
var userFromDb = await _userManager.FindByNameAsync(loginData.UserName);
if (userFromDb == null) return NotFound();
var result = await _signinManager.CheckPasswordSignInAsync(userFromDb, loginData.Password, false);
if (result == null) return BadRequest("Invalid Password");
var role = await _userManager.GetRolesAsync(userFromDb);
return Ok(
new
{
result = result,
username = userFromDb.UserName,
email = userFromDb.Email,
token = _tokenService.GenerateToken(userFromDb, role)
}
);
}
And this is the Token Service :
public class TokenService : ITokenService
{
private readonly IConfiguration _config;
public TokenService(IConfiguration config)
{
_config = config;
}
public string GenerateToken(IdentityUser user, IList<string> roles)
{
var claims = new List<Claim>
{
new Claim(JwtRegisteredClaimNames.GivenName, user.UserName),
new Claim(JwtRegisteredClaimNames.Email, user.Email)
};
if (roles != null)
{
foreach (var role in roles)
{
claims.Add(new Claim(ClaimTypes.Role, role));
}
}
var key = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(_config["Token:Key"]));
var creds = new SigningCredentials(key, SecurityAlgorithms.HmacSha256Signature);
var tokenDescriptor = new SecurityTokenDescriptor
{
Subject = new ClaimsIdentity(claims),
Expires = DateTime.Now.AddDays(7),
SigningCredentials = creds,
Issuer = _config["Token:Issuer"],
};
var tokenHandler = new JwtSecurityTokenHandler();
var token = tokenHandler.CreateToken(tokenDescriptor);
return tokenHandler.WriteToken(token);
I have tried to rewrite my code and espacially this part :
if (roles != null)
{
foreach (var role in roles)
{
claims.Add(new Claim(ClaimTypes.Role, role));
}
}
because when I put an entry point in the token service it tells me that role is null
I checked your code on my application, the code works well, check the following screenshot:
The issue might relates the userFromDb, role value or the configuration value, I suggest you could set some break point to check the userFromDb and role value in the Login method and set break point in the TokenService to check whether the token generated success, refer to the following image:
Update:
the LoginUser:
public class LoginUser
{
public string UserName { get; set; }
public string Password { get; set; }
public string EmailAddress { get; set; }
}
For the IdentityUser, I'm using the Asp.net core Identity default IdentityUser model. because in your code, the GenerateToken method also use the IdentityUser Model.
I think you it is because I am using my own user instead of
IdentityUser
You can check the GenerateToken method and check the userFromDb, might the issue relate it, in the GenerateToken method, you might need to change your code to use your custom user model, instead of the IdentityUser model.

How to return a cookie from an ASP.NET Core Auth Cookie in a Controller

I currently have a razor page where I return a cookie and it works great. However, I am developing a SPA which uses VueJS so I have created an API to directly communicate with. I have converted my code from the Razor Page to the controller but I am not sure how I actually return the cookie when the user tries to log in. If there is a match I want it to return the cookie and create the cookie. As of now I get a 400 code as if this request is not working. Any thoughts what could be wrong with this?
public class LoginController : Controller
{
private readonly LoginDBContext _context;
private string connectionString;
public IConfiguration Configuration { get; }
public LoginController(LoginDBContext context, IConfiguration configuration)
{
_context = context;
connectionString = configuration["ConnectionStrings:MMCARMSContext"];
}
// GET: HomeController
public ActionResult Index()
{
return Ok(new { Result = "Test" });
}
[HttpPost]
[ValidateAntiForgeryToken]
public async Task<IActionResult> Login([FromForm] string Username, [FromForm] string Password, [FromForm] bool RememberMe)
{
if (!String.IsNullOrEmpty(Username) && !String.IsNullOrEmpty(Password))
{
var users = _context.UsersAccountsTbl
.Where(a => a.Username == Username)
.Select(a => new { a.InternalUserNumber, a.Username, a.Password })
.ToArray();
if (users.Length == 1) //if we have more than 1 result we have security issue so do not allow login
{
var passwordHasher = new PasswordHasher<string>();
//To use you need to has with var hashedPassword = passwordHasher.HashPassword(UserName, Password);
//System.Diagnostics.Debug.WriteLine(passwordHasher.HashPassword("Username", "password"));
var user = users.First();
if (passwordHasher.VerifyHashedPassword(user.Username, user.Password, Password) == PasswordVerificationResult.Success)
{
var claims = new List<Claim>
{
new Claim(ClaimTypes.Name, user.InternalUserNumber.ToString())
};
var claimsIdentity = new ClaimsIdentity(claims, CookieAuthenticationDefaults.AuthenticationScheme);
if (RememberMe)
{
await HttpContext.SignInAsync(CookieAuthenticationDefaults.AuthenticationScheme,
new ClaimsPrincipal(claimsIdentity),
new AuthenticationProperties
{
IsPersistent = RememberMe,
ExpiresUtc = DateTimeOffset.UtcNow.AddHours(2)
});
}
else
{
await HttpContext.SignInAsync(CookieAuthenticationDefaults.AuthenticationScheme,
new ClaimsPrincipal(claimsIdentity));
}
return Ok(new { Result = "Cookie has been created!" });
}
else
{
return Ok(new { Result = "Password is incorrect!" });
}
}
return Ok(new { Result = "Username or Password does not exist!" });
}
else
{
return Ok(new { Result = "Username or Password invalid!" });
}
}
}
You could set the cookie in the HttpResponse, this way the cookie gets added when the client receives the response from your controller:
HttpCookie MyCookie = new HttpCookie("LastVisit");
DateTime now = DateTime.Now;
MyCookie.Value = now.ToString();
MyCookie.Expires = now.AddHours(1);
Response.Cookies.Add(MyCookie);
https://learn.microsoft.com/en-us/dotnet/api/system.web.httpresponse.cookies?view=netframework-4.8

The name "'_emailSender'" does not exist in the current context

I'm trying to learn authentication on razor pages
https://learn.microsoft.com/en-us/aspnet/core/security/authentication/identity?view=aspnetcore-5.0&tabs=visual-studio
And when I changed the Login page as they wrote in the example-
public async Task<IActionResult> OnPostAsync(string returnUrl = null)
{
returnUrl = returnUrl ?? Url.Content("~/");
ExternalLogins = (await _signInManager.GetExternalAuthenticationSchemesAsync())
.ToList();
if (ModelState.IsValid)
{
var user = new IdentityUser { UserName = Input.Email, Email = Input.Email };
var result = await _userManager.CreateAsync(user, Input.Password);
if (result.Succeeded)
{
_logger.LogInformation("User created a new account with password.");
var code = await _userManager.GenerateEmailConfirmationTokenAsync(user);
code = WebEncoders.Base64UrlEncode(Encoding.UTF8.GetBytes(code));
var callbackUrl = Url.Page(
"/Account/ConfirmEmail",
pageHandler: null,
values: new { area = "Identity", userId = user.Id, code = code },
protocol: Request.Scheme);
await _emailSender.SendEmailAsync(Input.Email, "Confirm your email",
$"Please confirm your account by <a href='{HtmlEncoder.Default.Encode(callbackUrl)}'>clicking here</a>.");
if (_userManager.Options.SignIn.RequireConfirmedAccount)
{
return RedirectToPage("RegisterConfirmation",
new { email = Input.Email });
}
else
{
await _signInManager.SignInAsync(user, isPersistent: false);
return LocalRedirect(returnUrl);
}
}
foreach (var error in result.Errors)
{
ModelState.AddModelError(string.Empty, error.Description);
}
}
// If we got this far, something failed, redisplay form
return Page();
}
I get the error-
The name "'_emailSender'" does not exist in the current context
And I searched later if there is a reference to the _emailSender class and did not see. Does anyone know how to fix this error?
Best regards
I should have added -
private readonly IEmailSender _emailSender;
Too bad they did not write it, it would have saved me a lot of time

Create roles Identity framework

I have a problem with creating a role for users. I follow the guide below. https://gooroo.io/GoorooTHINK/Article/17333/Custom-user-roles-and-rolebased-authorization-in-ASPNET-core/32835#.XOBFAcgzbcs
I added the "CreateRoles" method to "Startup.cs"
private async Task CreateRoles(IServiceProvider serviceProvider)
{
//adding custom roles
var RoleManager = serviceProvider.GetRequiredService<RoleManager<IdentityRole>>();
var UserManager = serviceProvider.GetRequiredService<UserManager<IdentityUser>>();
string[] roleNames = { "Admin", "Manager", "Member" };
IdentityResult roleResult;
foreach (var roleName in roleNames)
{
//creating the roles and seeding them to the database
var roleExist = await RoleManager.RoleExistsAsync(roleName);
if (!roleExist)
{
roleResult = await RoleManager.CreateAsync(new IdentityRole(roleName));
}
}
//creating a super user who could maintain the web app
var poweruser = new IdentityUser
{
UserName = Configuration.GetSection("UserSettings")["UserEmail"],
Email = Configuration.GetSection("UserSettings")["UserEmail"]
};
string UserPassword = Configuration.GetSection("UserSettings")["UserPassword"];
var _user = await UserManager.FindByEmailAsync(Configuration.GetSection("UserSettings")["UserEmail"]);
if (_user == null)
{
var createPowerUser = await UserManager.CreateAsync(poweruser, UserPassword);
if (createPowerUser.Succeeded)
{
//here we tie the new user to the "Admin" role
await UserManager.AddToRoleAsync(poweruser, "Admin");
}
}
}
I call it in "Configure":
CreateRoles(serviceProvider).Wait();
When running the application, an exception occurs
AggregateException: One or more errors occurred. (No service for type 'Microsoft.AspNetCore.Identity.RoleManager`1[Microsoft.AspNetCore.Identity.IdentityRole]' has been registered.)
How to fix this?
Append AddRoles to add Role services:
services.AddDefaultIdentity<IdentityUser>()
.AddRoles<IdentityRole>()
.AddDefaultUI(UIFramework.Bootstrap4)
.AddEntityFrameworkStores<ApplicationDbContext>();
Or replace AddDefaultIdentity method with the AddIdentity method in the ConfigureServices method
services.AddIdentity<IdentityUser,IdentityRole>()
.AddDefaultUI(UIFramework.Bootstrap4)
.AddEntityFrameworkStores<ApplicationDbContext>();

ASP.NET Core - Registering new Users

I'm adding the functionality of adding new users to my project following this project:
http://www.c-sharpcorner.com/article/asp-net-core-mvc-authentication-and-role-based-authorization-with-asp-net-core/
However, I encounter a problem when I try to Add a new user:
On the Post Method of the UserController:
[HttpPost]
public async Task<IActionResult> AddUser(UserViewModel model)
{
if (ModelState.IsValid)
{
ApplicationUser user = new ApplicationUser
{
Name = model.Name,
UserName = model.UserName,
Email = model.Email
};
IdentityResult result = await userManager.CreateAsync(user, model.Password);
if (result.Succeeded)
{
ApplicationRole applicationRole = await roleManager.FindByIdAsync(model.ApplicationRoleId);
if (applicationRole != null)
{
IdentityResult roleResult = await userManager.AddToRoleAsync(user, applicationRole.Name);
if (roleResult.Succeeded)
{
return RedirectToAction("Index");
}
}
}
}
return View(model);
I'm getting an error on this line:
IdentityResult result = await userManager.CreateAsync(user, model.Password);
if (result.Succeeded)
When I put a break here, I see that result is null.
I'm trying to solve it on my own but no luck so far.
Any ideas? Thanks in advance for any help.
Regards,