AWS SNS programmatically sending SMS is not delivered - amazon-web-services

We want our cloud application on AWS to send SMS messages to Indian phones. We were able to send messages in AWS Pinpoint Sandbox. We registered with TRAI and got our SenderID, PEID, TemplateID, message format, etc. Now, when I publish message using AWS sdk, it gives me no error. It gives me a message ID too. However, message is not delivered. Cloudwatch does not show any logs.
//Here is how session is created
sess, err := session.NewSession(&aws.Config{
Region: aws.String("ap-south-1"),
Credentials: credentials.NewStaticCredentials(myAWSID, myawssecretkey, mySession), //mySession is normally empty
})
That goes through with no error.
//Create a new sns client from the session above
snsSvc = sns.New(sess)
//setting message parameters based on AWS documentation,
params := &sns.PublishInput{
Message: aws.String(message),
PhoneNumber: aws.String(PhoneNumber),
MessageAttributes: map[string]*sns.MessageAttributeValue{
"AWS.SNS.SMS.SenderID": &sns.MessageAttributeValue{StringValue: aws.String(senderId), DataType: aws.String("String")},
"AWS.MM.SMS.TemplateId": &sns.MessageAttributeValue{StringValue: aws.String(templateId), DataType: aws.String("String")},
"AWS.MM.SMS.EntityId": &sns.MessageAttributeValue{StringValue: aws.String(peid), DataType: aws.String("String")},
"AWS.SNS.SMS.SMSType": &sns.MessageAttributeValue{StringValue: aws.String("Transactional"), DataType: aws.String("String")},
},
}
resp, err := snsSvc.Publish(params)
There is no error. I get a response from AWS as:
{ MessageId: "b781e287-4837-59b8-9edb-b90b05c3423b"}","time":"2022-09-02T18:30:12Z"}
Cloudwatch logs are always empty. Am I missing anything else above?

Related

Sending websocket message in Go lambda function

I have two Go functions:
func NewAPIGatewaySession() *apigatewaymanagementapi.ApiGatewayManagementApi {
sesh := session.Must(session.NewSessionWithOptions(session.Options{
SharedConfigState: session.SharedConfigEnable,
}))
return apigatewaymanagementapi.New(sesh)
}
and
func SendWsMessage(connectionID string, msgData []byte) error {
connectionInput := &apigatewaymanagementapi.PostToConnectionInput{
ConnectionId: aws.String(connectionID),
Data: msgData,
}
_, err := NewAPIGatewaySession().PostToConnection(connectionInput)
return err
}
But unfortunately I am getting the error:
RequestError: send request failed
caused by: Post "https://execute-api.us-east-1.amazonaws.com/#connections/GN5OCf-coAMCElw%3D": dial tcp: lookup execute-api.us-east-1.amazonaws.com on 169.254.78.1:53: no such host
This code is inside a Docker image lambda function. I am not sure if this is some sort of DNS error (but probably not if it has found out "169.254.78.1")?
Your lambda function needs to know what is the exact endpoint address of AWS Gateway management API. You can configure the address of the endpoint like this.
api := apigatewaymanagementapi.New(session)
api.Endpoint = "https://xxxxxxx.execute-api.eu-west-1.amazonaws.com/prod"
Endpoint address can be found in AWS console in API Gateway configuration under API -> Stage -> Connection URL.

Email not being sent / No errors AWS/SES

