As titled, I'm using MVC 3 helper Crypto to hash my password, then i save nothing except encrypted password into database.
When I implement the login form, I make a boolean function, retrieve password stored in database
and get the boolean result with this helper:
Crypto.VerifyHashedPassword(password in DB, password in login form)
if true, then login
if false, the display "password wrong"
This few day, I was trying to test my login function. I found one fact: If a new registrar comes to register at the system, the previous user will be failed to login, displaying "password wrong".
Is there any problem with my program or did I miss something?
Function to return bool
if (Crypto.VerifyHashedPassword(parent.Password, password))
{
return true;
}
else
{
ModelState.AddModelError("", "You was input password wrongly!");
return false;
}
Code to hash and store the password
parent.Password = Crypto.HashPassword(parent.Password);
peoplemanager.Add(parent);
I would advise you to follow this Tutorial I found it very helpful when creating my membership solution:
http://theintegrity.co.uk/2010/12/asp-net-mvc-2-custom-membership-provider-tutorial-part-3/
Related
I'm working on a practice project with spring boot and thymeleaf.
I have a registration form. After the user has registered their account, a verification code will be sent to their email. The next page is where they will enter the code.
I'm using RedirectAttributes to store the verification code id so I will be able to lookup/confirm the code in the verification code page. I am also using RedirectAttributes to pass the user email and raw password to the verification code page so they can be automatically logged in after confirming their account.
#PostMapping("/register")
public String processRegisterForm(#ModelAttribute NewAccountRequest newAccountRequest, RedirectAttributes redirectAttributes) {
Long verifyCodeId = accountService.createAccount(newAccountRequest);
redirectAttributes.addFlashAttribute("verifyCodeId", verifyCodeId);
redirectAttributes.addFlashAttribute("userEmail", newAccountRequest.getEmail());
redirectAttributes.addFlashAttribute("rawPassword", newAccountRequest.getPassword());
return "redirect:/register/verify-account";
}
Now, here is where it gets messy. In the GetMapping for the page to enter verification code, I also had to add the exact same attribute I added in the above PostMapping. Like this:
#GetMapping("/register/verify-account")
public String loadVerificationPage(#ModelAttribute("verifyCodeId") String verifyCodeId, #ModelAttribute("userEmail") String userEmail, #ModelAttribute("rawPassword") String rawPassword, Model model) {
model.addAttribute("verifyCodeId", verifyCodeId);
model.addAttribute("userEmail", userEmail);
model.addAttribute("rawPassword", rawPassword);
return "verify-account";
}
And in thymeleaf, I did this:
<input type="hidden" name="verificationCodeId" th:value="${verifyCodeId}">
<input type="hidden" name="userEmail", th:value="${userEmail}">
<input type="hidden" name="rawPassword" th:value="${rawPassword}">
And in the PostMapping for the page to enter verification code:
#PostMapping("/register/verify-account")
public String confirmVerifyCode(#RequestParam String enteredVerificationCode, #RequestParam String verificationCodeId, #RequestParam String userEmail, #RequestParam String rawPassword,
HttpServletRequest request) {
accountService.confirmVerificationCode(enteredVerificationCode, verificationCodeId);
try {
request.login(userEmail, rawPassword);
}
catch (ServletException e) {
System.out.println("Login error: " + e);
}
return "redirect:/?loginSuccess";
}
I can't get the flash attribute directly in the PostMapping of register/verify-account so I needed to use input fields in thymeleaf
I feel like there's a much easier and simpler way to do this because this just seem really messy. Any tips? Please also note that I haven't added any validations so ignore that. And could their be any security risks in using flash attributes to hold verification codes and passwords? Or to storing verification code in "hidden" input fields?
The registration flow:
User loads the register page
When the form is submitted, processRegisterForm() is called;
The user's account is created and the verification code id is returned.
To confirm the verification code in the next page, the id is stored in a flash attribute. The user's email and raw password is also stored. User is redirected to the page to enter verification code
For the GetMapping of the page to enter verification code, model attributes are added to store the code id, user email, and password. In thymeleaf, hidden input fields are used to hold those 3 values mentioned.
The PostMapping of the page to enter verification code gets those 3 fields and uses it to verify/auto login the user.
I'm hopping there is a much better way to achieve this. Sorry for the long post. Any help will be appreciated, thanks
I ended up cleaning the code up by using a DTO instead of those request params. Don't know why I didn't do that earlier.
In the /register/verify-account mapping, before logging in the user, I did this:
if (passwordEncoder.matches(userPassword, dbPassword)) {
try {
request.login(account.getEmail(), userPassword);
}
catch (ServletException e) {
System.out.println("Login after account verification failed" + e);
}
}
else {
System.out.println("fishy fishy");
}
I'm not sure if this really adds any security but it's good enough for me for now. Special thanks to these people for helping me: , , and
EDIT: I ended up using HttpSession
I have built my own UserProvider that is authenticating against another database. Everything is working great; I'm able to log in/out etc. I'm down to returning error messages and running into a snag.
Here is what an example of my code looks like:
MyUserProvider.php
...
$auth = json_decode($response, true); // response from Guzzle to 3rd party auth
if ($auth) {
if ($auth['errors']) {
return redirect('login')
->withErrors(['auth' => 'Invalid username or password']);
} else {
$myUser = $auth['data']['user']; // auth object from 3rd party.
$user = User::where('id', $myUser['id'])->first(); // find user in local db.
...
}
}
}
The part I am struggling with is how to handle the redirect. The error I am getting is:
validateCredentials() must be an instance of Illuminate\Contracts\Auth\Authenticatable, instance of Illuminate\Http\RedirectResponse given
Which make sense, I'm returning a redirect, not an authenticatable object (a user). But, I don't have a user -- my username or password was wrong.
How can I redirect the user back and display a flash message telling them what happened?
Thank you for any suggestions!
I am trying to migrate users to Cognito when they sign in the first time. For this I wrote a lambda function that does call an API to check if the users exist in db or not ? if the user exists, it will be created in cognito but I am not sure how do I tell the application that user is created and it should allow the user to login .
Here is the code in c#:
public async Task<Stream> FunctionHandlerAsync(Stream stream, ILambdaContext context)
{
RootObject rootObj = DeserializeStream(stream);
User user = new User(rootObj.userName, rootObj.request.password);
ApiResponse apiResponse = await MobileAuthenticateAsync(user.UserName, user.Password);
// Considering apiResponse returns "user authenticated", we create the user in //cognito. This is working.
// How do I send response back to Application so it knows that user is // //created and authenticated and should be allowed to login.
//Before returning stream, I am setting following 2 status.
rootObj.response.finalUserStatus = "CONFIRMED"; // is this correct ?
rootObj.response.messageAction = "SUPPRESS";
return SerializeToStream(rootObj);;
}
You're pretty close.
You can see the full documentation on the Migrate User Lambda Trigger page, however in short you need your response to look like:
{
response: {
userAttributes: {
email: 'user#example.com',
email_verified: true,
custom:myAttribute: 123,
},
finalUserStatus: 'CONFIRMED',
messageAction: 'SUPPRESS',
forceAliasCreation: false,
}
}
Where:
userAttribute: this is a dictionary/map of the user's attributes keys in cognito (note that any custom attributes need to be prefixed with custom:), to the values from the system you're migrating from. You do not need to provide all of these, although if you're using an email alias you may want to set email_verified: true to prevent the user having to re-verify their e-mail address.
finalUserStatus: if you set this to CONFIRMED then the user will not have to re-confirm their email address/phone number, which is probably a sensible default. If you are concerned that the password is given as plain-text to cognito this first-time, you can instead use RESET_REQUIRED to force them to change their password on first sign-in.
messageAction: should probably be SUPPRESS unless you want to send them a welcome email on migration.
forceAliasCreation: is important only if you're using email aliases, as it stops users who manage to sign-up into cognito being replaced on migration.
If you respond with this (keeping the rest of the original rootObj is convenient but not required then the user will migrated with attributes as specified.
If you throw (or fail to respond with the correct event shape) then the migration lambda fails and the user is told that they couldn't migrated. For example, because they do not exist in your old user database, or they haven't provided the right credentials.
I have a login form that I created in MVC 3 which has a 'change password' view.
At the moment there are no restrictions with respect to reusing previous passwords.
I understand that I'd need to create a custom password manager as ASP.net has nothing out the box for this.
I have a 'previousPassswords' table created.
currently , my 'changePassword' controller looks like this:
if (ModelState.IsValid)
{
// ChangePassword will throw an exception rather
// than return false in certain failure scenarios.
bool changePasswordSucceeded;
try
{
MembershipUser currentUser = Membership.GetUser(User.Identity.Name, true /* userIsOnline */);
changePasswordSucceeded = currentUser.ChangePassword(model.OldPassword, model.NewPassword);
}
catch (Exception)
{
changePasswordSucceeded = false;
}
if (changePasswordSucceeded)
{
return RedirectToAction("ChangePasswordSuccess");
}
else
{
ModelState.AddModelError("", "The current password is incorrect or the new password is invalid.");
}
}
// If we got this far, something failed, redisplay form
return View(model);
I'd like to compare new passwords (preferrably encrypted) with the last 'n' passwords in the previousPasswords table an act accordingly.
Is this the recommended approach or is there a better way ?
You shouldn't check on previous passwords (compare: https://nvlpubs.nist.gov/nistpubs/SpecialPublications/NIST.SP.800-63b.pdf).
First, it increases the Chance that users go for easier and therefore more insecure Passwords:
'I can't use myPassword as Password any longer? well the make it password then'. Or worse: 'I can't use my individual Password? Well, I'm gonna use the email password then'
Secondly, if your user data get stolen/hacked the effect will be even worse: you won't loose one Password per user but multiple.
For Password policies, check Paragraph 5.1
I am using forms authentication for an MVC website and I am having a problem adding Cookies, I am using an Encrypted Forms Authentication Ticket and adding it to the Cookies but when inspecting my cookies it is there (by name "AuthCookie") but the value is always null and the Expires date is always set to "01/01/0001 00:00"... here is my Login controller code:
[HttpPost]
public ActionResult Index(Login login, string returnUrl)
{
if (ModelState.IsValid)
try
{
User user = UserManager.Login(login.Username, login.Password);
string serialUser = Serialize.SerializeToString(user);
string ticket = FormsAuthentication.Encrypt(
new FormsAuthenticationTicket(1, login.Username, DateTime.Now, DateTime.Now.AddMinutes(20.0), login.RemeberMe, serialUser));
HttpCookie cookie = new HttpCookie(FormsAuthentication.FormsCookieName, ticket) { Expires = DateTime.Now.AddMinutes(20) };
Response.Cookies.Add(cookie);
if (String.IsNullOrEmpty(returnUrl))
return RedirectToAction("Index", "Home");
else
return Redirect(returnUrl);
}
catch (LoginFailedException)
{
ModelState.AddModelError("", "Login failed: Invalid Username or Password.");
return View(login);
}
else
return View(login);
}
At first I assumed the encrypted string was not working due to the length but I have tested this by creating a simple test cooke and I am getting the same result.
Can anyone help
When you call Redirect() or RedirectToAction(), you're terminating the response so the cookies aren't sent to the client. Some solutions:
Use TempData to persist the information across the direct, writing the Cookie in the action you redirect to.
Take a look at the way Forms Authentication cookie information is written in the NerdDinner code on CodePlex.
As mentioned in the comments, you can persist role information in Session. The recommendation to store the role information in Session and retrieve from Roles if not found would work, but I'd start by using the membership system as-is and performance tuning later if you see that it's a problem, rather than assuming it will be.