Can you manually set 'phone_number_verified' to true on an AWS Cognito user, without having the end user need to interact with your web-app? - amazon-web-services

We just solved an issue where entering a wrong phone number (starting digit was missing) resulted in 'phone_number_verified' defaulting to false. Now that the issue is resolved, we do still have a lot of registered users who have had an activation sent to them (and entered said code) whose statuses are CONFIRMED in AWS Cognito, but have the 'phone_number_verified' property still set to false. Is there any way to edit this property in bulk / per user in Cognito itself or is there an API call that needs to be made? Or any solution that does not require the end user to go through the activation process again themselves.
(Further Info)
- AngularJS as Front-End
- Node v6.9 as Back-End

The recommendation here is that the users should call GetUserAttributeVerificationCode and receive a code to verify the phone/email which is then supplied to VerifyUserAttribute. This will ensure that if their numbers are verified.
Although if you are sure that all of their numbers are verified, you can use AdminUpdateUserAttributes to mark them verified. We do not have a batch API right now.

The phone number verified can be set to true by using the following example payload:
let parameters = {
UserPoolId : `${USER_POOL_ID}`,
Username : `${Username}`,
UserAttributes : [
{
'Name': "phone_number" ,
'Value': `${phoneNumber}`
},{
'Name':"phone_number_verified",
'Value': "true"
}]
}
cognitoIdentityServiceProvider.adminUpdateUserAttributes(parameters,function (err, result) {
if(err)
console.log(err);
else
console.log("Attribute updated successfully");
})

Related

Importing PHP crypt MD5 passwords into Google Cloud Identity Platform?

I'm trying to import existing users from another application into Google Cloud Identity Platform using passwords that were hashed using PHP's crypt function with an MD5 output.
This is an example of the hashed password being used in PHP:
$hashed_password = '$1$AT$JGYIRSP7xIYmg1XSoJmvB1';
$user_input = 'test123';
if (hash_equals($hashed_password, crypt($user_input, $hashed_password))) {
echo "Password verified!";
}
I've tried all sorts of combinations for importing the user and their password, but no combination seems to work. This is the NodeJS import script I'm using:
var admin = require('firebase-admin');
var app = admin.initializeApp({
credential: admin.credential.applicationDefault()
});
admin
.auth()
.importUsers(
[
{
uid: '31',
email: 'user31#test.test',
// Must be provided in a byte buffer.
passwordHash: Buffer.from('$1$AT$JGYIRSP7xIYmg1XSoJmvB1'),
// Must be provided in a byte buffer.
passwordSalt: Buffer.from('$1$AT$'),
},
],
{
hash: {
algorithm: 'MD5',
rounds: 0,
},
}
)
.then((results) => {
console.log(results);
results.errors.forEach((indexedError) => {
console.log(`Error importing user ${indexedError.index}`);
});
})
.catch((error) => {
console.log('Error importing users :', error);
});
As mentioned above, I've tried just about every combination of hash and passwordSalt I could think of. I've tried:
passing no passwordSalt
passing a passwordSalt of AT
passing a passwordSalt of AT$
passing a passwordSalt of $AT
passing a passwordSalt of $AT$
all of the above but with a hash algorithm of BCRYPT
I can see the user getting imported. If I change the hash to something like a regular MD5 hash, I'm able to authenticate as that user, so I know the import process is working correctly.
Does GCP Identity Platform simply not support these hashes? Am I passing the salt incorrectly or passing an incorrect number of rounds? Am I passing the wrong hash algorithm? I'm a little surprised, as I would've thought passwords hashed using PHP's crypt function would've been supported.
4 points:
1.- You are mentioning that you are trying to import existing users and their passwords from another application into Google Cloud Identity Platform. Also, you mentioned that you are using a NodeJS import script focused on MD5. Based on that, and using the same official GCP documentation that you quoted Migrating users from an existing app vs the NodeJS’s script you posted, it seems that you didn’t use the code sample that is posted in that documentation, exactly in the way it should be used, unless that you are the owner of the domain "#test.test":
getAuth()
.importUsers(
[
{
uid: 'some-uid',
email: 'user#example.com',
// Must be provided in a byte buffer.
passwordHash: Buffer.from('password-hash'),
// Must be provided in a byte buffer.
passwordSalt: Buffer.from('salt'),
},
],
{
hash: {
algorithm: 'PBKDF2_SHA256',
rounds: 100000,
},
}
)
.then((results) => {
results.errors.forEach((indexedError) => {
console.log(`Error importing user ${indexedError.index}`);
});
})
.catch((error) => {
console.log('Error importing users :', error);
});
2.- Are we sure you have the required admin rights or role over IAM in your GCP’s Organization? You can use the following official GCP documentation for understanding roles Understanding roles
3.- How are you figuring out that it is not working? Are you facing any of the following errors? Error codes. Is there any particular behavior after the importing process finishes or any log that you can share here editing your original post? Or is it only because you see that once the users are imported to IAM, they are not able to authenticate?
4.- I suggest you by now to follow (Method: users.insert) as stated here Method: users.insert first, this way you are going to review the hash formats that are supported. Finally, you need to do this test with the domains that you have verified in Cloud Identity.

