How can I change UserManager logic so that user must exist in database before they register - asp.net-mvc-5

I am customizing the MVC5 registration process so that when users are registering they must enter two custom fields 'MyNewField1' and 'MyNewField2' which will be then checked against the user context to ensure if they exist in which case the registration can succeed by updating that current user.
public async Task<ActionResult> CustomRegister(CustomRegisterViewModel model)
{
if (ModelState.IsValid)
{
var context = new ApplicationDbContext();
ApplicationUser user = context.Users.Where(a => a.MyNewField1== model.MyNewField1& a.MyNewField2== a.MyNewField2).SingleOrDefault();
if(user != null)
{
var emailCheck = await UserManager.FindByNameAsync(model.Email);
if (emailCheck == null)
{
//We have found a user and email address has not been already assigned to another
//assign the email entered for this user in place of the username and email place
//holders and update the user before saving to the database
user.UserName = model.Email;
user.Email = model.Email;
var hasher = new PasswordHasher();
user.PasswordHash = hasher.HashPassword(model.Password);
context.SaveChanges();
var 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, "Budget Energy Email Verification", "Please confirm your account by clicking this link: link");
ViewBag.Link = callbackUrl;
ViewBag.Message = "Check your email and confirm your account, you must be confirmed before you can log in.";
return View("Info");
}
else
{
//This email address is already assigned to a user
return View(model);
}
}
else
{
//No user exists with these details so redisplay form
return View(model);
}
}
}
This method is passing off successfully and I am being informed that an email has been sent however when I click on this email link I am taken to an error page with the error being Invalid Token. Because I have changed the logic here do I have to create a token in a different manner?

I was able to solve this as follows:
[HttpPost]
[AllowAnonymous]
[ValidateAntiForgeryToken]
public async Task<ActionResult> BillpayRegister(BillpayRegisterViewModel model)
{
if (ModelState.IsValid)
{
var context = new ApplicationDbContext();
ApplicationUser customer = context.Users.Where(a => a.MyNewField1 == model.MyNewField1 & a.MyNewField2 == model.MyNewField2).SingleOrDefault();
if(customer != null)
{
var emailCheck = await UserManager.FindByNameAsync(model.Email);
if (emailCheck == null)
{
//We have found a user and email address has not been already assigned to another
//assign the email entered for this user in place of the username and email place
//holders and update the user before saving to the database
var user = UserManager.FindById(customer.Id);
user.UserName = model.Email;
UserManager.SetEmail(user.Id, model.Email);
string hashedNewPassword = UserManager.PasswordHasher.HashPassword(model.Password);
user.PasswordHash = hashedNewPassword;
var result = UserManager.Update(user);
if (result.Succeeded)
{
var 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, "Email Verification", "Please confirm your account by clicking this link: link");
ViewBag.Link = callbackUrl;
ViewBag.Message = "Check your email and confirm your account, you must be confirmed before you can log in.";
return View("Info");
}
}
else
{
//This email address is already assigned to a user
return View(model);
}
}
else
{
//No user exists with these details so redisplay form
return View(model);
}
}
// If we got this far, something failed, redisplay form
return View(model);
}

Related

What is the proper way to update a particular column in nestjs

I want to save token generated into the user's confirmed email column. The token is part of the confirmation link that will be sent to the user so that when the user clicks on the link I can check if it matches, then updates it to "activated".
Now the problem is I can't figure out how to save it in the ConfirmEmailLink method .
async register(createDTO: CreateUserDto) {
const { email } = createDTO;
const user = await this.userModel.findOne({ email })
if (user) {
throw new HttpException('User already exists', HttpStatus.BAD_REQUEST);
}
const createdUser = new this.userModel(createDTO);
var newUser = await createdUser.save();
await SendEmail(createDTO.email, await **this.ConfirmEmailLink(createdUser._id)**, createDTO.email);
return this.sanitizeUser(createdUser);
//return null;
}
In the above code there is ConfirmEmailLink that is a parameter to SendEmail method
async ConfirmEmailLink(userId: string) {
const id = v4();
var payload = { userId: userId };
var secret = process.env.JWT_SIMPLE_TOKEN;
var token = jwt.encode(payload, secret);
console.log("This is uuid", userId);
var link = `${process.env.HOST}/user/confirm/${token}/${id}`;
let user = await this.userModel.findById(userId);
if (!user) {
throw new HttpException("Registration not complete, try again or contact admin", HttpStatus.NOT_FOUND);
}
**//This is where the problem is, I want to save the token in ConfirmEmail column**
await this.userModel.updateOne({confirmEmail: token});
return link;
}
I will appreciate your suggestions or if there is a better way to do this
Thanks
updateOne needs 2 parameters, a filter to identify which document to modify, and a update indicating what to do.
.updateOnde({"_id":userId},{"$set":{"confirmEmail": token}})

Microsoft Bot V4 Authentication