I have an AWS Lambda function:
sendEmail.js
const AWS = require('aws-sdk');
const ses = new AWS.SES();
function sendEmail(subject, message, senderEmail) {
const params = {
Destination: {
ToAddresses: [
process.env.VERIFIED_EMAIL
]
},
Message: {
Body: {
Text: {
Data: message,
Charset: 'UTF-8'
}
},
Subject: {
Data: subject,
Charset: 'UTF-8'
}
},
Source: process.env.VERIFIED_EMAIL,
ReplyToAddresses: [senderEmail]
}
return ses.sendEmail(params).promise();
}
module.exports = sendEmail;
Called from
index.js
const sendEmail = require('./sendEmail');
exports.handler = async (event, context) => {
return sendEmail(event.subject, event.message, event.email).then((response) => { context.done(null, 'Email sent')});
};
I have the env. variable VERIFIED_EMAIL set to a personal e-mail that is verified by AWS/SES.
My test case:
{
"email": "redacted#outlook.com",
"subject": "desc",
"message": "Hello!"
}
Which passes and returns "Email sent" but I don't actually receive an e-mail. I've also deployed the API-GATEWAY and I can call the API w/ Postman and I receive the same "Email sent" message but no e-mail is actually sent.
I don't think it should be an issue with being in sandbox mode because the email I am sending it from is verified.
PS:
When Looking at the SES management console it says that the emails are being sent (they take up part of the 200 daily quota) and then that none of them were bounced or rejected but simply deliveries.
A few things you should check with your SES before diving deeper.
In sandbox mode both "source email address" and "destination email address" have to be verified. Instead a mail won't be delivered.
In case you verify Email Domain so appropriate dns and DKIM records have to be added in your domain. Plus additional whitelist clearance is assumed if you use corporate domains.
Your IAM AWS user should be permitted to execute SES api calls. Check your policies and roles. Check Secret Key and Secret Id you use.
There might be problems when you use inappropriate email types and email sending endpoints (eg you try to send "SingleTemplatedMessage" via "BulkTemplatedMessage" endpoint)
Check all this things first.
Then you might try something else. In my project we use AWS SDK based on java to interact between BE and SES. It provides logs containing message statuses (if a message was sent successfully, rejected, its id and other errors if they occurred)
Additionally to keep track on every single message you send you can set up SES-SNS to exchange with notifications. It's described in the following article
https://sysgears.com/articles/how-to-track-email-status-with-amazon-ses-and-amazon-sns/
Other problems might occur in your mail client (spam filters etc)
Follow the check list to troubleshoot SES email sending:
Check the SMTP credential is ok or not using code shared by AWS
Same credential using in multiple application/IP may not work
Use minimum mail configuration, if you do not know setting value remove it, do not leave it with NULL.
Set, $config['charset'] = 'UTF-8'; // // 'UTF-8', 'ISO-8859-15'
Set, $config['newline'] = "\r\n"; // "\r\n" or "\n" or "\r"
Additional Check
Make sure trying to send email to the same domain if SES account is Sandbox
Check all outbound with mail TCP port is open
If using NATgateway Check both inbound and outbound with mail TCP port is open in open for the mail sending instance in it.

AWS Cognito InitiateAuth through Lambda function results into error

I am writing a Lambda function in Go to authenticate a user, the AccessToken/IdToken I want to use for subsequent API calls.
When I execute the Go code from a standalone program, it works, the InitiateAuth was successful.
When I tried to use the same code from the lambda function, I get an error NotAuthorizedException: Unable to verify secret hash for client .......
Here is the code snippet I am using
func AuthenticateUser(userName string, passWord string) (*cognitoidentityprovider.InitiateAuthOutput, error) {
username := aws.String(userName)
password := aws.String(passWord)
clientID := aws.String(constants.COGNITO_APP_CLIENT_ID)
params := &cognitoidentityprovider.InitiateAuthInput{
AuthFlow: aws.String("USER_PASSWORD_AUTH"),
AuthParameters: map[string]*string{
"USERNAME": username,
"PASSWORD": password,
},
ClientId: clientID,
}
authResponse, authError := cognitoClient.InitiateAuth(params)
if authError != nil {
fmt.Println("Error = ", authError)
return nil, authError
}
fmt.Println(authResponse)
fmt.Println(*authResponse.Session)
return authResponse, nil
}
I have given sufficient permissions to the lambda user
- cognito-idp:AdminCreateUser
- cognito-idp:AdminDeleteUser
- cognito-idp:InitiateAuth
- cognito-idp:ChangePassword
- cognito-idp:AdminRespondToAuthChallenge
- cognito-idp:AdminInitiateAuth
- cognito-idp:ConfirmForgotPassword
Am I missing something here?
When we create a new App client, by default it has an associated App client secret.
I created one more app client, without "Client Secret". I used this new App client.
I modified the code to use the API AdminInitiateAuth, instead of the InitiateAuth
I was able to successfully login.
Here is the reference link, which was useful - Amplify "Unable to verify secret hash for client"

