How can I the userId before an account has been created? - c#

I know the title is a bit of a oxymoron; however, I have a register form that also takes more details from the user such as first name, last name etc.
Inside the Register method of the account controller, I have added a Members object, populated it with the data, however, I need the GUID (UserId) of the account being registered to be stored as the "MemberId".
The user MUST have clicked register to get into this actionresult method but no userId is created until program flow has left.
How can I get the userId of the registered account as it returns null? Any help is appreciated. Sorry for the ambiguity.
public async Task<ActionResult> Register(RegisterForm model)
{
if (ModelState.IsValid)
{
var user = new ApplicationUser { UserName = model.RegisterViewModel.Email, Email = model.RegisterViewModel.Email };
var result = await UserManager.CreateAsync(user, model.RegisterViewModel.Password);
if (result.Succeeded)
{
await SignInManager.SignInAsync(user, isPersistent: false, rememberBrowser: false);
var members = new Members()
{
MemberId = new Guid(User.Identity.GetUserId()),
// Obviously the above returns null because there is no userId yet.
FirstName = model.Members.FirstName,
LastName = model.Members.LastName,
PhoneNumber = model.Members.PhoneNumber,
Role = model.Members.Role
};
_context.Members.Add(members);
_context.SaveChanges();
return RedirectToAction("Index", "Home");
}
AddErrors(result);
}
return View(model);
}

Assuming you are using EntityFramework, after calling UserManager.CreateAsync the property for user.Id will have been updated.
So you can use the user id like this:
MemberId = new Guid(user.Id)

Related

Setting a user detail in Login.cshtml.cs

