AWS Cognito - Users lost "non-mutable" attribute "email_verified" - amazon-web-services

After using Cognito for a few months, some users in a user pool have now lost the "email_verified" attribute. I can't understand how it is missing or how to recover.
Symptoms are:
Users can still login
User password can not change (eg via JS SDK - changePassword), produces error: "x-amzn-errormessage: Cannot reset password for the user as there is no registered/verified email or phone_number"
Getting the user attributes for the user with the list-users CLI shows the attribute is missing
aws cognito-idp list-users --user-pool-id MYID-123 --query 'Users[?Username==`error#bla.com`].[*]'
[
[
[
"error#bla.com",
true,
"CONFIRMED",
1522127817.526,
1522127819.369,
[
{
"Name": "sub",
"Value": "123123123341241238"
},
{
"Name": "email",
"Value": "bla#bla.com"
}
]
]
]
]
vs. one with the attribute in place
aws cognito-idp list-users --user-pool-id MYID-123 --query 'Users[?Username==`bla#bla.com`].[*]'
[
[
[
"bla#bla.com",
true,
"CONFIRMED",
1524048734.588,
1524048737.777,
[
{
"Name": "sub",
"Value": "1231231231231235"
},
{
"Name": "email_verified",
"Value": "true"
},
{
"Name": "email",
"Value": "bla#bla.com"
}
]
]
]
]
If I try deleting the attribute (with enough permissions), it fails - as one would expect - explaining it is not mutable.
aws cognito-idp admin-delete-user-attributes --user-pool-id MYID-123 --username test2#test.com --user-attribute-names email_verified
An error occurred (InvalidParameterException) when calling the AdminDeleteUserAttributes operation: Cannot modify the non-mutable attribute email_verified

I can not find the cause for this problem, other than blaming AWS Cognito.
A workaround/hack/patch is to add the attribute back, this time, the non-mutable check is not a problem
aws cognito-idp admin-update-user-attributes --user-pool-id MYID-123 --username error#bla.com --user-attributes Name=email_verified,Value=true
And now the user has the attribute again and I can reset the password.

If there are 2 users with same email address, and email_verified is true for one and not the other, it is possible it is an issue with your client code.
When you call confirmRegistration the first parameter is the confirmation code and the second is a boolean: forceAliasCreation. If set to true, then if a user already exists with the email address that is being used to register with, the new user "steals" the email address of the existing user.
It's not obvious this is a problem cause the Cognito API docs show examples of confirmRegistration with forceAliasCreation as true and not explaining what the parameter does (https://github.com/aws-amplify/amplify-js/tree/master/packages/amazon-cognito-identity-js - Use Case 2, assuming you are using JS). We ran into this problem with our app and this was the culprit.

Related

aws ssm session - how do I set "runAsDefaultuser" to be a interactive parameter when starting a session