Calling AppSync Mutation from Lambda with Golang

I'm trying to invoke a mutation from lambda (specifically using golang). I used AWS_IAM as the authentication method of my AppSync API. I also give appsync:GraphQL permission to my lambda.
However, after looking at the docs here: https://docs.aws.amazon.com/sdk-for-go/api/service/appsync/
I can't find any documentation on how to invoke the appsync from the library. Can anyone point me to the right direction here?
P.S. I don't want to query or subscribe or anything else from lambda. It's just a mutation
Thanks!
------UPDATE-------
Thanks to #thomasmichaelwallace for informing me to use https://godoc.org/github.com/machinebox/graphql
Now the problem is how can I sign the request from that package using aws v4?
I found a way of using plain http.Request and AWS v4 signing. (Thanks to #thomasmichaelwallace for pointing this method out)
client := new(http.Client)
// construct the query
query := AppSyncPublish{
Query: `
mutation ($userid: ID!) {
publishMessage(
userid: $userid
){
userid
}
}
`,
Variables: PublishInput{
UserID: "wow",
},
}
b, err := json.Marshal(&query)
if err != nil {
fmt.Println(err)
}
// construct the request object
req, err := http.NewRequest("POST", os.Getenv("APPSYNC_URL"), bytes.NewReader(b))
if err != nil {
fmt.Println(err)
}
req.Header.Set("Content-Type", "application/json")
// get aws credential
config := aws.Config{
Region: aws.String(os.Getenv("AWS_REGION")),
}
sess := session.Must(session.NewSession(&config))
//sign the request
signer := v4.NewSigner(sess.Config.Credentials)
signer.Sign(req, bytes.NewReader(b), "appsync", "ap-southeast-1", time.Now())
//FIRE!!
response, _ := client.Do(req)
//print the response
buf := new(bytes.Buffer)
buf.ReadFrom(response.Body)
newStr := buf.String()
fmt.Printf(newStr)
The problem is that that API/library is designed to help you create/update app-sync instances.
If you want to actually invoke them then you need to POST to the GraphQL endpoint.
The easiest way for testing is to sign-in to the AWS AppSync Console, press the 'Queries' button in the sidebar and then enter and run your mutation.
I'm not great with go, but from what I can see there are client libraries for GraphQL in golang (e.g. https://godoc.org/github.com/machinebox/graphql).
If you are using IAM then you'll need to sign your request with a v4 signature (see this article for details: https://docs.aws.amazon.com/general/latest/gr/signing_aws_api_requests.html)

boto3 - aws sns - specify Sender ID

The following code works to send a message but when it arrives, it displays the text 'VERIFY' for a sender id. How do I specific a sender ID? I think it's done with the message attributes but I cannot figure out the syntax.
session = boto3.session.Session(profile_name='Credentials',region_name='us-east-1')
theMessage='Now is the time for all good people to come to the aid of their party'
senderID='Godzilla'
snsclient = session.client('sns')
response = snsclient.publish(PhoneNumber='+84932575571', Message=theMessage)
pp = pprint.PrettyPrinter(indent=4)
print(pp.pprint(response))
Add a third parameter MessageAttributes to the publish method.
snsclient.publish(PhoneNumber='+84932575571', Message=theMessage,MessageAttributes={
'AWS.SNS.SMS.SenderID': {
'DataType': 'String',
'StringValue': 'Godzilla'
}})
The sender id is not supported in many countries. see AWS SNS SMS SenderId Supported Countries