What is "cognitoUser.getAttributeVerificationCode"?

I'm convinced that Amazon goes out of its way to make understanding their platform as difficult as is possible.
I've read over the documentation regarding "cognitoUser.getAttributeVerificationCode" at Amazon only to have it make me even more confused!
Verify an Attribute
The following example verifies user attributes for an authenticated user.
cognitoUser.getAttributeVerificationCode('email', {
onSuccess: function (result) {
console.log('call result: ' + result);
},
onFailure: function(err) {
alert(err);
},
inputVerificationCode: function() {
var verificationCode = prompt('Please input verification code: ' ,'');
cognitoUser.verifyAttribute('email', verificationCode, this);
}
});
Can anyone help me understand what this is (cognitoUser.getAttributeVerificationCode) and/or how I would use it? I don't understand why I would verify an email attribute w/a verification code.
The verification code is sent to the users email. The user has to properly receive that email to retrieve the code and enter it in a UI so that the email is set to verified in Cognito. That user can then reset their password using that email.
What if the user had entered an incorrect email? Or their email system didn't allow them to receive the code sent by AWS?
By sending out a verification code, and having the user send it back, Cognito verifies that the email was entered correctly and belongs to that user. This can seem like a pain but is standard on many web platforms now days...The same process is needed with phone numbers for users in your Cognito user pool.

AWS Cognito username/email login is case-sensitive

Setup
I am using AWS Cognito to manage the user registration and user access for my web application. Specifically I am using the Cognito hosted UI. This means that Cognito presents a UI for my users to register, I do not have access to modify the user sign-up or login pages for my application (other than the controls provided by Cognito). I am using email addresses as usernames, so new users are simply asked to provide an email address and password.
Problem
Cognito treats email addresses as case sensitive. If a user signs up with the email address JOHN_smith#randommail.com, they cannot then sign in using john_smith#randommail.com.
I want user email addresses for sign-up and login to be case insensitive.
What I have tried
Usually this would be trivial to deal with by setting the email address to the lowercase in the client before sending it to the server. However I do not have access to the client UI as it is hosted by Cognito.
My plan therefore was to try using a Lambda function invoked by a Cognito pre-signup trigger to lowercase the email supplied by the user.
Pre sign-up
Amazon Cognito invokes this trigger when a user attempts
to register (sign up), allowing you to perform custom validation to
accept or deny the registration request.
Here is the lamdba function I wrote:
'use strict';
console.log('Loading function');
exports.handler = (event, context, callback) => {
console.log('Received event:', JSON.stringify(event, null, 2));
var triggerSource = event.triggerSource;
console.log('Received triggerSource:', triggerSource);
var email = event.request.userAttributes.email;
console.log('Received email:', email);
var modifiedEvent = event;
if (email !== null) {
var lowerEmail = email.toLowerCase();
modifiedEvent.request.userAttributes.email = lowerEmail;
console.log('Set email in request to', lowerEmail);
console.log('Modified event:', JSON.stringify(modifiedEvent, null, 2));
} else {
console.log('Email evaluated as NULL, exiting with no action');
}
// Return result to Cognito
callback(null, modifiedEvent);
};
This 'worked' in the sense that the email address in the event request was modified to be lowercase (john_smith#randommail.com). However, it seems the account has already been created in the userpool by the time my Lambda function receives this event. Changing the email address in the request had no effect - the original email address (JOHN_smith#randommail.com) still appears in my user pool. I suspect the only fields in the event that have any effect are the response fields. Here is what my modified event looks like:
{
"version": "1",
"region": "us-east-1",
"userPoolId": "us-east-1_xxxxxxx",
"userName": "xxxxxx-xxxx-xxxx-xxxx-xxxxxxx",
"callerContext": {
"awsSdkVersion": "aws-sdk-java-console",
"clientId": "xxxxxxxxxxxxxxxxxxxxxx"
},
"triggerSource": "PreSignUp_SignUp",
"request": {
"userAttributes": {
"email": "john_smith#randommail.com"
},
"validationData": null
},
"response": {
"autoConfirmUser": false,
"autoVerifyEmail": false,
"autoVerifyPhone": false
}
}
My question
I'm looking for ideas or examples to make my user registration and login case insensitive. This might include changes to my lambda trigger approach or something else entirely.
Please note I know I could implement my own UI, which I will only do as a last resort.
Fixed on new user pools. You can turn off case sensitivity now.
https://aws.amazon.com/about-aws/whats-new/2020/02/amazon-cognito-user-pools-service-now-supports-case-insensitivity-for-user-aliases/
You could trigger a Lambda function after sign-up to change the email to lowercase. Without actually testing it, you should be able to trigger a Lambda post confirmation. That Lambda could use AdminUpdateUserAttributes API, called from your SDK of choice, to change the email to lowercase.
Note that user names are also case sensitive.
Since username is case sensetive: why not just use the username as the email address and have the prehook populate the email field with the username?
Make username lowercase when creating the account. Make username lowercase when logging in. At least this way you only have to re-create users that have uppercase characters. Or you can rebuild your entire user pool and migrate the users, but who knows what affect that will have on passwords and MFA. It doesn't seem worth the trouble with an existing user pool. Hopefully you are doing a brand new project and you can select the case insensitive option when creating the user pool.
After multiple attempts, I have tried adding a small css for username field on login page and forgot password page.
#amplify-id-8,#amplify-id-0{
text-transform: lowercase;
}
#amplify-id-0::placeholder,#amplify-id-8::placeholder{
text-transform: capitalize;
}
This worked for me on the amplify-authenticator , I used as a login component.
another possibility is if you can create a user signup api, which would create the user in the cognito. after trimming email along with performing lower case on email. you can perform your custom steps before that.
you are right that pre-signup trigger basically get invoked after signup is performed.
https://docs.aws.amazon.com/cognito/latest/developerguide/user-pool-lambda-pre-sign-up.html#aws-lambda-triggers-pre-registration-tutorials

