[HttpPost]
public ActionResult Register(User user)
{
if (ModelState.IsValid)
{
UserAPIController uApi = new UserAPIController(true);
HttpResponseMessage response = uApi.Register(user, Request.QueryString["TeamId"]);
if (response.StatusCode == System.Net.HttpStatusCode.Conflict)
{
ModelState.AddModelError("", HttpContext.GetGlobalResourceObject("LanguageResource", "DuplicateEmailErrorMessage").ToString());
return View();
}
//Send Registration Email
string EmailBodyStr = string.Empty;
string EmailFrom = Helpers.CommonFunctions.GetApplicationSettingValue("SystemEmailId");
string EmailSub = HttpContext.GetGlobalResourceObject("LanguageResource", "EmailTemplate_Reistration_Subject").ToString();
string userName = user.FullName;
if (string.IsNullOrEmpty(userName))
{
userName = user.Email;
}
EmailBodyStr = HttpContext.GetGlobalResourceObject("LanguageResource", "EmailTemplate_Registration_TeamLeader_Body").ToString();
EmailBodyStr = EmailBodyStr.Replace("[UserFullName]", userName);
string email = HttpUtility.UrlEncode(Helpers.Encrypt.EncryptString(user.Email));
EmailBodyStr = EmailBodyStr.Replace("[NavigationLink]", "click here");
if (EmailFrom != string.Empty)
{
Helpers.Emailer.SendEmail(user.Email, EmailSub, EmailBodyStr, EmailFrom);
}
ModelState.AddModelError("", HttpContext.GetGlobalResourceObject("LanguageResource", "SuccessfullRegistrationMessage").ToString());
}
return View(user);
}
I have created a registration form in mvc4 in which the user get confirmation email once it get registered but its sending the same registration email two times. Above is the code which is used for registration confirmation.
Please let me know where is the problem in code and why its triggering same event two times.
Related
So i'm trying to authenticate my email in .Net 6 core application but without front end (without cshtml), only by sending email,clicking the link and changing the EmailConfirmed box on the user from false to true.
However i have some problem with generating link form Url.Action which i use to send email with the link
var confirmationLink = Request.Scheme + "://" + Request.Host + Url.Action("ConfirmEmail", "Register",new { code = code,userId = userToAdd.Id });
Console.WriteLine(confirmationLink);
When i do this i get this output
and nothing else.
This is my ConfirmEmail action which the email should redirect to but when i do this i get 404 and no action from this controller is triggered.
[HttpPost]
[Route("ConfirmEmail/{token}/{userId}")]
public async Task<IActionResult> EmailAuthentication([FromRoute]string token, [FromRoute]string userId) {
var user = await _userManager.FindByIdAsync(userId);
Console.WriteLine(user.Email);
Console.WriteLine(user.UserName);
Console.WriteLine(token);
Console.WriteLine(user.EmailConfirmationToken);
Console.WriteLine(token == user.EmailConfirmationToken);
if(user == null)
{
return NotFound();
}
if (token == user.EmailConfirmationToken)
{
user.EmailConfirmed = true;
return Ok();
}
return BadRequest();
}
And this is the email sender:
public async Task SendEmailAsync(string toEmail, string subject, string message)
{
if (string.IsNullOrEmpty(Options.SendGridKey))
{
throw new Exception("Null SendGridKey");
}
await Execute(Options.SendGridKey, subject, message, toEmail);
}
public async Task Execute(string apiKey, string subject, string message, string toEmail)
{
var client = new SendGridClient(apiKey);
var emailPass = _config["emailPassword"];
var msg = new SendGridMessage()
{
From = new EmailAddress("employmentlyy#gmail.com", "wlqegxqlkepwrguh"),
Subject = subject,
PlainTextContent = message,
HtmlContent = message
};
msg.AddTo(new EmailAddress(toEmail));
// Disable click tracking.
// See https://sendgrid.com/docs/User_Guide/Settings/tracking.html
msg.SetClickTracking(false, false);
var response = await client.SendEmailAsync(msg);
_logger.LogInformation(response.IsSuccessStatusCode
? $"Email to {toEmail} queued successfully!"
: $"Failure Email to {toEmail}");
}
}
This API is intended for a mobile application. The goal is to let the user confirm the email upon registration. When the user registers, a confirmation link is generated and sent over the email. I've done it the same way in a MVC project, it worked fine, but in a Web API project looks like it ain't gonna cut.
Now when the user clicks that link, the respective action method should be hit and do the job.
The only problem is, the ConfirmEmail action method is just not getting triggered when clicking the confirmation link although it looked fine.
Here are the main configurations which might help
MVC service configuration
services.AddMvc(options =>
{
options.EnableEndpointRouting = true;
options.Filters.Add<ValidationFilter>();
})
.AddFluentValidation(mvcConfiguration => mvcConfiguration.RegisterValidatorsFromAssemblyContaining<Startup>())
.SetCompatibilityVersion(Microsoft.AspNetCore.Mvc.CompatibilityVersion.Version_3_0);
Identity Service
public async Task<AuthenticationResult> RegisterAsync(string email, string password)
{
var existingUser = await _userManager.FindByEmailAsync(email);
if(existingUser != null)
{
return new AuthenticationResult { Errors = new[] { "User with this email address exists" } };
}
// generate user
var newUser = new AppUser
{
Email = email,
UserName = email
};
// register user in system
var result = await _userManager.CreateAsync(newUser, password);
if (!result.Succeeded)
{
return new AuthenticationResult
{
Errors = result.Errors.Select(x => x.Description)
};
}
// when registering user, assign him user role, also need to be added in the JWT!!!
await _userManager.AddToRoleAsync(newUser, "User");
// force user to confirm email, generate token
var token = await _userManager.GenerateEmailConfirmationTokenAsync(newUser);
// generate url
var confirmationLink = _urlHelper.Action("ConfirmEmail", "IdentityController",
new { userId = newUser.Id, token = token }, _httpRequest.HttpContext.Request.Scheme);
// send it per email
var mailresult =
await _emailService.SendEmail(newUser.Email, "BingoApp Email Confirmation",
$"Please confirm your account by <a href='{HtmlEncoder.Default.Encode(confirmationLink)}'>clicking here</a>.");
if (mailresult)
return new AuthenticationResult { Success = true };
else
return new AuthenticationResult { Success = false, Errors = new List<string> { "Invalid Email Address"} };
}
Controller
[HttpPost(ApiRoutes.Identity.Register)]
public async Task<IActionResult> Register([FromBody] UserRegistrationRequest request)
{
if (!ModelState.IsValid)
{
return BadRequest(new AuthFailedResponse
{
Errors = ModelState.Values.SelectMany(x => x.Errors.Select(xx => xx.ErrorMessage))
});
}
// register the incoming user data with identity service
var authResponse = await _identityService.RegisterAsync(request.Email, request.Password);
if (!authResponse.Success)
{
return BadRequest(new AuthFailedResponse
{
Errors = authResponse.Errors
});
}
// confirm registration
return Ok();
}
[HttpGet]
public async Task<IActionResult> ConfirmEmail(string userId, string token)
{
if (userId == null || token == null)
{
return null;
}
var user = await _userManager.FindByIdAsync(userId);
if (user == null)
{
return null;
}
var result = await _userManager.ConfirmEmailAsync(user, token);
if (result.Succeeded)
{
await _emailService.SendEmail(user.Email, "BingoApp - Successfully Registered", "Congratulations,\n You have successfully activated your account!\n " +
"Welcome to the dark side.");
}
return null;
}
Your _urlHelper.Action(..) looks a bit suspicious to me.
I'm not sure you should pass the full controller name, that is, including the actual word controller.
Try _urlHelper.Action("ConfirmEmail", "Identity", instead.
As a tip: I try to avoid magic strings like these by using nameof(IdentityController) because it will return the controller name without the controller postfix.
In my website i had a group of people working on my site and i have this code that they put a canned message in for an error. When i debug the code it is actually a different error but displays this canned message. For instance when i put the information in the form i used an email address that already exists in the database but it is showing a message to check the password requirements. How can this be fixed to show the actual error. To me it also seems like there is a lot code going on in this that may not need to be or can be achieved cleaner Your thoughts?
Code of Post Action:
[HttpPost]
[ValidateAntiForgeryToken]
public ActionResult CreateCompanyUser(ExpandedUserDTO ExpandedUserDTO)
{
try
{
if (ExpandedUserDTO == null)
{
return new HttpStatusCodeResult(HttpStatusCode.BadRequest);
}
var Email = ExpandedUserDTO.Email.Trim();
var UserName = ExpandedUserDTO.UserName.Trim();
var Password = ExpandedUserDTO.Password.Trim();
if (UserName == "")
{
throw new Exception("No Username");
}
if (Password == "")
{
throw new Exception("No Password");
}
// UserName is LowerCase of the Email
// UserName = Email.ToLower();
// Create user
var objNewAdminUser = new Models.ApplicationUser { UserName = UserName, Email = Email };
var AdminUserCreateResult = UserManager.Create(objNewAdminUser, Password);
if (AdminUserCreateResult.Succeeded == true)
{
string strNewRole = Convert.ToString(Request.Form["Roles"]);
if (strNewRole != "0")
{
// Put user in role
UserManager.AddToRole(objNewAdminUser.Id, strNewRole);
}
var viewModel = new Users();
{
viewModel.UsersId = Convert.ToString(Guid.NewGuid());
viewModel.Email = Email;
viewModel.FirstName = UserName;
viewModel.AspNetUsersId = objNewAdminUser.Id;
viewModel.CreatedDate = System.DateTime.Now;
viewModel.UpdatedDate = System.DateTime.Now;
};
UsersBusinessModels Login = new UsersBusinessModels();
var results = Login.insertUserWithougAsny(viewModel);
string[] roleRemove = new string[2] { "Administrator", "CompanyAdmin" };
ViewBag.Roles = GetAllRolesAsSelectList().Where(k => !roleRemove.Contains(k.Text)).ToList();
// return RedirectToAction();
Response.Redirect("/Customer/ManageUsers/" + User.Identity.GetUserId());
return PartialView();
}
else
{
ViewBag.Roles = GetAllRolesAsSelectList();
ModelState.AddModelError(string.Empty,
"Error: Failed to create the user. Check password requirements.");
return PartialView(ExpandedUserDTO);
}
}
catch (Exception ex)
{
ViewBag.Roles = GetAllRolesAsSelectList();
ModelState.AddModelError(string.Empty, "Error: " + ex);
string[] roleRemove = new string[2] { "Administrator", "CompanyAdmin" };
ViewBag.Roles = GetAllRolesAsSelectList().Where(k => !roleRemove.Contains(k.Text)).ToList();
return PartialView(ExpandedUserDTO);
}
}
As Microsoft stated on their website :
Security Stamp:
A random value that must change whenever a users credentials change (password changed, login removed)
I am writing an action where the admin can change a users password manually:
[HttpPost]
[ValidateAntiForgeryToken]
public async Task<IActionResult> ForgotPassword(string userId, string newPassword)
{
var user = await _userManager.FindByIdAsync(userId);
if (user == null)
{
TempData.Put("M", new ResultMessage
{
CssClassName = "alert alert-danger alert-dismissible",
Title = "Failed!",
Message = "User not found!"
});
return RedirectToAction("Index");
}
string resetToken = await _userManager.GeneratePasswordResetTokenAsync(user);
var result = await _userManager.ResetPasswordAsync(user, resetToken, newPassword);
if (result.Succeeded)
{
var ress = await _userManager.UpdateSecurityStampAsync(user); //Is this step necessary?
if (ress.Succeeded)
{
TempData.Put("M", new ResultMessage
{
CssClassName = "alert alert-success alert-dismissible",
Title = "Success!",
Message = "Password updated successfully!"
});
return RedirectToAction("Index");
}
TempData.Put("M", new ResultMessage
{
CssClassName = "alert alert-danger alert-dismissible",
Title = "Failed!",
Message = "Failed at the security stamp step!"
});
return RedirectToAction("Index");
}
TempData.Put("M", new ResultMessage
{
CssClassName = "alert alert-danger alert-dismissible",
Title = "Failed!",
Message = "Failed to reset password!"
});
return RedirectToAction("Index");
}
and validating the security stamp every 15 minutes to force user logout on password change like this :
services.Configure<SecurityStampValidatorOptions>(options =>
options.ValidationInterval = TimeSpan.FromMinutes(15)
);
do i have to generate a new security stamp on every password change or is it done automatically by the user manager ?
using ASP.Net Core 2.2
I'm trying to decrypt the password on the login method, but it allows to login with any password I type in, not sure why, maybe someone could help me out?
My login method in the db layer:
public string loginUser(string userName, string pass)
{
string result = "";
try
{
var mongoClient = new MongoClient("mongodb://localhost");
var database = mongoClient.GetDatabase("SearchForKnowledge");
var coll = database.GetCollection<BsonDocument>("Users");
var filter = Builders<BsonDocument>.Filter.Eq("userName", userName);
var results = coll.Find(filter).ToList().First();
if (BCrypt.Net.BCrypt.Verify(pass, results["password"].ToString()))
{
result = results["userName"].ToString();
}
}
catch (Exception ex)
{
result = "";
}
return result;
}
My user controller:
public ActionResult Login(UsersLogin form)
{
User user = new User();
UserDB udb = new UserDB();
if (!form.Username.IsEmpty())
{
udb.loginUser(form.Username, form.Password);
Session["userName"] = form.Username;
return RedirectToRoute("Home");
}
return RedirectToRoute("Login");
}
The problem is in your controller
udb.loginUser(form.Username, form.Password);
Session["userName"] = form.Username;
return RedirectToRoute("Home");
You call udb.loginUser(form.Username, form.Password);, but you never check the return value of udb.loginUser method, so your code will always redirect to the home page no matter what the user name and password are.
Based on the code of loginUser method, it will return an empty string if the login fails, so change the above three lines of code to below
var loginResult = udb.loginUser(form.Username, form.Password);
if (!string.IsNullOrEmpty(loginResult))
{
Session["userName"] = form.Username;
return RedirectToRoute("Home");
}