I'm trying to create a document that I call and specific my IAM user as the user to connect with. This document will be a shared document that has a parameter "runAsDefaultuser". It will default to a user that doesn't exist. This will force me to provide an argument like,
--parameters '{"runAsDefaultUser": ["joeschmo"]}'.
My end goal is to allow users to login in as their IAM user via CLI through SSM. I've tried using tag's in the IAM user account. That only works when using a SSM session over the Web UI in the AWS Session Manager page. Doesn't work vi SSM CLI. The SSM documents override this.
I can't set this as a parameter. It will only accept it as a hardcoded value. Same with "runAsEnabled".
I get this error when I try to set it as a parameter using the "aws ssm update-document or create-document command.
An error occurred (InvalidDocumentContent) when calling the UpdateDocument operation: DefaultUser: {{runAsDefaultUser}} is invalid
I'm using this page as a reference.
https://docs.aws.amazon.com/systems-manager/latest/userguide/getting-started-create-preferences-cli.html
Here is the example document that I am using.
{
"schemaVersion":"1.0",
"description":"Session Document Parameter Example JSON Template",
"sessionType":"Standard_Stream",
"parameters":{
"s3BucketName":{
"type":"String",
"default":""
},
"s3KeyPrefix":{
"type":"String",
"default":""
},
"s3EncryptionEnabled":{
"type":"String",
"default":"false"
},
"cloudWatchLogGroupName":{
"type":"String",
"default":""
},
"cloudWatchEncryptionEnabled":{
"type":"String",
"default":"false"
},
"runAsDefaultUser":{
"type":"String",
"default":"nobody"
}
},
"inputs":{
"s3BucketName":"{{s3BucketName}}",
"s3KeyPrefix":"{{s3KeyPrefix}}",
"s3EncryptionEnabled":"{{s3EncryptionEnabled}}",
"cloudWatchLogGroupName":"{{cloudWatchLogGroupName}}",
"cloudWatchEncryptionEnabled":"{{cloudWatchEncryptionEnabled}}",
"kmsKeyId":"",
"runAsEnabled": true
"runAsDefaultUser":"{{runAsDefaultUser}}",
"shellProfile": {
"windows": "",
"linux": "bash"
}
}
}
The command that I want to use:
aws ssm start-session --target i-ThisIsObviouslyMadeUp \
--document-name Custom-SessionManagerRunShell \
--parameters '{"runAsDefaultUser": ["joeschmo"]}'

How to get AWS email address for a user?

In these docs:
https://docs.aws.amazon.com/cli/latest/reference/iam/get-user.html
we can get a user with:
aws iam get-user --user-name Bob
and the result is:
{
"User": {
"UserName": "Bob",
"Path": "/",
"CreateDate": "2012-09-21T23:03:13Z",
"UserId": "AKIAIOSFODNN7EXAMPLE",
"Arn": "arn:aws:iam::123456789012:user/Bob"
}
}
but how do we get this user's email address?
IAM users do not have an email address property. Are you sure you're referring to IAM users?
The root user for your AWS account will have an email address associated with it, but there is only one of these users.
Also, if you have a Cognito user pool, these users can have an email address associated with them.
https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-iam-user.html
https://docs.aws.amazon.com/cognito/latest/developerguide/cognito-user-identity-pools.html

Backing up API Keys for recovery

I am designing and implementing a backup plan to restore my client API keys. How to go about this ?
To fasten the recovery process, I am trying to create a backup plan for taking the backup of Client API keys, probably in s3 or local. I am scratching my head from past 2 days on how to achieve this ? May be some python script or something which will take the values from apigateway and dump into some new s3 bucket. But not sure how to implement this.
You can get all apigateway API keys list using apigateway get-api-keys. Here is the full AWS CLI command.
aws apigateway get-api-keys --include-values
Remember --include-values is must to use otherwise actual API Key will not be included in the result.
It will display the result in the below format.
"items": [
{
"id": "j90yk1111",
"value": "AAAAAAAABBBBBBBBBBBCCCCCCCCCC",
"name": "MyKey1",
"description": "My Key1",
"enabled": true,
"createdDate": 1528350587,
"lastUpdatedDate": 1528352704,
"stageKeys": []
},
{
"id": "rqi9xxxxx",
"value": "Kw6Oqo91nv5g5K7rrrrrrrrrrrrrrr",
"name": "MyKey2",
"description": "My Key 2",
"enabled": true,
"createdDate": 1528406927,
"lastUpdatedDate": 1528406927,
"stageKeys": []
},
{
"id": "lse3o7xxxx",
"value": "VGUfTNfM7v9uysBDrU1Pxxxxxx",
"name": "MyKey3",
"description": "My Key 3",
"enabled": true,
"createdDate": 1528406609,
"lastUpdatedDate": 1528406609,
"stageKeys": []
}
}
]
To get API Key detail of a single API Key, use below AWS CLI command.
aws apigateway get-api-key --include-value --api-key lse3o7xxxx
It should display the below result.
{
"id": "lse3o7xxxx",
"value": "VGUfTNfM7v9uysBDrU1Pxxxxxx",
"name": "MyKey3",
"description": "My Key 3",
"enabled": true,
"createdDate": 1528406609,
"lastUpdatedDate": 1528406609,
"stageKeys": []
}
Similar to get-api-keys call, --include-value is must here, otherwise actual API Key will not be included in the result
Now you need to convert the output in a format which can be saved on s3 and later can be imported to apigateway.
You can import keys with import-api-keys
aws apigateway import-api-keys --body <value> --format <value>
--body (blob)
The payload of the POST request to import API keys. For the payload
format
--format (string)
A query parameter to specify the input format to imported API keys.
Currently, only the CSV format is supported. --format csv
Simplest style is with two fields only e.g Key,name
Key,name
apikey1234abcdefghij0123456789,MyFirstApiKey
You can see the full detail of formats from API Gateway API Key File Format.
I have implemented it in python using a lambda for backing up APIs keys. Used boto3 APIs similar to the above answer.
However, I am looking for a way to trigger the lambda with an event of "API key added/removed" :-)