Cognito change phone_number before confirm via Phone

I want to change phone_number attribute of user before they confirm via phone. My flow step:
User register by username, password, and phone number
User must be enter confirmation code received by the phone. In this step user want to change the phone number (wrong number or change the phone...)
2.1 In case the 1st phone number be wrong, the next phone number is correct -> only one confirmation code had been sent -> it works!
2.2 In case the 1st phone number and the next are correct -> have two confirmation code had been sent(1st - xxx, 2nd - yyy) -> User enter 2nd confirmed code, Cognito throws CodeMismatchException: Invalid verification code provided, please try again. error. User enter 1st code, user had been confirmed, but in Cognito system the user has phone_number is 2nd number and phone_number_verified is true.
I use adminUpdateUserAttributes to change phone_number of a user who has status is UNCONFIRMED. Confirmation code auto send after me call change phone number.
How to fix this?
!!!Update
Currently, I removed the feature User can update their phone_number before they confirmed via phone from my application.
It takes me about 5 days, I just want to memo my case.
When you try to update phone_number (or email) attribute, Cognito will send a confirmation to your phone (or email) in automatically, this is the first code - (1st - xxx), the code to confirm your new attribute value (not for user confirmation).
In the same time, logic code calls resendConfirmationCode function, it send the second code - (2nd - yyy), this is main reason only the second code working (we use confirmSignUp function to handle the code).
I am on the Cognito team, same as behrooziAWS. After looking at your scenario, it does seem to be a bug on our side. I will mention it within the team so that we prioritize it accordingly.
This question was asked awhile ago but some people still having issues with verification code being sent and no way to verify the code on an account not confirmed yet so I found a solution that works for us.
Our auth flow is:
SignUp -> OTP Screen -> Confirmed OTP -> Cognito Account confirmed -> Custom email sent to user to verify email address -> Update attribute email_verified = true
On the OTP screen, we display the number OTP has been sent to, if it's the incorrect number, we allow the user to go back to signup page and change number and resubmit signup. We use a UUID for the user on cognito so as to allow a user to signup again without causing errors where account already exists but not confirmed.
This means we get two accounts with UUID in cognito, one being confirmed and one being unconfirmed with the only difference in the accounts is the phone number. We then get rid of unconfirmed accounts after a certain period. eg 7 days
For anybody else seeking the answer to this, what I ended up doing was writing a lambda that essentially checks if a user is unconfirmed, delete's the user and then signs them up again. I originally went the updateUserAttributes route but it felt insecure in case a bad actor got access to the lambda and updated a confirmed users phone number to theirs. If a user signups with a different username but the same number from a different account, it will invalidate the others users account. Hence the logic below.
try {
const userParams = {
UserPoolId: process.env.userpool_id,
Username: event.args.username
};
const { UserStatus } = await identity.adminGetUser(userParams).promise();
if (UserStatus === 'UNCONFIRMED') {
const deletedIdentity = await identity.adminDeleteUser(userParams).promise();
if (deletedIdentity) {
const signupParams = {
ClientId: process.env.client_id,
Password: event.args.password,
Username: event.args.password,
UserAttributes: [
{
Name: 'phone_number',
Value: event.args.phoneNumber
}
]
}
const newSignUp = await identity.signUp(signupParams).promise();
if (newSignUp) {
response.send(event, context, response.SUCCESS, {
newSignUp
});
callback(null, newSignUp)
}
}
} else {
response.send(event, context, response.ACCESSDENIED, {
error: 'User not authorized to perform this action'
});
callback({error: 'User not authorized to perform this action'}, null)
}
} catch (error) {
response.send(event, context, response.FAILURE, {
error
});
callback(error, null)
}