Edit for clarity:
user.LastActiveDate in Login.cshtml.cs is successfully getting the data from the user (Screenshot showing this), but I am unable to actually change the data of LastActiveDate to current date.
Original Question:
I have set up AspNetCore.Identity.UI and have set up Login and Registration through it. As soon as the user logs in, I am trying to set up a detail LastActiveDate, which would contain DateTime.UtcNow, which should be set as soon as the user logs in.
What I've tried is setting this in the if(result.Succeeded), however this doesn't seem to be working. Any idea on what I might be doing wrong/idea on how I should be doing this?
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);
if (result.Succeeded)
{
//THIS NEEDS TO SET LastActiveDate IN User TO DateTime.UtcNow
var user = await _userManager.FindByEmailAsync(Input.Email);
user.LastActiveDate = DateTime.UtcNow;
_logger.LogInformation("User logged in.");
return LocalRedirect(returnUrl);
}
...
}
...
}
...
User.cs
namespace Nemesys.Models
{
public class User : IdentityUser
{
public DateTime LastActiveDate { get; set; }
...
}
}
Just in case, this is how I set up the new user during Registration in
Register.cshtml.cs, but I highly doubt there is any issue with this as it seems to be displaying the set DateTime.UtcNow just fine.
public async Task<IActionResult> OnPostAsync(string returnUrl = null)
{
returnUrl ??= Url.Content("~/");
ExternalLogins = (await _signInManager.GetExternalAuthenticationSchemesAsync()).ToList();
if (ModelState.IsValid)
{
var user = new User {
...
LastActiveDate = DateTime.UtcNow,
};
...
I am quite sure there is a quick way to do this but I'm quite new to C# and can't figure it out or find a related query on it online. Any help would be highly appreciated.
No. You must set user detail by write it to AspNetUsers table. You maybe need extend user entity (it is a popular practice), see https://www.freecodespot.com/blog/custom-identity-columns-in-aspdotnet-core/
For a specific case (in comment), record last log in time, you need extend AspNetUsers, by extend entity
public class ApplicationUser : IdentityUser
{
public virtual DateTime? LastLoginTime { get; set; }
public virtual DateTime? RegistrationDate { get; set; }
// other properties
}
See more at https://stackoverflow.com/a/24618153/3728901

Can't update SQL Bit column with ASP .NET Core

I have an ASP .NET Core web app, and when I created the project the authentication was set up automatically using the individual user accounts option. The database tables for user accounts/roles were also created automatically.
I created my own ApplicationUser class which inherits from IdentityUser, and I added parameters for FirstName, LastName and LockoutReason. (I added these fields to the DB table as well) Now I'm trying to add a feature to allow someone to manually lock out a user, and I can't seem to update the LockoutEnabled field in the database. Every time I do, it automatically gets reset to false.
Here is a picture of the form:
And the GET/POST code for it:
//GET Users lock
public async Task<IActionResult> Lock(string id)
{
if (id == null)
{
return NotFound();
}
var userFromDb = await _db.ApplicationUser.SingleOrDefaultAsync(m => m.Id == id);
if (userFromDb == null)
{
return NotFound();
}
else
{
return View(userFromDb);
}
}
//POST Users lock
[HttpPost]
[ValidateAntiForgeryToken]
public async Task<IActionResult> Lock(string Id, ApplicationUser model)
{
var userFromDb = await _db.Users.Where(u => u.Id == Id).FirstOrDefaultAsync();
userFromDb.LockoutEnd = model.LockoutEnd;
userFromDb.LockoutEnabled = true;
userFromDb.AccessFailedCount = model.AccessFailedCount;
userFromDb.LockoutReason = model.LockoutReason;
_db.ApplicationUser.Update(userFromDb);
await _db.SaveChangesAsync();
return RedirectToAction(nameof(Index));
}
ApplicationUser.cs
public class ApplicationUser : IdentityUser
{
public string FirstName { get; set; }
public string LastName { get; set; }
public string LockoutReason { get; set; }
}
When I click the Lock button, the LockoutEnd and LockoutReason fields are updated correctly, but the LockoutEnabled field remains false and the account isn't locked out.
New POST method after trying to implement solution:
//POST Users lock
[HttpPost]
[ValidateAntiForgeryToken]
public async Task<IActionResult> Lock(string Id, ApplicationUser model)
{
var userFromDb = await _db.Users.Where(u => u.Id == Id).FirstOrDefaultAsync();
userFromDb.LockoutEnd = model.LockoutEnd;
userFromDb.AccessFailedCount = model.AccessFailedCount;
userFromDb.LockoutReason = model.LockoutReason;
_db.ApplicationUser.Update(userFromDb);
await _db.SaveChangesAsync();
var user2 = await _userManager.FindByIdAsync(userFromDb.Id);
var result = await _userManager.SetLockoutEnabledAsync(user2, true);
return RedirectToAction(nameof(Index));
}
You need the combination of the two proprieties The LockoutEnabled and LockoutEnd.
The LockoutEnabled is an indicator to see if the user could be locked out or not, and the LockoutEnd is a DateTimeOffset. So in order to lock the user set the lockout to true and put the value of LockoutEnd in the future, because A value in the past means the user is not locked out.
you cannot achieve this by altering LockoutEnabled property.
Instead, you need to add custom property such as IsEnabled
On Register Action Modify Application user
var user = new ApplicationUser { UserName = model.Email, Email = model.Email, IsEnabled = true };
On PasswordSigninAsync add IsEnabled check
'
if (user.IsEnabled)
return base.PasswordSignInAsync(userName, password, rememberMe, shouldLockout);
return Task.FromResult<SignInStatus>(SignInStatus.LockedOut);
UserManager<ApplicationUser> _userManager
_userManager = userManager; //DI'd via CTOR for the class
it is never done with direct access with the table from the user table.
in your case you might need to grab the user with a call to
var user = await _userManager.FindByIdAsync(model.Id)
because you might not have enough information with the pass through from the post populated within the model variable.
//used from with in method
//model.LockoutEnabled will be either true or false depending on your selection
//from the view.
var result = await _userManager.SetLockoutEnabledAsync(user, model.LockoutEnabled);
this returns IdentityResult which has Errors collection Or bool Succeeded properties, can be handled as you like.

ASP.NET MVC 5 Identity - "A user name already exists" when using custom user

I am using ASP.NET MVC 5 to create a web app.
For the purposes of my app I have created a seprate "Website User" class that looks like this:
public class WebsiteUser
{
public WebsiteUser()
{
this.Comments = new List<CommentEntityModel>();
}
public int Id { get; set; }
public string Name { get; set; }
public virtual ApplicationUser IdentityUser { get; set; }
public ICollection<CommentEntityModel> Comments { get; set; }
}
My issue comes when I try and register a new user into the website.
My register action looks like this
public async Task<ActionResult> Register(RegisterViewModel model)
{
if (ModelState.IsValid)
{
var user = new ApplicationUser { UserName = model.Username, Email = model.Email };
var websiteUser = service.CreateWebsiteUser(user);
var result = await UserManager.CreateAsync(user, model.Password);
var websiteUserResult = service.RegisterWebsiteUser(websiteUser);
UserManager.AddToRole(user.Id, UserRoles.WebsiteUser.ToString());
if (result.Succeeded && websiteUserResult)
{
await SignInManager.SignInAsync(user, isPersistent:false, rememberBrowser:false);
return RedirectToAction("Index", "Home");
}
AddErrors(result);
}
// If we got this far, something failed, redisplay form
return View(model);
}
Pretty much the default one that Identity creates, except for these two lines:
var websiteUser = service.CreateWebsiteUser(user);
var websiteUserResult = service.RegisterWebsiteUser(websiteUser);
And what I have for them in the service is this :
CreateWebsiteUser()
public WebsiteUser CreateWebsiteUser(ApplicationUser appuser)
{
var user = new WebsiteUser
{
Name = appuser.UserName,
IdentityUser = appuser
};
return user;
}
RegisterWebsiteUser()
public bool RegisterWebsiteUser(WebsiteUser websiteUser)
{
try
{
this.context.WebsiteUsers.Add(new WebsiteUser {Name = websiteUser.Name, IdentityUser = websiteUser.IdentityUser});
this.context.SaveChanges();
return true;
}
catch (Exception e)
{
throw new Exception(e.Message);
}
}
And my issue is, that whenever I try to register a new user I get an exception on the this.context.SaveChanges() line, saying "A user name with the name |Name| already exists".
|Name| is basically the value of the UserName column in the ASPNetUsers Table.
I am really stuck and can't find a solution. (I can't even understand why an error like this would happen)
My DB structure seems okay to me so I guess the issue is somewhere else.
The only solution I can think of, is to completely remove my own User class and just add properties to the deafult ASP User.
Any help is greatly appreciated.
Solved it.
Leaving this here if anyone faces a similar problem.
Basically, due to some voodoo magic, that I do not understand, when you try to just add the just-created ApplicationUser to the IdenityUser's property of your custom User class, EF screws up, thinking you are using 2 data contexts.
The way to solve this is to instead use .Find(id) to find the ApplicationUser in the database, and then give that to the property.
So, instead of going
public bool RegisterWebsiteUser(ApplicationUser appUser)
{
this.context.YourUsers.Add(new YourUser {IdentityUser = appUser});
this.context.SaveChanges();
}
You do this:
public bool RegisterWebsiteUser(ApplicationUser applicationUser)
{
string id = applicationUser.Id;
var userFromDb = context.Users.Find(id);
this.context.YourUsers.Add(new YourUser {IdentityUser = userFromDB});
this.context.SaveChanges();
}

UpdateAsync method is not work in web api?

I am create small demo for user registration using web api mvc c#.i am register Succeeded for user using 'Register' in web api.now i want to using this method also edit this user by id so how can do that i don't know any one know please let me know. i my code i will manage add/edit call in one method so i will first check the condition on id is null then add and id is not null then go for the edit but how can edit this record and role.
here this my method :
[Route("Register")]
public async Task<IHttpActionResult> Register(RegisterBindingModel model)
{
try
{
if (model.Id == "")
{
//here is for add user method
var user = new ApplicationUser() { UserName = model.Email, Email = model.Email,PhoneNumber = model.PhoneNumber };
IdentityResult result = await UserManager.CreateAsync(user, model.Password);
if (result.Succeeded)
{
var UsersContext = new ApplicationDbContext();
var res = UsersContext.Users.Where(x => x.UserName == user.UserName).FirstOrDefault();
var UserId = res.Id;
await UserManager.AddToRoleAsync(UserId, model.UserRole);
return Ok(result);
}
return Ok(result);
}
else
{
//here i want write edit code
var UsersContext = new ApplicationDbContext();
var Team = UsersContext.Users.Find(model.Id);
Team.UserName = model.UserName;
Team.Email = model.Email;
Team.PhoneNumber = model.PhoneNumber;
IdentityResult result = await UserManager.UpdateAsync(Team); //here getting error.
return Ok(result);
}
return Ok("Done");
}
catch (Exception ex)
{
}
return Ok();
}
UPDATED:
First check whether you are passing values to all required field or not.
Keep this UserStore after your controller class calling braces {.
[Authorize]
public class AccountController : Controller
{
UserStore<IdentityUser> myUserStore = new UserStore<IdentityUser>(new
ApplicationDbContext());
//rest code
Then inside your Register() Post method, do like this bellow,
if (model.Id == "")
{
//Add new user code
}
else
{
var context = myUserStore.Context as ApplicationDbContext;
var Team = context.Users.Find(model.Id);
//change the field value what ever you want to update
context.Users.Attach(Team);
context.Entry(Team).State = EntityState.Modified;
context.Configuration.ValidateOnSaveEnabled = false;
await context.SaveChangesAsync();
return Ok("Updated");
}
Hope it helps :)

Is it possible to use Asp.net Identity with autoincremental key fields?

I am use my own EF database using long as the Id field. I have created all the entities and the relevant custom stores.
However when registering a new user, it is trying to call SetPasswordHashAsync before it has called CreateAsync, which means that it is trying to write to a database record that doesn't exist yet and of course it is throwing an error.
It looks like the system is expecting to know the Id field before it has been written to the DB but until then it cannot know the Id as it is set to be an auto-incremental field.
What am I missing? Is this even possible?
Here is the code from the AccountController (taken from the MVC project template)
[HttpPost]
[AllowAnonymous]
[ValidateAntiForgeryToken]
public async Task<ActionResult> Register(RegisterViewModel model)
{
if (ModelState.IsValid)
{
var user = new User { UserName = model.Email, Email = model.Email };
var result = await userManager.CreateAsync(user, model.Password);
if (result.Succeeded)
{
// We do not want the user to be logged in as soon as they register. We want them have to go through the login process.
//await SignInManager.SignInAsync(user, isPersistent:false, rememberBrowser:false);
// Send an email with this link
string code = await userManager.GenerateEmailConfirmationTokenAsync(user.Id);
var callbackUrl = Url.Action("ConfirmEmail", "Account", new { userId = user.Id, code = code }, protocol: Request.Url.Scheme);
await userManager.SendEmailAsync(user.Id, "Confirm your account", "Please confirm your account by clicking here");
return View("DisplayEmail");
}
AddErrors(result);
}
// If we got this far, something failed, redisplay form
return View(model);
}
and here is the code from UserManager (from Identity.core) that is called
public virtual async Task<IdentityResult> CreateAsync(TUser user, string password)
{
ThrowIfDisposed();
var passwordStore = GetPasswordStore();
if (user == null)
{
throw new ArgumentNullException("user");
}
if (password == null)
{
throw new ArgumentNullException("password");
}
var result = await UpdatePasswordInternal(passwordStore, user, password).WithCurrentCulture();
if (!result.Succeeded)
{
return result;
}
return await CreateAsync(user).WithCurrentCulture();
}
So you can see that updating the password before calling createUser is the intended process, but how is that meant to work? That just seems wrong to me.

Categories