If I click the "reset password" button for a user in the AWS Cognito, all it does is re-send the verification email, containing the account registration email code.
However, if the user takes that code and enters it on the verification page, AWS returns the error:
NotAuthorizedException: User cannot be confirmed. Current status is RESET_REQUIRED
First, how do I get Cognito to send a real "password reset" email instead of the "confirm your registration" email?
I assume it's giving me this error because the verification JS is calling:
createCognitoUser(email).confirmRegistration(code, true, function confirmCallback(err, result)
and not some undocumented password reset function. What function should I be calling?
You should be calling forgotPassword. From the AWS Documentation at Using Amazon Cognito User Identity Pools Javascript Examples:
cognitoUser.forgotPassword({
onSuccess: function (result) {
console.log('call result: ' + result);
},
onFailure: function(err) {
alert(err);
},
inputVerificationCode() {
var verificationCode = prompt('Please input verification code ' ,'');
var newPassword = prompt('Enter new password ' ,'');
cognitoUser.confirmPassword(verificationCode, newPassword, this);
}
});
So Even I faced a same issue, Even in AWS cognito documentation it was not clear, basically the process involves two steps.
call cognitoUser.forgotPassword() this will start forgot password process flow, and the user will receive a verification code.
then call cognitoUser.confirmPassword() which will reset the password verifying the code send to the email of user.
Below I have given a cognitoUserClass which has static methods forgotPassword() and confirmPassword() methods which implements those two steps.
import * as AmazonCognitoIdentity from 'amazon-cognito-identity-js'
class cognitoUserClass {
static cognitouser: AmazonCognitoIdentity.CognitoUser
static userPool = new AmazonCognitoIdentity.CognitoUserPool({
UserPoolId: 'your pool id',
ClientId: 'your client id',
})
static forgotPassword(userName: string): void {
const userData = {
Username: userName,
Pool: cognitoUserClass.userPool,
}
cognitoUserClass.cognitouser = new AmazonCognitoIdentity.CognitoUser(
userData
)
cognitoUserClass.cognitouser.forgotPassword({
onSuccess: (data) => {
console.log(data)
},
onFailure: (err) => {
console.log('ERR:', err)
},
})
}
static confirmPassword(
verificationCode: string,
newPassword: string
): void {
cognitoUserClass.cognitouser.confirmPassword(
verificationCode,
newPassword,
{
onFailure(err) {
console.log(err)
},
onSuccess(data) {
console.log(data)
},
}
)
}
}
export { cognitoUserClass }
Related
I am trying to make a custom authentication flow using AWS Cognito so that i can send MFA codes via email instead through the cognito triggers. I am using the initiateAuth() method to do this which is correct according to the documentation;
https://docs.aws.amazon.com/cognito-user-identity-pools/latest/APIReference/API_InitiateAuth.html https://docs.aws.amazon.com/AWSJavaScriptSDK/latest/AWS/CognitoIdentityServiceProvider.html#initiateAuth-property
My payload seems to be valid but when i try login with a user i get the error 't.getauthparameters is not a function'
I've had a look through some other stackoverflow posts but nothing is helping
Any ideas what is going wrong?
This is a snippet from my code below:
const payload = {
AuthFlow: 'CUSTOM_AUTH',
ClientId: 'my client id',
AuthParameters: {
USERNAME: $('input[name=username]').val(),
PASSWORD: $('input[name=password]').val(),
CHALLENGE_NAME: 'SRP_A'
}
};
cognitoUser.initiateAuth(payload, {
onSuccess: function(result) {
// User authentication was successful
},
onFailure: function(err) {
// User authentication was not successful
},
customChallenge: function(challengeParameters) {
// User authentication depends on challenge response
var verificationCode = prompt('Please input OTP code' ,'');
cognitoUser.sendCustomChallengeAnswer(verificationCode, this);
},
});
So i ended up finding out that initiateAuth() is not the correct method to use.
The right method to use is cognitoUser.authenticateUser() (since i am using SRP-based authentication then adding a custom challenge) - My updated code is below
This was a similar example that i followed to help me find the answer
I couldnt find very much online for doing it with just the Amazon Cognito Identity SDK so hopefully this is helpful for anyone doing the same!
AWSCognito.config.region = 'region';
var poolData = {
UserPoolId : 'user pool id',
ClientId : 'client id'
};
var userPool = new AmazonCognitoIdentity.CognitoUserPool(poolData);
var userData = {
Username: $('input[name=username]').val(),
Pool: userPool,
};
var authenticationData = {
Username : $('input[name=username]').val(),
Password : $('input[name=password]').val(),
};
var authenticationDetails = new AmazonCognitoIdentity.AuthenticationDetails(authenticationData);
var cognitoUser = new AmazonCognitoIdentity.CognitoUser(userData);
cognitoUser.setAuthenticationFlowType('CUSTOM_AUTH');
cognitoUser.authenticateUser(authenticationDetails, {
onSuccess: function(result) {
console.log('success');
var resultStr = 'Login Successful';
console.log(resultStr);
$('#resultsSignIn').html(resultStr);
},
onFailure: function(err) {
alert(err);
},
customChallenge: function(challengeParameters) {
// User authentication depends on challenge response
var verificationCode = prompt('Please input OTP code' ,'');
cognitoUser.sendCustomChallengeAnswer(verificationCode, this);
},
});
return false;`
A downside to the authenticateUser() method is that you won't be able to get user's input mid-execution during the authenticateUser workflow (i.e, having to use prompts in the callbacks for customchallenge etc). I believe initiateAuth() would solve this issue.
https://docs.aws.amazon.com/cognito/latest/developerguide/user-pool-lambda-define-auth-challenge.html
I am built a sign up component by using React and Loopback 3. At first I was not using email verification. In the code below I was getting response from rest api about user creation.
export function signUpUser(user) {
let userEmail = user.email;
let userPassword = user.password;
let userName = user.userName;
let privilege="user"
let credentials = {
email: userEmail,
password: userPassword,
userName: userName,
privilege:privilege
};
//console.log(credentials);
return dispatch => {
axios
.request({
method: "post",
url: loopBack + "/blogusers",
data: credentials
})
.then(response => {
console.log(response)
return dispatch(signUpSuccess(response.data));
})
.catch(error => {
return dispatch(signUpFailure(error));
});
};
}
My user model js was that
"use strict";
module.exports = function(Bloguser) {
};
After I add hook to create api my user model js is that.
"use strict";
var config = require("../../server/config.json");
var path = require("path");
var senderAddress = "jp6t63je22rfqgf7#ethereal.email";
module.exports = function(Bloguser) {
//send verification email after registration.
Bloguser.afterRemote("create", function(context, userInstance, next) {
console.log(">user.afterRemote triggered");
var options = {
type: "email",
to: userInstance.email,
from: senderAddress,
subject: "Thanks for registering.",
template: path.resolve(__dirname, "../../server/views/verify.ejs"),
Bloguser: userInstance
};
userInstance.verify(options, function(err, response) {
if (err) {
Bloguser.deleteById(userInstance.id);
return next(err);
}
console.log(">verification email sent.", response);
context.res.render("response", {
title: "Signed up successfully",
content:
"Please check your email and click on the verification link" +
"before logging in.",
redirectTo: "/login",
redirectToLinkText: "Log in"
});
});
});
};
By this code I can successfully send verification email and I can verify user by the link in the email. However I cannot get response from the Post api in the signUpUser() function. I need to get response to show the user status of his/her sign up process
I changed
context.res.render("response", {
title: "Signed up successfully",
content:
"Please check your email and click on the verification link" +
"before logging in.",
redirectTo: "/login",
redirectToLinkText: "Log in"
});
with
context.res.status(200).send(userInstance)
And Voile it worked.
I'm doing my app users management with the Cognito AmazonWebService and on AngularJS.
I can not figure out how to solve this problem :
After registering, users are receiving an email with a code to confirm it. When I try to enter and validate the code I have a pop-up message saying "Error: the user is not authenticated".
But if is I swap the steps I can not authenticated myself because I've this error: "Your account must be confirmed".
EDIT: That's how I'm confirming the registration :
var userPool = new AWSCognito.CognitoIdentityServiceProvider.CognitoUserPool(_poolData);
var userData = {
Username : username,
Pool : userPool
};
var cognitoUser = new AWSCognito.CognitoIdentityServiceProvider.CognitoUser(userData);
cognitoUser.getAttributeVerificationCode('email', {
onSuccess: function (result) {
console.log('call result: ' + result);
},
onFailure: function(err) {
console.log("error");
alert(err);
},
inputVerificationCode: function(code) {
var verificationCode = prompt('Check you email for a verification code and enter it here: ' ,'');
cognitoUser.verifyAttribute('email', verificationCode, this);
}
});
I have also try this code below :
var cognitoUser = new AWSCognito.CognitoIdentityServiceProvider.CognitoUser(userData);
cognitoUser.confirmRegistration('123456', true, function(err, result) {
if (err) {
alert(err);
return;
}
console.log('call result: ' + result);
});
But everytime I'm using the code you gave me to confirm an user I have this error message : "ExpiredCodeException: Invalid code provided, please request a code again." while user is well confirmed in my user pool...
How could I solve it ?
Your second code is the right one to be called. How long are you waiting to call?
I've turned on email verification in my Parse account, but no emails are being sent.
a) I've successfully signed up two private email accounts (using Javascript API), and neither receives an email.
b) The Users are created properly, but the emailVerified field is set to "undefined", shouldn't it be false?
d) I've tried setting emailVerified to false programmatically, and saving the user, but no verification email.
e) I've tried changing the user's email in the Parse data viewer, but no email.
Any suggestions? I recently tested password recovery and it works fine.
Here is my code.
function psSignup(roleName, email, password, company, domain, firstName, lastName, phone, position, referralName, referralSource) {
// Always log out to clear old sessions before we sign in new user.
Parse.User.logOut();
var user = new Parse.User();
user.set("username", email);
user.set("password", password);
user.set("firstName", firstName);
user.set("lastName", lastName);
user.set("phone", phone);
user.set("company", company);
user.set("position", position);
user.set("referralName", referralName);
user.set("referralSource", referralSource);
// Signup asynch
user.signUp(null, {
success: function(user) {
var queryRole = new Parse.Query(Parse.Role);
queryRole.equalTo('name', roleName);
queryRole.first({
success: function(result) {
// Assign users role for security purposes.
var role = result;
role.getUsers().add(user);
role.save().then(function() {
// Create acount, link to user, save account
var account = psMakeAccount(company, user, domain);
return account.save();
}).then(function(accountObject) {
// link to user
user.set("account", accountObject);
user.save();
}).then(function(userObject) {
// Success Go to second login page.
document.location.href = "/signup/location-information/";
}, function(error) {
messageAlert("Signup", "Failed to save objects with error: " + error.message);
});
}
});
},
error: function(user, error) {
// Show the error message somewhere and let the user try again.
messageAlert("Signup", "Signup error: " + error.message);
}
});
}
I have a form where users enter their email address and password into a join form. This creates the account but I now want to develop it further.
client.js:
Template.joinForm.events({
'submit .form-join': function(e, t) {
e.preventDefault();
var email = t.find('#email').value,
password = t.find('#password').value,
username = Random.id(),
array = [],
profile = {
nameOfArray: array
};
Accounts.createUser({
email: email,
username: username,
password: password,
profile: profile
}, function(error) {
if (error) {
alert(error);
} else {
Router.go('/');
}
});
}
});
Before creating a user account, how do you:
Check if the email variable from the joinForm does not already exist in the Meteor.users collection. Processing this on the server?
If email does exist, then reject user creation?
I have seen the new function and wondering if I can use this http://docs.meteor.com/#/full/accounts_validatenewuser
Accounts.validateNewUser(function (user) {
// pseudo if statement code
if (email not exist) {
// 1. create the user account and then
Accounts.sendVerificationEmail(userId, [email])
} else {
throw new Meteor.Error(403, "email address is already registered");
}
});
Thank you for reading this.
I'm unclear as to whether to use Accounts.createUser or Accounts.onCreateUser and which code should be on the client, and which on the server. My aim is to build the account securely, therefore, deny any other modification privileges during this process from the console.
The extra empty array nameOfArrayis now created on the server if the account is allowed to be created, ie, passing the validateNewUser function. Of course, you can add more validation checks for example, password length.
client.js:
Template.joinForm.events({
'submit .form-join': function(e, t) {
e.preventDefault();
var email = t.find('#email').value,
password = t.find('#password').value,
username = Random.id();
Accounts.createUser({
email: email,
username: username,
password: password,
profile: profile
}, function(error) {
if (error) {
alert(error.reason);
} else {
Router.go('/');
}
});
}
});
server.js:
Accounts.onCreateUser(function(options, user) {
var newEmail = user.emails[0].address;
console.log(newEmail);
var emailAlreadyExist = Meteor.users
.find({"emails.address": newEmail}, {limit: 1})
.count() > 0;
console.log(emailAlreadyExist + ' already exists');
if (emailAlreadyExist === true) {
throw new Meteor.Error(403, "email already registered");
} else {
profile = options.profile;
profile.nameOfArray = [];
user.profile = profile;
return user;
}
});
I've found that Accounts.createUser has it's own validation built in and checks for already existing email/login.
Meteor docs: Accounts.createUser:
If there are existing users with a username or email only differing in
case, createUser will fail.
Thus Accounts.onCreateUser doesn't even fire if Accounts.createUser email/login validation throws error.
The Accounts.validateNewUser function requires users to validate their email after submitting. It's basically that step where, after you sign up for something, before you can sign in you have to enter a code that's sent to you on your email or mobile device -- basically, it ensures that the user is who they say they are. It's what might prevent you from signing up with the email totallyfake#totally_not_a_real_place.com.
If I'm reading your question right, you're more interested in seeing if an email is unique than in seeing if the user actually owns that email account. You can do this with Accounts.onCreateUser, which runs on the server:
Called whenever a new user is created. Return the new user object, or throw an Error to abort the creation.
The full process will look something like this. On the client, exactly what you have:
Template.joinForm.events({
'submit .form-join': function(e, t) {
e.preventDefault();
var email = t.find('#email').value,
password = t.find('#password').value,
username = Random.id(),
array = [],
profile = {
nameOfArray: array
};
Accounts.createUser({
email: email,
username: username,
password: password,
profile: profile
}, function(error) {
if (error) {
alert(error);
} else {
Router.go('/');
}
});
}
});
Then, on the server, before the user is actually created, it will run the user through your onCreateUser function, and whatever you return will be inserted into the users collection:
Accounts.onCreateUser(function(options, user) {
var email = user.emails[0];
if (!email) { throw new Meteor.Error("You must provide a non-empty email"); // this may actually not be necessary -- createUser might do it for you
if (Meteor.users.find({emails: email}) {
throw new Meteor.Error("A user with email " + email + " already exists!");
}
... // whatever other transformation you might want
return user;
});
You might also check out the accounts-ui package, since depending on how much you want to do vary from a vanilla implementation of user accounts, a lot may already be done for you.
The Accounts.validateNewUser is used to check if the fields of the user object comply with the desired format, returning true or false accordingly.
To check if the email is already registered, I think you should include this verification in the Accounts.onCreateUser function (http://docs.meteor.com/#/full/accounts_oncreateuser).
Without having tested the code, you can try something like this:
Accounts.validateNewUser(function (user) {
// Check compliance of the user object fields, using check http://docs.meteor.com/#/full/check
});
Accounts.onCreateUser(function(options, user) {
if (options.profile){
user.profile = options.profile;
}
if (Meteor.users.find({email: user.email}).fetch == 0) {
if(Accounts.validateNewUser(user)){
Accounts.sendVerificationEmail(userId, [email]);
return user;
} else {
throw new Meteor.Error(403, "Error checking user fields");
} else {
throw new Meteor.Error(403, "email address is already registered");
}
}