AWS Cognito Test Environment

I'm currently working on using AWS Cognito to manage user authentication for our application. One snag I'm running into is figuring out a good way to implement a "test" or "qa" environment.
I have a lot of automated tests for my APIs that will create users with random data. Obviously I don't want to Cognito to send out actual SMS or Email messages in this environment. Also when doing manual testing we will be creating users a lot with fake phone numbers and emails. Is there any way to turn the User Pool in "development" mode where all messages simply get logged some way?
You can write a pre sign up lambda function and auto confirm the user in the lambda function by setting the autoConfirmUser flag. In that case, Cognito doesn't send any SMSes or emails with confirmation codes. Example lambda below from the documentation (http://docs.aws.amazon.com/cognito/latest/developerguide/cognito-user-identity-pools-working-with-aws-lambda-triggers.html#aws-lambda-triggers-pre-registration-example).
exports.handler = function(event, context) {
// This Lambda function returns a flag to indicate if a user should be auto-confirmed.
// Perform any necessary validations.
// Impose a condition that the minimum length of the username of 5 is imposed on all user pools.
if (event.userName.length < 5) {
var error = new Error('failed!');
context.done(error, event);
}
// Access your resource which contains the list of emails of users who were invited to sign up
// Compare the list of email IDs from the request to the approved list
if(event.userPoolId === "yourSpecialUserPool") {
if (event.request.userAttributes.email in listOfEmailsInvited) {
event.response.autoConfirmUser = true;
}
}
// Return result to Cognito
context.done(null, event);
};
Here is what I did to create a "staging" environment User Pool in AWS Cognito that does not send real notifications to users. There were actually a couple different pieces involved, but I think I was able to get everything covered. That being said, it would sure be nice if Cognito simply provided a User Pool setting to turn off all notifications, that way I don't have to write environment specific logic into my code.
Prevent User Invitations
In our app we use the AdminCreateUser function to create users who get invited by other users. This function will normally send an invitation message to the new user's phone number or email. In order to prevent those invitations, you can provide MessageAction: 'SUPPRESS' as a parameter to the function arguments. Like so:
let params = {
UserPoolId: config.cognitoUserPoolId,
Username: uuid.v4(),
MessageAction: 'SUPPRESS', /* IMPORTANT! */
TemporaryPassword: user.phone_number.slice(-6),
UserAttributes: [
{ Name: 'given_name', Value: user.first_name },
{ Name: 'family_name', Value: user.last_name },
{ Name: 'phone_number', Value: user.phone_number }
]
};
cognito.adminCreateUser(params).promise().then(data => {
console.log(data);
});
Official docs for that here: http://docs.aws.amazon.com/cognito-user-identity-pools/latest/APIReference/API_AdminCreateUser.html
Prevent User Attribute Update Verfications
In our production app, we want users to have to re-verify their phone number or email if it changes. But in our staging environment we don't actually need this. So uncheck the boxes for email and phone under the "Do you want to require verification of emails or phone numbers?" section in your User Pool settings.