AWS Cognito - MFA setup

I use the JavaScript AWS SDK for MFA setup and have 2 issues:
First, I update phone number (phone_number attribute with updateUserAttributes method).
It updates but returns empty object instead of (according to docs):
{
"CodeDeliveryDetailsList": [
{
"AttributeName": "string",
"DeliveryMedium": "string",
"Destination": "string"
}
]
}
Second, I am trying to send user a verification code with getAttributeVerificationCode with the following payload:
const params = {
AccessToken: auth.accessToken,
AttributeName: 'phone_number'
}
and I am getting
CustomMessage failed with error
Cannot read property identity of undefined
as error. Any ideas?
For the ones who come across same problem, we could solve it by using cognitoUser.updateAttributes instead of cognitoidentityserviceprovider.updateUserAttributes contrary to the official docs. AWS, especially cognito is premature and lack of docs is another issue for the ones who are considering to use.
AWS is expecting strings, maybe the token is getting passed incorrectly here...
const params = {
"AccessToken": auth.accessToken.toString(),
"UserAttributes": [
{
"Name": "phone number",
"Value": "(555)555-5555"
}
]
}
And here...
const params = {
"AccessToken": auth.accessToken.toString(),
"AttributeName": "phone number"
}

How pass json value for admin-update-user-attributes operation via cli in aws?

Consider the example:
aws cognito-idp admin-update-user-attributes --user-pool-id myUserPollId
--username myUser
--user-attributes [{"Name": "custom:roles","Value": "ROLE1,ROLE2"}] --region us-east-1
This gets me error:
Invalid JSON:
[{Name:
You can always try using shorthand syntax:
--user-attributes Name="custom:roles",Value="ROLE1,ROLE2"
If you really want to use the JSON syntax, try this:
--user-attributes '[{"Name" : "custom:roles","Value" : "ROLE1,ROLE2"}]'
Ensure that the user-attributes list is enclosed in single quotes
--user-attributes '[{"Name": "phone_number", "Value": "+123434532"},
{"Name": "name", "Value":"name_your"}]'
In case someone get stuck in the same problem again, below are the tested steps to have user attributes updated via aws cli with json file.
Step 0: Setup AWS CLI in case you haven't already. Mac users can run:
brew install awscli
Step 1: Have a valid json handy with you, saved in a file. Sample json with valid format:
{
"UserAttributes": [{
"Name": "custom:additional-attribute1",
"Value": "Value for additional attribute 1"
},
{
"Name": "custom:additional-attribute2",
"Value": "Value for additional attribute 2"
}
]
}
Step 2: Run the following in your console:
aws cognito-idp admin-update-user-attributes --user-pool-id XX-XXXX-X_XXXXXXXXX --username XXXXX#XXXXX.com --cli-input-json file:///Users/YOUR_PATH_TO_THE_FILE/user-attributes.json
Parameters:
--user-pool-id :: Your user pool ID.
--username :: The user you want to udpate.
--cli-input-json :: This is the command that loads json file and parses it.
That's it. If your json is valid and aws cli authorises, the given user record should be updated instantly.