Can't resend verification code through AWS Cognito API - amazon-web-services

I have an AWS Cognito User Pool where users are created through Cognito's API using the AdminCreateUser action, which works fine. This sends out a verification e-mail to the user, containing a temporary password. So far so good.
Now a user did not receive this verification e-mail, so I need to send it again, using the ResendConfirmationCode action. I am attempting to do that with the below PHP code.
$userPoolId = '[POOL_ID_HERE]';
$backendAppId = '[APP_ID_HERE]';
$clientSecret = '[SECRET_HERE]';
$username = '[UUID_HERE]';
$secretHash = base64_encode(hash_hmac('sha256', $username . $backendAppId, $clientSecret, true));
$cognitoIdp->resendConfirmationCode([
'ClientId' => $backendAppId,
'SecretHash' => $secretHash,
'Username' => $username,
]);
That gives me the following error:
Aws/CognitoIdentityProvider/Exception/CognitoIdentityProviderException
with message 'Error executing "ResendConfirmationCode" on
"https://cognito-idp.eu-central-1.amazonaws.com"; AWS HTTP error:
Client error: POST https://cognito-idp.eu-central-1.amazonaws.com
resulted in a 400 Bad Request response:
{"__type":"NotAuthorizedException","message":"Can't resend
confirmation code for this user"} NotAuthorizedException (client):
Can't resend confirmation code for this user -
{"__type":"NotAuthorizedException","message":"Can't resend
confirmation code for this user"}'
I am using the credentials of a user which has the following IAM permissions for the user pool:
cognito-idp:AdminDeleteUser
cognito-idp:AdminCreateUser
cognito-idp:AdminAddUserToGroup
cognito-idp:ResendConfirmationCode
If I test the permissions using the IAM Policy Simulator, it gives me the green light, saying that everything is OK. To my knowledge, the cognito-idp:ResendConfirmationCode action should be sufficient, as sending out the verification e-mail works fine when creating the user.
What am I doing wrong here? An alternative approach would be to invoke the AdminCreateUser action again, setting the MessageAction parameter to RESEND. This would force the verification e-mail to be resent for existing users, but I prefer using the ResendConfirmationCode action if I can get it to work.
Any ideas? Thanks!

I understand that you would like your web application end users to receive a confirmation code again if they do not get the confirmation code due to some reason after they sign-up, and I also understand that you are getting the "NotAuthorizedException" when you are trying to run the ResendConfirmationCode API call from your code that uses the PHP SDK.
The ResendConfirmationCode API call[1] can be used after the sign-up API call[2], and it is not a part of the AdminCreateUser Authentication flow, which is why the error is thrown. The AdminCreateUser API call changes the status of the new user to the "Force Change Password State", and neither the ResendConfirmationCode call or the ForgotPassword call can work after AdminCreateUser is used for creating a new user.
If you would like your end-users to get the confirmation code again, you could use the AdminCreateUser API call itself and set the "RESEND" flag in MessageAction in the PHP code. There would be no other way to send the verification message again in this particular use-case, as per my understanding of Amazon Cognito.
An example of the API call in PHP according to the official documentation is as follows[3]:
$result = $client->adminCreateUser([
'DesiredDeliveryMediums' => ['<string>', ...],
'ForceAliasCreation' => true || false,
'MessageAction' => 'RESEND|SUPPRESS',
'TemporaryPassword' => '<string>',
'UserAttributes' => [
[
'Name' => '<string>', // REQUIRED
'Value' => '<string>',
],
// ...
],
'UserPoolId' => '<string>', // REQUIRED
'Username' => '<string>', // REQUIRED
'ValidationData' => [
[
'Name' => '<string>', // REQUIRED
'Value' => '<string>',
],
// ...
],
]);
After using setting 'MessageAction' as 'RESEND', the end-users should be able to receive the verification message again on their end.
References
[1]. https://docs.aws.amazon.com/cli/latest/reference/cognito-idp/resend-confirmation-code.html
[2]. https://docs.aws.amazon.com/cli/latest/reference/cognito-idp/sign-up.html
[3]. https://docs.aws.amazon.com/aws-sdk-php/v3/api/api-cognito-idp-2016-04-18.html#admincreateuser

Related

AWS Cognito Reset Password Verify Code then Reset Password

Is there any way using AWS Cognito to send the user their verification code, have them enter the code, verify it is a valid code, THEN have them set their username and password?
For some reason, the workflow in my mind seems strange for the user to enter their code and new password in the same step. I want to check their code, and if it is valid, then take them to the screen to reset their password.
So far I've used the API function call:
forgotPassword
To send the code, which works fine, and from all my reading of the docs and searching here and online, I see that the next step is to call:
confirmForgotPassword
But in this step, it requires the new password (from what I can tell from the documentation):
$result = $client->confirmForgotPassword([
'AnalyticsMetadata' => [
'AnalyticsEndpointId' => '<string>',
],
'ClientId' => '<string>', // REQUIRED
'ClientMetadata' => ['<string>', ...],
'ConfirmationCode' => '<string>', // REQUIRED
'Password' => '<string>', // REQUIRED
'SecretHash' => '<string>',
'UserContextData' => [
'EncodedData' => '<string>',
],
'Username' => '<string>', // REQUIRED
]);
Am I missing something?
For context, I'm using the PHP API, but I'm really just looking for the correct API calls in the correct order to accomplish what I'd like if it is even possible.
Thanks in advance.
I changed my workflow so that I used a lambda function in AWS to send a link with the code, this way I validate the code on the link click and then the user can enter their password in.
Doesn't seem possible to do this in two steps.

Paws::CognitoIdp::GetUser returns "Invalid Access Token"

Summary:
I tried to obtain AWS Cognito User name (OAuth2 from Google) with Perl module Paws::CognitoIdp::GetUser but I failed the code:
my $GetUserResponse = $cognito_idp->GetUser(
AccessToken => 'MyTokenModelType',
);
with the error Invalid Access Token.
Environment
I am developing Web service with following environment:
AWS + EC2 + Ubuntu 20.04 + nginx 1.20.1 + Perl 5.30.0
Using Cognito with Google OAuth2.0
What I can do now
When you access my Web service, Sign In with Google button will appear. If you click it, you can run my Perl CGI script.
What I cannot do now
I want to obtain your Google user name and mail address, but I cannot.
What I did
I wrote the following code:
my $cognito_idp = Paws->service('CognitoIdp',
region => "ap-northeast-1",
max_attempts => 3,
);
my $GetUserResponse = $cognito_idp->GetUser(
AccessToken => 'MyTokenModelType',
);
my $uname = $GetUserResponse->Username;
Then...
When I access my Web service, it failed with the Software error:
Software error:
Invalid Access Token
Trace begun at /usr/local/share/perl/5.30.0/Paws/Net/JsonResponse.pm line 22
Paws::Net::JsonResponse::process('Paws::Net::JsonResponse=HASH(0x56495fb33d78)', 'Paws::CognitoIdp::GetUser=HASH(0x56495fb41fd0)', 'Paws::Net::APIResponse=HASH(0x56495fde6f38)') called at /usr/local/share/perl/5.30.0/Paws/Net/Caller.pm line 46
Paws::Net::Caller::caller_to_response('Paws::Net::Caller=HASH(0x56495da09a38)', 'Paws::CognitoIdp=HASH(0x56495e05da48)', 'Paws::CognitoIdp::GetUser=HASH(0x56495fb41fd0)', 'Paws::Net::APIResponse=HASH(0x56495fde6f38)') called at /usr/local/share/perl/5.30.0/Paws/Net/RetryCallerRole.pm line 19
Paws::Net::RetryCallerRole::do_call('Paws::Net::Caller=HASH(0x56495da09a38)', 'Paws::CognitoIdp=HASH(0x56495e05da48)', 'Paws::CognitoIdp::GetUser=HASH(0x56495fb41fd0)') called at /usr/local/share/perl/5.30.0/Paws/CognitoIdp.pm line 331
Paws::CognitoIdp::GetUser('Paws::CognitoIdp=HASH(0x56495e05da48)', 'AccessToken', 'MyTokenModelType') called at /DocumentRoot/index.cgi line 16
For help, please send mail to this site's webmaster, giving this error message and the time and date of the error.
Maybe...
I referred following to the documentation
Maybe 'MyTokenModelType' is just the placeholder and I should specify the correct code.
But I don't know how I can make it.
In addition, I want to know how can I obtain not only user name but also user email address.
(Additional Info 2021-11-12)
Thanks to the comment, I read the article:
How to generate access token for an AWS Cognito user?
and I hacked AWS CLI command but I have not got the answer yet.
I think I got to do $ aws connito-idp initiate-auth but I cannot find what arguments I should pass.
In the AWS => Cognito => Users and groups menu, I see the entry of a user of my Web app.
Username: google_??????????
Enabled: Enabled
Account status: EXTERNAL_PROVIDER
Email: (users_email)
I changed my code like this for the experiment to find what information my Web app has.
use Data::Dumper;
my $cognito_idp = Paws->service('CognitoIdp',
region => "ap-northeast-1",
max_attempts => 3,
);
# my $GetUserResponse = $cognito_idp->GetUser(
# AccessToken => 'MyTokenModelType',
# );
# my $uname = $GetUserResponse->Username;
print "Content-type: text/html\n\n\n";
print '<!DOCTYPE html>';
print '<html><pre>';
print Dumper $cognito_idp;
print '</pre></html>';
and I see the contents of $cognito_idp like this:
$VAR1 = bless( {
'_region_for_signature' => 'ap-northeast-1',
...
'credentials' => bless( {
...
'selected_provider' => bless( {
'expiration' => 1636712506,
'actual_creds' => {
'Type' => 'AWS-HMAC',
'SecretAccessKey' => '??????????????',
'Code' => 'Success',
'AccessKeyId' => '???????????????',
'LastUpdated' => '2021-11-12T03:54:16Z',
'Token' => '?????????????????????????????????????',
'Expiration' => '2021-11-12T10:21:46Z'
},
...
I hope I could get the information of the user from the data above.
My question is, how I can specify the MyTokenModelType value of the code:
my $GetUserResponse = $cognito_idp->GetUser(
AccessToken => 'MyTokenModelType',
);
Still I want your info.
Thanks again.

Customize email verification page AWS Cognito

I am using Amazon Cognito for user authentication. After the user is registered verification email is sent to his email address. After clicking on the email link he is prompted with this in his browser.
How can I customize this page in order to insert a script that will trigger deep links within the mobile application, and also make the page look bit nicer?
You can do that using Cognito triggers.
You can configure a trigger template to define a message with a link to a page you control.
The assets will be stored at: amplify/backend/auth/<your-resource-name>CustomMessage/assets
The documentation has more details
Cognito allows you to configure your User Pool to send an email to
your users when they attempt to register an account. You can configure
this email to contain a link to Cognito’s Hosted UI where the user’s
account will be marked as confirmed.
This trigger template allows you to define an email message with a
link to a static S3 bucket that you control, where the user’s account
will be confirmed and they can then be redirected to a URL of your
choice (presumably your application). The URL will automatically
contain the username as a query string parameters.
Please note that this trigger template will create an S3 resource. The
files that populate the static site are available for edit in
amplify/backend/auth/CustomMessage/assets. They
consist of:
index.html
spinner.js (controls the spinner that appears on the page while users are awaiting confirmation)
style.css
verify.js (the script which performs the verification request)
I was not able to customize the verification page provided by AWS. I created my own UI on my page, which sent the generated code to cognito for verification. For that I needed to:
trigger custom email upon registration
put custom link to verification in the email using the codes provided for the lambda
process the codes on my page
send the codes and username through aws package
Step 1.
In AWS Cognito User Pool, customize workflow with triggers, choose "Custom Message". The triggerSource for verification that I check for are:
event.triggerSource === 'CustomMessage_SignUp' || event.triggerSource === 'CustomMessage_ResendCode'
You can see other trigger sources for CustomMessage here: https://docs.aws.amazon.com/cognito/latest/developerguide/user-pool-lambda-custom-message.html
Step 2. The lambda provides parameters for verification for my users: event.request.userAttributes.sub and event.request.codeParameter. Using these to I constructed a link to my page like so:
https://mypage.com?user_name=${event.request.userAttributes.sub}&confirmation_code=${event.request.codeParameter}
Step 3. On my page, I check if the url params for user_name and confirmation_code are present, and display a modal which is supposed to inform the user if the verification went correctly or not.
Using a package "amazon-cognito-identity-js" I process the code and user_name. First I create the user pool:
import { CognitoUserPool } from 'amazon-cognito-identity-js';
//Aws-cognito credentials
const poolData = {
UserPoolId: YOUR_USERPOOL_ID,
ClientId: YOUR_CLIENT_ID,
};
export default new CognitoUserPool(poolData);
Then to process the code I create a user instance:
import { CognitoUser } from 'amazon-cognito-identity-js';
import UserPool from 'utils/UserPool';
const getUser = () => {
return new CognitoUser({
Username: user_name.toLowerCase(),
Pool: UserPool,
});
};
// After that you can process the code:
getUser().confirmRegistration(code, false, function (err, result) {
if (err) {
if (
err.message === 'User cannot be confirmed. Current status is CONFIRMED'
) {
// Handle already confirmed error
} else {
// Handle other errors you want
}
}
// Handle successful verification
});
The account is verified and you can guide the user to the login page or any other.

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.

Can't Authenticate through Facebook PHP SDK

I currently force users to authenticate through Facebook each time they start a new session with my site. This means I force users to hit the url obtained through $facebook->getLoginURL() each time the browser is restarted.
When a user completes the login process, they are redirected to my login script:
{checks for CSRF token and error status...}
$facebook = new Facebook(array(
'appId' => '{my app id}',
'secret' => '{my app secret}',
'cookie' => true
));
//Get user info
$user = $facebook->getUser();
if($user)
echo 'user found';
else
echo 'no user!';
$user always results in 0; I am getting the expected 'state' variable and no errors in the API response. Also, I see that the relevant App permissions exist on my profile. However, $facebook->getUser() still returns 0. Is there some intermediate step that I'm missing (am I expected to do anything with the user's auth token?)
I know there are many threads on this problem but haven't found any resolutions. Also, I think this is a very basic example so I'm hoping the answer here will be more useful for future users dealing with the problem. Thanks!