I'm attempting to extend the ASP.NET Identity. I feel like I have most of the parts. The model and user objects all properly populate. However, when I check the database for the new record inserted via the CreateAsync function, the new fields are all NULL. What am I missing?
[HttpPost]
[AllowAnonymous]
[ValidateAntiForgeryToken]
public async Task<IActionResult> Register(RegisterViewModel model, string returnUrl = null)
{
ViewData["ReturnUrl"] = returnUrl;
if (ModelState.IsValid)
{
var user = new ApplicationUser { UserName = model.Email, Email = model.Email, FirstName = model.FirstName, LastName = model.LastName
, Organization = model.Organization };
var result = await _userManager.CreateAsync(user, model.Password);
if (result.Succeeded)
{
_logger.LogInformation("User created a new account with password.");
var code = await _userManager.GenerateEmailConfirmationTokenAsync(user);
var callbackUrl = Url.EmailConfirmationLink(user.Id.ToString(), code, Request.Scheme);
await _emailSender.SendEmailConfirmationAsync(model.Email, callbackUrl);
await _signInManager.SignInAsync(user, isPersistent: false);
_logger.LogInformation("User created a new account with password.");
return RedirectToLocal(returnUrl);
}
AddErrors(result);
}
// If we got this far, something failed, redisplay form
return View(model);
}
services.AddIdentity<ApplicationUser, IdentityRole>(options =>
{
// Signin settings
options.SignIn.RequireConfirmedEmail = false;
options.SignIn.RequireConfirmedPhoneNumber = false;
})
.AddEntityFrameworkStores<ApplicationDbContext>()
.AddDefaultTokenProviders();
public class ApplicationUser : IdentityUser
{
public String FirstName;
public String LastName;
public String Organization;
}
You need to use auto-implemented properties in your ApplicationUser class instead of just using public fields. That might be the problem.
public class ApplicationUser : IdentityUser
{
public String FirstName { get; set; }
public String LastName { get; set; }
public String Organization { get; set; }
}
Related
I am kinda new to this, so my apologizes in advance.
so after (result = await _userManager.UpdateAsync(user);
I get in the logging (DuplicateUserName), be cause the name already exist in another user, So I want to return the validation error to the blazor component and show it to the UI, same validation error that is in the logger
warn: Microsoft.AspNetCore.Identity.UserManager[13]
User validation failed: DuplicateUserName.
[HttpPost("EditSaveUser")]
public async Task<ActionResult>EditSaveUser(EditUserModel model)
{
var user = await _userManager.FindByIdAsync(model.Id);
if (user == null)
{
return BadRequest($"User with Id = {model.Id} cannot be found");
}
else
{
user.Email = model.Email;
user.UserName = model.UserName;
user.City = model.City;
var result = await _userManager.UpdateAsync(user);
if (result.Succeeded)
{
return Ok(result);
}
else
{
foreach (var error in result.Errors)
{
ModelState.AddModelError("", error.Description);
}
return BadRequest(ModelState);
}
}
}
this is the model that has the UserName that i want to apply the validation from the API to it
public class EditUserModel
{
public string Id { get; set; }
[Required]
public string UserName { get; set; } = string.Empty;
[Required]
public string Email { get; set; } = string.Empty;
public string City { get; set; } = string.Empty;
public List<string> Claims { get; set; } = new();
public IList<string> Roles { get; set; }
}
The model inherent from IdentityUser
public class ApplicationUser : IdentityUser
{
public string City { get; set; } = string.Empty;
}
here i get the response from the API Post
public async Task EditSaveUser(EditUserModel model)
{
var result = await _httpClient.PostAsJsonAsync($"https://localhost:7023/api/administration/EditSaveUser/", model);
if (result.IsSuccessStatusCode == false)
{
//TODO
}
}
here is the code on the blazor component
private EditUserModel model = new();
protected async override Task OnInitializedAsync()
{
var result = await AdminService.EditUser(Id);
if (result != null)
{
model = result;
}
}
private async Task HandleSubmit()
{
await AdminService.EditSaveUser(model);
}
ofc i use
<EditForm Model="model" OnValidSubmit="HandleSubmit">
<DataAnnotationsValidator/>
<ValidationSummary />
<ValidationMessage For="#(()=> model.Email)" />
in Program.cs
var connectionString = builder.Configuration.GetConnectionString("DefaultConnection");
builder.Services.AddDbContext<ApplicationDbContext>(options =>
options.UseSqlServer(connectionString));
builder.Services.AddDatabaseDeveloperPageExceptionFilter();
builder.Services.AddRazorPages();
builder.Services.AddServerSideBlazor();
builder.Services.AddScoped<AuthenticationStateProvider, RevalidatingIdentityAuthenticationStateProvider<ApplicationUser>>();
builder.Services.AddScoped<IAdminService,AdminService>();
builder.Services.AddScoped<IAuthService, AuthService>();
builder.Services.AddSingleton<WeatherForecastService>();
builder.Services.AddHttpClient();
builder.Services.AddControllers();
builder.Services.AddBlazoredLocalStorage();
builder.Services.AddAuthorizationCore();
builder.Services.AddScoped<AuthenticationStateProvider, ApiAuthenticationStateProvider>();
builder.Services.AddEndpointsApiExplorer();
builder.Services.AddDefaultIdentity<ApplicationUser>()
.AddRoles<IdentityRole>()
.AddEntityFrameworkStores<ApplicationDbContext>();
builder.Services.AddAuthentication(JwtBearerDefaults.AuthenticationScheme).AddJwtBearer(options =>
{
options.TokenValidationParameters = new TokenValidationParameters
{
ValidateIssuer = true,
ValidateAudience = true,
ValidateLifetime = true,
ValidateIssuerSigningKey = true,
ValidIssuer = builder.Configuration["JwtIssuer"],
ValidAudience = builder.Configuration["JwtAudience"],
IssuerSigningKey = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(builder.Configuration["JwtSecurityKey"]))
};
});
The model for ApplicationDbContext
public class ApplicationDbContext : IdentityDbContext<ApplicationUser>
{
public ApplicationDbContext(DbContextOptions<ApplicationDbContext> options)
: base(options)
{
}
protected override void OnModelCreating(ModelBuilder builder)
{
base.OnModelCreating(builder);
foreach (var foreignKey in builder.Model.GetEntityTypes().SelectMany(x=> x.GetForeignKeys()))
{
foreignKey.DeleteBehavior = DeleteBehavior.Restrict;
}
}
}
I was expecting the UI to get the validation error be cause I am using validation
I'm Working on a project that had authorization implemented with One user has One role.
Now we want to convert that relation to many to many but in the asp.net core authorization it went wrong.
[Serializable]
public class User
{
[Key]
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
[Required]
public Guid? Id { get; set; }
public virtual IList<UserRole> UserRoles { get; set; } = new List<UserRole>();
[NotMapped]
public string Token { get; set; }
/**/
[Serializable]
public class UserRole
{
public Guid UserId { get; set; }
public User User { get; set; }
public int RoleId { get; set; }
public Role Role { get; set; }
}
[Serializable]
public class Role
{
[Key]
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
[JsonIgnore]
public int Id { get; set; }
public string Name { get; set; }
}
}
while our database and mapping works perfect. the authorization in asp.net core fails.
autorization service:
public async Task<DTO_User> Authenticate(string username, string password)
{
var users = await _userRepo.GetAll();
var user = users.Where(u => u.Username == (username) && u.Password == (password)).FirstOrDefault();
if (user == null)
return null;
var tokenHandler = new JwtSecurityTokenHandler();
var key = Encoding.ASCII.GetBytes(_appSettings.Secret);
var tokenDescriptor = new SecurityTokenDescriptor
{
Expires = DateTime.UtcNow.AddDays(1),
SigningCredentials = new SigningCredentials(new SymmetricSecurityKey(key),
SecurityAlgorithms.HmacSha256Signature)
};
var claims = new List<Claim>
{
new Claim(ClaimTypes.NameIdentifier, user.Id.ToString()),
new Claim(ClaimTypes.Name, user.Username.ToString()),
};
var roles = await this._userRepo.GetUserRoles(user.Id.Value.ToString());
var claimsWithRoles = roles.ToList().Select(role => new Claim(ClaimTypes.Role, role.Name));
var allClaims = claims.Concat(claimsWithRoles);
tokenDescriptor.Subject = new ClaimsIdentity(allClaims);
var token = tokenHandler.CreateToken(tokenDescriptor);
user.Token = tokenHandler.WriteToken(token);
// remove password before returning
user.Password = null;
return _mapper.Map<DTO_User>(user);
}
**Controller**
[Route("api/[controller]")]
[ApiController]
[Authorize]
[EnableCors("CorsPolicy")]
public class SessionController : ControllerBase
{
[HttpGet]
[Route("active")]
public async Task<IActionResult> GetAllActive()
{
}
}
}
but where getting the exception:
I wants to create a new user with role. I'm using entity migration. so i get the all default tables. Without roles I'm able to register a new user but when i add the roles drop down I'm facing a error Cannot convert from string to ourproject.entitites.user
Please help and teach me where i did the mistake. I'm new to .net core technology.
I'm facing a problem with user.Id
await _userManager.AddToRoleAsync(user.Id, model.UserRoles);
ControllerCode:
[HttpPost, ValidateAntiForgeryToken]
public async Task<IActionResult> Register(RegisterUserModel model)
{
if (ModelState.IsValid)
{
var user = new User { UserName = model.Username, Email = model.Email,
PhoneNumber = model.PhoneNumber };
//var phoneNo = new User { PhoneNumber = model.PhoneNumber };
var createResult = await _userManager.CreateAsync(user, model.Password);
if (createResult.Succeeded)
{
await _userManager.AddToRoleAsync(user.Id, model.UserRoles);
return RedirectToAction("Details", "Home");
}
else
{
ViewBag.Name = new SelectList(_Context.Roles.Where(u => !u.Name.Contains("Admin"))
.ToList(), "Name", "Name");
foreach (var error in createResult.Errors)
{
ModelState.AddModelError("", error.Description);
}
}
}
return View();
}
Registermodel Code:
public class RegisterUserModel
{
[Required,MaxLength(256)]
public string Username { get; set; }
[Required, DataType(DataType.Password)]
public string Password { get; set; }
[Required, DataType(DataType.Password), Compare(nameof(Password))]
public string ConfirmPassword { get; set; }
[Required, DataType(DataType.EmailAddress),MaxLength(256)]
public string Email { get; set; }
[Required,DataType(DataType.PhoneNumber)]
public string PhoneNumber { get; set; }
[Display(Name ="UserRoles")]
public string UserRoles { get; set; }
}
User Entity Code:
using Microsoft.AspNetCore.Identity;
namespace OurProject.Entities
{
public class User : IdentityUser
{
}
}
Here is a functional code for .net core 2.0. In AddToRoleAsync method I am sending user object itself.
[AllowAnonymous]
[ValidateAntiForgeryToken]
public async Task<IActionResult> Register(RegisterViewModel model, string returnUrl = null)
{
ViewData["ReturnUrl"] = returnUrl;
if (ModelState.IsValid)
{
var user = new ApplicationUser { UserName = model.Email, Email = model.Email };
var result = await _userManager.CreateAsync(user, model.Password);
if (result.Succeeded)
{
string roleName = "Manager";
var isExistRole = await this._roleManager.RoleExistsAsync(roleName);
if (!isExistRole)
{
await this._roleManager.CreateAsync(
new IdentityRole
{
Id = Guid.NewGuid().ToString(),
Name = roleName
});
}
await _userManager.AddToRoleAsync(user, roleName);
return RedirectToLocal(returnUrl);
}
AddErrors(result);
}
// If we got this far, something failed, redisplay form
return View(model);
}
How can I add a related object to the database?
My IdentityUser:
public class ApplicationUser : IdentityUser
{
public ICollection<Todo> Todos { get; set; }
}
My Todo model:
public class Todo
{
public int Id { get; set; }
public string Name { get; set; }
public ApplicationUser User { get; set; }
}
I have a WebAPI like /api/todos.
[HttpPost]
public async Task<IActionResult> Post([FromBody]string name)
{
var user = await _userManager.GetUserAsync(User);
// _db is IdentityDbContext that I use
var newTodo = new Todo() { Name = "do the laundry" }; // Do I have to specify the user like User = user?
_db.Todos.Add(newTodo);
await _db.SaveChangesAsync();
return CreatedAt(...);
}
Question: Am I doing it right? Do I have to pass the user to the new Todo item?
Can I somehow add the todo like that?:
user.Todos.Add(newTodo);
But how would I save it?
Add the User:
var user = await _userManager.GetUserAsync(User);
var newTodo = new Todo() { Name = "do the laundry", User = user };
_db.Todos.Add(newTodo);
await _db.SaveChangesAsync();
or add to the user and update it:
var user = await _userManager.GetUserAsync(User);
var newTodo = new Todo() { Name = "do the laundry" };
user.Todos.Add(newTodo);
_db.Users.Update(user);
await _db.SaveChangesAsync();
Either way should be fine.
I have created web api 2 project which created a local db in which there was a table "AspNetUser". I have merged those tables with my own db. In my database I have a table "Employee Information" that will store all information of the employee except his Email,Password and UserName and all the other information will be stored in "Employee Information" table.
This is the pre-written code to register user:
[AllowAnonymous]
[Route("Register")]
public async Task<IHttpActionResult> Register(RegisterBindingModel model)
{
if (!ModelState.IsValid)
{
return BadRequest(ModelState);
}
var user = new ApplicationUser() { UserName = model.UserName, Email = model.Email};
IdentityResult result = await UserManager.CreateAsync(user, model.Password);
if (result.Succeeded)
{
var currentUser = UserManager.FindByName(user.UserName);
var roleresult = UserManager.AddToRole(currentUser.Id, model.SelectedRole);
}
else if (!result.Succeeded)
{
return GetErrorResult(result);
}
return Ok();
}
And This is my own logic to register User:
[ResponseType(typeof(EmployeeInformation))]
[ActionName("EmployeeInfo")]
[DeflateCompression]
public IHttpActionResult PostEmployeeInformation(EmployeeInformation EmployeeInfo)
{
if (!ModelState.IsValid)
{
return BadRequest(ModelState);
}
db.EmployeeInformations.Add(EmployeeInfo);
db.SaveChanges();
So how can I Store Email,password and Username in "AspNetUser" table and all the other information(name,fathername,dob etc) in "EmployeeInformation" table.
Thanks.
Simply add relation between ApplicationUser and EmployeeInformation entities:
public class ApplicationUser: IdentityUser
{
// other properties
public int EmployeeInformationID {get;set;}
public virtual EmployeeInformation EmployeeInformation {get;set;}
}
Do same for EmployeeInformation class
public class EmployeeInformation
{
// other properties
public string UserID {get;set;}
public virtual ApplicationUser User {get;set;}
}
Now you have seprate class for user's extra information for example you could write:
public IHttpActionResult PostEmployeeInformation(EmployeeInformation employeeInfo)
{
employeeInfo.UserID="your desired user id, current user id for example";
db.EmployeeInformations.Add(employeeInfo);
db.SaveChanges();
}