I am using a login dialog with below code. How do I know if user is authenticated or not ?
namespace Sample.Dialogs
{
public class LoginDialog : ComponentDialog
{
private readonly BotStateService _botStateService;
public LoginDialog(string dialogId, BotStateService botStateService) : base(dialogId)
{
_botStateService = botStateService ?? throw new
System.ArgumentNullException(nameof(botStateService));
InitializeWaterfallDialog();
}
private void InitializeWaterfallDialog()
{
AddDialog(new OAuthPrompt(
nameof(OAuthPrompt),
new OAuthPromptSettings
{
ConnectionName = "",
Text = "Please login",
Title = "Login",
Timeout = 300000, // User has 5 minutes to login
}));
AddDialog(new TextPrompt(nameof(TextPrompt)));
AddDialog(new WaterfallDialog(nameof(WaterfallDialog), new WaterfallStep[]
{
PromptStepAsync,
LoginStepAsync
}));
InitialDialogId = nameof(WaterfallDialog); //InitialDialogId = $"
{nameof(LoginDialog)}.mainFlow";
}
private async Task<DialogTurnResult> PromptStepAsync(WaterfallStepContext stepContext, CancellationToken cancellationToken)
{
var tokenResponse = (TokenResponse)stepContext.Result;
if (tokenResponse != null)
{
return await stepContext.EndDialogAsync();
}
else
{
return await stepContext.BeginDialogAsync(nameof(OAuthPrompt), null , cancellationToken);
}
}
private async Task<DialogTurnResult> LoginStepAsync(WaterfallStepContext stepContext, CancellationToken cancellationToken)
{
var tokenResponse = (TokenResponse)stepContext.Result;
// if token exists
if (tokenResponse != null)
{
// welcome user
await stepContext.Context.SendActivityAsync(MessageFactory.Text("You are now logged
in."),cancellationToken);
// Display the name
await OAuthHelpers.GetUserDetail(stepContext.Context, tokenResponse);
return await stepContext.CancelAllDialogsAsync();
}
await stepContext.Context.SendActivityAsync(MessageFactory.Text("Login was not successful
please try again."), cancellationToken);
return await stepContext.EndDialogAsync();
}
}
}
Now, even after successful login user have to type something to know if they logged in or not. However, we want once the login is (Azure AD) successful message should push to user from bot saying that your name is this and you have successfully logged in.
How do we achieve this. ?
Thanks,

MVC 5 create user using Identity 2 in synchronous way

I'm trying to create new user and add him to selected role. I want to do it sync way, but always get stacked at point of creating in part ModelState. If I make it without ModelState then method will stack at point adminresult where is user created with UserManager. It looks like there is a problem with adding user to selected role, but I'm not sure. I'm using Identity 2. Is it possible to create user sync way or is it totally async process?
[HttpPost]
public ActionResult Create(User userViewModel, params string[] selectedRoles)
{
if (ModelState.IsValid)
{
var user = new User()
{
UserName = userViewModel.UserName,
Email = userViewModel.Email,
FirstName = userViewModel.FirstName,
LastName = userViewModel.LastName,
Password = userViewModel.Password
};
var adminresult = UserManager.Create(user, userViewModel.Password); // without using ModelState stack here
var roleStore = new RoleStore<IdentityRole>(context);
var roleManager = new RoleManager<IdentityRole>(roleStore);
var userStore = new UserStore<User>(context);
var userManager = new UserManager<User>(userStore);
var result = userManager.AddToRoles(user.Id, selectedRoles);
if (adminresult.Succeeded)
{
if (selectedRoles != null)
{
if (!result.Succeeded)
{
ModelState.AddModelError("", result.Errors.First());
// gets all names of roles to list
ViewBag.RoleId = new SelectList(RoleManager.Roles.ToList(), "Name", "Name");
return View("_Create");
}
}
return RedirectToAction("UserWizardIndex");
}
return View("_Create");
}
I would recommend you to use group based roles instead of giving multiple roles at login.
Your a sync will work fine in this way if single role is at login..
one thing that I saw in your code is if you have selected multiple
roles why there isn't any way to add every role against that user...
// POST: /Account/Register
[HttpPost]
[AllowAnonymous]
[ValidateAntiForgeryToken]
public async Task<ActionResult> Register(RegisterViewModel model)
{
if (ModelState.IsValid)
{
var roleManager = new RoleManager<IdentityRole>
(
new RoleStore<IdentityRole>
(
new ApplicationDbContext()
)
);
if (!roleManager.RoleExists(model.RoleName))
{
roleManager.Create(new IdentityRole(model.RoleName));
}
var user = new ApplicationUser { UserName = model.Email, Email = model.Email };
var role = Db.GetUserRoleId(model.RoleName);
var strRole = role.FirstOrDefault().ToList();
var result = await UserManager.CreateAsync(user, model.Password);
if (result.Succeeded)
{
var userInfo = new UserInformation
{
FirstName = model.FirstName,
LastName = model.LastName,
GUID = user.Id,
RoleId = model.RoleId
};
Db.UserInformations.Add(userInfo);
UserManager.AddToRoles(userInfo.GUID, model.RoleName);
await UserManager.UpdateSecurityStampAsync(user.Id);
Db.SaveChanges();
await SignInManager.SignInAsync(user, isPersistent: false, rememberBrowser: false);
// For more information on how to enable account confirmation and password reset please visit http://go.microsoft.com/fwlink/?LinkID=320771
// 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 RedirectToAction("Index", "Home");
}
AddErrors(result);
}
// If we got this far, something failed, redisplay form
return View(model);
}

Can`t login with seeded database configuration using asp.net identity

I have a problem with login, even though i see the tables are filled with my seeded user info from Configuration.cs:
protected override void Seed(www.Models.ApplicationDbContext context)
{
if (!context.Roles.Any(x => x.Name == "admin"))
{
var roleStore = new RoleStore<IdentityRole>(context);
var roleManager = new RoleManager<IdentityRole>(roleStore);
var role = new IdentityRole { Name = "admin" };
roleManager.Create(role);
}
if (!context.Users.Any(x => x.UserName == "admin" && x.Email == "admin#admin.com"))
{
var userStore = new UserStore<ApplicationUser>(context);
var userManager = new UserManager<ApplicationUser>(userStore);
var user = new ApplicationUser { UserName = "admin", Email = "admin#admin.com" };
var hasher = new PasswordHasher();
userManager.Create(user, "MySecret5");
userManager.AddToRole(user.Id, "admin");
}
}
and when i try to login i get error "Invalid login attempt".
What am i missing?
EDIT:
Im in the process of learning all stuff about asp.net so im pretty big noob now :( so i found this example to be working for me, and if anyone else needs it here it is:
protected override void Seed(www.Models.ApplicationDbContext context)
{
var userStore = new UserStore<ApplicationUser>(context);
var userManager = new UserManager<ApplicationUser>(userStore);
if (!context.Users.Any(x => x.UserName == "admin#v.com"))
{
var user = new ApplicationUser { UserName = "admin#v.com", Email = "admin#v.com" };
userManager.Create(user, "Password5%");
context.Roles.AddOrUpdate(x => x.Name, new IdentityRole { Name = "admin" });
context.SaveChanges();
userManager.AddToRole(user.Id, "admin");
}
}
And thanks for all your help and time.
It seems that the problem with logging in with seeded users is associated with MVC inaccuracy, look at:
var result = await SignInManager.PasswordSignInAsync(model.Emali, model.Password, model.RememberMe, shouldLockout: false);
if we seed this user:
var user = new ApplicationUser { UserName = "SomeName", Email = "admin#v.com" };
result will be == false, but if we change model.Emali in result to model.UserName then login end with success - of course now when we log in, we must give the UserName e.g:
UserName: SomeName;
Passwort: Password5%;
This worked flawlessly for me.
protected override void Seed(www.Models.ApplicationDbContext context)
{
var userStore = new UserStore<ApplicationUser>(context);
var userManager = new UserManager<ApplicationUser>(userStore);
if (!context.Users.Any(x => x.UserName == "admin#v.com"))
{
var user = new ApplicationUser { UserName = "admin#v.com", Email = "admin#v.com" };
userManager.Create(user, "Password5%");
context.Roles.AddOrUpdate(x => x.Name, new IdentityRole { Name = "admin" });
context.SaveChanges();
userManager.AddToRole(user.Id, "admin");
}
}

How to retrive profile photo,email from facebook graph version V 2.4 in asp.net MVC 5

i need to facebook profile photo and email in purpose of asp.mvc 5 web application. but facebook graph api version 2.4 email permission accessed. but .net mvc 5 code email value is null.and profile photo.
startup.Auth
var facebookAuthenticationOptions = new FacebookAuthenticationOptions()
{
AppId = "1655122338065609",
AppSecret = "5e858655ce82fad864e3991ae00db87a"
};
facebookAuthenticationOptions.Scope.Add("email");
app.UseFacebookAuthentication(facebookAuthenticationOptions);
ExternalLoginCallback
public async Task<ActionResult> ExternalLoginCallback(string returnUrl)
{
var loginInfo = await AuthenticationManager.GetExternalLoginInfoAsync();
if (loginInfo == null)
{
return RedirectToAction("Login");
}
var externalIdentity = HttpContext.GetOwinContext().Authentication.GetExternalIdentityAsync(DefaultAuthenticationTypes.ExternalCookie);
var emailClaim = externalIdentity.Result.Claims.FirstOrDefault(c => c.Type == ClaimTypes.Email);
var email = emailClaim.Value;
// Sign in the user with this external login provider if the user already has a login
var result = await SignInManager.ExternalSignInAsync(loginInfo, isPersistent: false);
switch (result)
{
case SignInStatus.Success:
return RedirectToLocal(returnUrl);
case SignInStatus.LockedOut:
return View("Lockout");
case SignInStatus.RequiresVerification:
return RedirectToAction("SendCode", new { ReturnUrl = returnUrl, RememberMe = false });
case SignInStatus.Failure:
default:
// If the user does not have an account, then prompt the user to create an account
ViewBag.ReturnUrl = returnUrl;
ViewBag.LoginProvider = loginInfo.Login.LoginProvider;
return View("ExternalLoginConfirmation", new ExternalLoginConfirmationViewModel { UserName=loginInfo.DefaultUserName,Email=loginInfo.Email });
}
}
externallogoncallbacck method var emailcliam value is null return from startup.auth ???

Resources