I want to automate the process of authorize email addresses to send mails through the Amazon SES service in javascript/Node.JS.
We are already sending mails via Node and that is working fine, but I can't get this working.
What I so far:
function verifyEmail(email, callback){
var sesService = new AWS.SES({
accessKeyId: "mykey",
secretAccessKey: 'myaccesskey',
region: 'eu-west-1'
});
sesService.verifyEmailIdentity({EmailAddress: email}, function(err, data){
console.log("verifyEmailIdentity", err, data);
return callback(err, data);
})
}
In the log I get this:
verifyEmailIdentity null
Object {ResponseMetadata: Object}
The ResponseMetadata object contains RequestId: "some-string"
So, I didn't get back an error but I didn't also receive an e-mail to confirm this request.
The key have the AmazonSESFullAccess policy added, so there are sufficient permissions to complete this.
I guess I forgot something very simple, but bases on the docs from Amazon I can't find what. And there aren't many examples for javascript is this, so I can't compare my code with others.
Still no idea why this wasn't working but now it works.
We had already plans to create an custom verify template (https://docs.aws.amazon.com/ses/latest/DeveloperGuide/custom-verification-emails.html) and I created an template while waiting on an answer using the CLI.
For using this template we must use an the sendCustomVerificationEmail() function from AWS.
And guess what? With that function the mail is send, and I'm able to verify the requested email address!
The verifyMail function now looks like:
function verifyEmail(email, callback){
sesService.sendCustomVerificationEmail({
EmailAddress: email,
TemplateName: 'MyTemplateName'
},function(err, data){
console.log("verifyEmailIdentity", err, data);
return callback(err, data);
})
}
Related
I am new to nodejs and working on a proof of concept just for fun.
Background:
I have a cloud directory of user information (like username, password and other info). This cloud directory can be used to authenticate a user only via restful API (i.e. no direct connectivity using LDAP or JDBC etc.).
Aim:
To build an LDAP interface for this cloud directory. To start with I am interested only in authentication (LDAP bind).
Intended Flow:
LDAPClient initiates a standard LDAP simple BIND request:
Host: host where my nodejs app will run
Port: 1389 (port that my nodejs app will be bound to)
Username: a user from cloud directory
Password: user's password
This request is received by my NodeJS app (I am using ldapjs module).
// process ldap bind operation
myLdapServer.bind(searchBase, function (bindReq, bindRes, next) {
// bind creds
var userDn = req.dn.toString();
var userPw = req.credentials;
console.log('bind DN: ' + req.dn.toString());
...
...
}
Within the above callback, I must use http.request to fire a restful API (POST) to the cloud directory with the details I received from the BIND request (i.e. username, password).
If restful api response status is 200 (auth success), then I must return success to the LDAPClient, else I must return invalid credentials error.
Success:
bindRes.end();
return next();
Failure:
Console.log("returning error");
return next(new ldap.InvalidCredentialsError());
Questions:
Is this possible using NodeJS? Asking because of the nesting involved as evident above (calling of REST API from within a callback). Also since this is an authentication operation, this is meant to be a blocking operation(?)
Thanks,
Jatin
UPDATE:
Thanks Klvs, my solution is more or less like the one you posted. Please have a look at the snippet below:
// do the POST call from within callback
var postRequest = https.request(postOptions, function(postResponse) {
console.log("statusCode: ", postResponse.statusCode);
if(postResponse.statusCode!=200) {
console.log("cloud authentication failed: "+postResponse.statusCode);
return next(ldapModule.InvalidCredentialsError());
} else {
postResponse.on('data', function(d) {
console.info('POST result:\n');
process.stdout.write(d);
console.info('\n\nPOST completed');
});
res.end();
return next();
}
});
// write json data
postRequest.write(postData);
postRequest.end();
postRequest.on('error', function(e) {
console.error("postRequest error occured: "+e);
});
Successful authentication works fine, however, failed authentication does not send any response back to the LDAPClient at all. My client just times out instead of showing authentication failure error. I do see the "cloud authentication failed: " log message on the Node console, which means the below statement is not doing what I intend it do:
return next(ldapModule.InvalidCredentialsError());
Note that the above statement works when I remove the rest call etc, and just return the error back to the client.
Am I missing something?
Thanks,
Jatin
Of course it's possible in nodejs. If I understand you want to make an authenticating request to a server and have it either fail or succeed.
const request = require('request')
// process ldap bind operation
myLdapServer.bind(searchBase, function (bindReq, bindRes, next) {
// bind creds
var userDn = req.dn.toString();
var userPw = req.credentials;
console.log('bind DN: ' + req.dn.toString());
request.post({username: userDn, password: userPw}, (err, res, body)=>{
if(err) {
console.log("returning error");
next(new ldap.InvalidCredentialsError());
} else {
bindRes.end();
next();
}
})
}
Is that what you're looking for? If so, you just need to get accustom to callbacks.
Im trying to create a Cloud Function trigger that will execute after email has been verified.
In the Cloud Functions samples I could only find examples on triggers for onCreate and onDelete.
Within the documentation I found something about creating custom action handlers but I don't actually want to replace the standard email verification dialog they have by default, I just want to change the property of a "user" after the email is verified.
Does anyone have any experience with this, and is this even possible? Or is my only option to create my custom verification view/dialog webpage?
I faced this problem and took me a long time to figure it out how to solve so I hope this could help anyone that could get stuck into this too:
1 -> I created a function that was triggered with onCreate() for a new user
exports.sendConfirmationEmail = functions.auth.user()
.onCreate((user) => {
const actionCodeSettings = {
url: 'https://appNextURL.com/',
handleCodeInApp: false//ensure that the link will open into browser
};
return admin.auth().generateEmailVerificationLink(user.email, actionCodeSettings)
.then(async (link) => {
await db.collection('users').doc(user.uid).set({
verificationLink: link,
emailVerified: false
}, {merge: true});
return sendCustomVerificationEmail(user.email, user.displayName, link);
})
.catch((err) => {
console.error("Error:", err);
return Promise.reject(err);
});
});
The generateEmailVErificationLink() will generate the link based on the link we will save on step 3.
The function sendCustomVerificationEmail() is just an internal function that overcomes the standard email firebase send
2 -> Then I created a function that will receive a manual http trigger with the data that would be generated automatically by firebase when sending an automatic email
exports.verifyEmail = functions.https.onRequest((req, res) => {
const {mode, oobCode, apiKey, continueUrl, lang} = req.query;
const link = "https://us-central1-projectId.cloudfunctions.net/verifyEmail/?mode=" + encodeURIComponent(mode) + "&oobCode=" + encodeURIComponent(oobCode) + "&apiKey=" + encodeURIComponent(apiKey) + "&continueUrl=" + encodeURIComponent(continueUrl) + "&lang=" + encodeURIComponent(lang);
return db.collection("users")
.where("verificationLink", "==", link)
.get()
.then(function (querySnapshot) {
querySnapshot.forEach(function (user) {
const userData: UserData = user.data();
console.log("email verified: ", userData.userId);
return admin.auth().updateUser(userData.userId, {
emailVerified: true
}).then(function (userRecord) {
return db.collection('users').doc(userData.userId).set({emailVerified: true}, {merge: true});
});
});
return res.sendStatus(200).end();
}).catch(function (err) {
console.log("error:", err);
return res.sendStatus(403).end();
});
});
As I saved the link in the onCreate() I can now query that link to get who is the user that I am authenticating
3 -> the third step is to change the link in to Firebase Authentication template to the link generated into the 2nd step:
Navigate to Authentication>Templates:
Click on edit icon> Click on customize action URL:
Navigation
Paste the link generated into the step 2 and save:
Save link
Now every link generated automatically will go trought that function you created on step 2 and you will be able to handle the actions you want to happen.
I hope I could be clear.
you could still check for the verification status (at least) on Android with interface UserInfo method isEmailVerified(); eg. in order to send another verification email upon successful login, in case the current user has not yet verified the email address - and show the login screen again. one could as well HTTP trigger a cloud function or update values in the Firebase directly, through the client library. this might also apply to other platform clients, where one can check for the verification status.
this would not be exactly the event when the email just had been verified, but upon each single login attempt one knows the verification status and this value might be merely relevant on the client-side.
Create a publish button so your users trigger your cloud function
Instead of firing the cloud function immediately upon auth.emailVerified, I'm giving my users a 'Publish Profile' button which fires an http cloud function (passing in user.uid). This function looks up the user auth using the passed in user.uid
if user.uid && auth.emailVerified
write auth.emailVerified to each user.post
By default, post document "post.emailVerified" fields start out false, and cannot be written to except via adminFirestore in a cloud function.
I am using nodemailer module with my nodejs/express.js app.
I am using gmail as the transporter.
It seems like every 20 successful emails sent, I will get this error:
454 4.7.0 Cannot authenticate due to temporary system problem
I'm testing out an invoice system so I'm regularly sending an email to the same email address for testing.
I am not using Google apps and I have set Gmail security to work with low security apps.
Invoices are important and if I cant guarantee all invoices will get sent to my customers, its going to be bad.
Is this normal?
app.get('/thankyou', function(req,res) {
var transporter = nodemailer.createTransport({
service: 'Gmail',
auth: {
user: 'somethinggggg#gmail.com',
pass: 'password here'
}
});
res.render('thankyou_email', function(err,html) {
var mailOptions = {
from: 'John Doe <somethinggggg#gmail.com>',
to: 'someoneee#icloud.com',
subject: 'Your order was received',
html: html
};
transporter.sendMail(mailOptions, function(error, info) {
if(error) {
console.log('error1: ' + error);
transporter.sendMail(mailOptions, function(error, info) {
if(error) {
console.log('error2: ' + error);
} else {
console.log('cool bro everything works2');
console.log(info);
}
});
} else {
console.log('cool bro everything works');
console.log(info);
}
});
res.render('thankyou_page');
});
As you can see, if I have an error I try to resend the email again, but still I receive the same error.
Also notice that I am using res.render() twice. I thought this was weird but it still works. The first res.render() I am using to generate the html for the email, and the last res.render() is to render the html in the response back to the client. I used to get an error about sending headers twice, but I don't see that error no more. Nevertheless ,it still worked.
Whose fault is this? Me? Nodemailer? Gmail?
If I were to sign up for Google apps will I still have this problem?
I have come across Nodemailer documentation and it was stated in Using OAuth2 that if a XOAuth2 token generator is used as the value for auth.xoauth2 when setting up transporter object then you do not need to set the value for user or pass as OAuth2 is used for authenticated.
XOAuth2 generator generates required accessToken itself if it is missing or expired. If authentication fails, a new token is requested and the authentication is retried once. If it still fails, an error is returned.
Sample code is given in the documentation. Though, I haven't personally tried using it, I hope it works with you. :)
I am trying to store some data on Custom Object of Appcelerator ACS. So there will be a service to do that. Each time it require authentication to create a new object
But I am sometime getting below error while login with ACS. But it not occuring always. It only if I call service multiple time.
error i am getting is:
{
success: false,
error: true,
code: 400,
message: "Invalid request sent."
}
Code used to login :
ACS.Users.login(userData, function(data){
if(data.success) {
console.log("----------Successful to login.---------------");
console.log(data);
res.send(data);
res.end();
} else {
console.log("------------------login failed---------------");
console.log(data);
res.send(data);
res.end();
}
},req, res);
Can some one can help me to understand how to re-use session id from node.ACS web service app (Not web app)?
How I can keep session / check session validity before pushing something to custom object? Has anyone faced similar issue?
Thanks
Peter
Since you are passing in the req and res parameters into ACS.Users.login, the session is saved in the _session_id cookie:
http://docs.appcelerator.com/cloud/latest/#!/guide/node_acs
When you make subsequent calls to ACS, you pass in the req and res parameters and it will check for this session token.
A session can become invalid after timeout or logout. To check whether a session is still good, one way is to check against this REST API endpoint (GET):
https://api.cloud.appcelerator.com/v1/users/show/me.json?key=(YOUR ACS KEY)&_session_id=(SESSION ID OF THE USER)
Also, for some reason acs-node v0.9.3 appears to be returning the same session ID, even for different users. Some side-effects I've seen include (1) the wrong user attempting to make a change to an object, and (2) objects created by one user are actually owned by the last person who logged in. Making sure acs-node is at v0.9.2 avoids this issue.
Now that node-acs has been shut down, everyone is obliged to move to the new arrowdb node sdk.
To solve the issue above about not authenticating the User, before every ArrowDB call, make sure to pass the session_id of the user like so:
// Connect Node.ACS to existing ACS
var ArrowDB = require('arrowdb'),
arrowDBApp = new ArrowDB('XXYYZZZ', { // ArrowDB Key
apiEntryPoint: 'https://api.cloud.appcelerator.com',
autoSessionManagement: false, // handle session_id yourself
prettyJson: true,
responseJsonDepth: 3
});
// == Creates the ACS_Event for a logged in User on ArrowDB ==
function createACSEvent(uniqueId, params) {
arrowDBApp.sessionCookieString = params.session_id; //<-- THIS IS IT!
arrowDBApp.eventsCreate({
name: 'someEvent',
start_time: params.start_time,
custom_fields: params,
}, function(err, result) {
if (err) {
logger.info( 'ERROR ACS_Event created '+ err);
} else {
logger.info( 'Success Creating ACSEvent ' + JSON.stringify(result));
}
});
}
I have a issue with publishing sns to a specific endpoint.
My code:
var AWS = require('aws-sdk');
AWS.config.loadFromPath('/web/config.json');
var sns = new AWS.SNS();
sns.publish({
// TopicArn:'arn:aws:sns:us-west-2:302467918846:MyTestTopik',
TargetArn: 'arn:aws:sns:us-west-2:302467918846:MyTestTopik:613ee49c-d4dc-4354-a7e6-c1d9d8277c56',
Message: "Success!!! ",
Subject: "TestSNS"
}, function(err, data) {
if (err) {
console.log("Error sending a message " + err);
} else {
console.log("Sent message: " + data.MessageId);
}
});
When I use TopicArn, everything is fine. But when I try to send notification to a specific endpoint I take error:
Error sending a message InvalidParameter: Invalid parameter: Topic Name
And I have no idea what kind of parameters it is and from where.
Something similar is working fine for me.
I'm able to publish to a specific endpoint using: Apple Push Notification Service Sandbox (APNS_SANDBOX)
You might also want to try and update the aws-sdk, current version is 1.9.0.
Here's my code, TargetArn was copied directly from the SNS console. I omitted some of the data, like &
var sns = new AWS.SNS();
var params = {
TargetArn:'arn:aws:sns:us-west-2:302467918846:endpoint/APNS_SANDBOX/<APP_NAME>/<USER_TOKEN>'
Message:'Success!!! ',
Subject: 'TestSNS'
};
sns.publish(params, function(err,data){
if (err) {
console.log('Error sending a message', err);
} else {
console.log('Sent message:', data.MessageId);
}
});
You might have an invalid Region. Check you Region for the Topic and set it accordingly. For example if you are us-west-2 you could do something like
var sns = new aws.SNS({region:'us-west-2'});
None of this will work if you don't massage the payload a bit.
var arn = 'ENDPOINT_ARN';
console.log("endpoint arn: " + arn);
var payload = {
default: message_object.message,
GCM: {
data: {
message: message_object.message
}
}
};
// The key to the whole thing is this
//
payload.GCM = JSON.stringify(payload.GCM);
payload = JSON.stringify(payload);
// Create the params structure
//
var params= {
TargetArn: arn,
Message: payload,
MessageStructure: 'json' // Super important too
};
sns.publish(params , function(error, data) {
if (error) {
console.log("ERROR: " + error.stack);
}
else {
console.log("data: " + JSON.stringify(data));
}
context.done(null, data);
});
So, it turns out that you have to specify the message structure (being json). I tried to publish to endpoint from the AWS console and it worked great as long as I selected JSON. Using RAW would do nothing.
In my script was doing was the previous posts were doing:
var params = {
TargetArn: arn,
Message:'Success!!! ',
Subject: 'TestSNS'
};
And even though CloudWatch was logging success, I never once got the message.
As soon as I added the MessageStructure data and that I properly formatted the payload, it worked like a charm.
The [default] parameter is not useful but I left it in there to show what the structure could look like.
If you don't stringify the payload.GCM part, SNS will barf and say that your message should include a "GCM" element.
The only thing that is annoying is that you are required to know what the endpoint is. I was hoping that you didn't have to format the message based on the endpoint, which really defeats the purpose of SNS in some ways.
Are you trying endpoints other that push notifications such as sms? Direct addressing is currently only available for push notifications endpoints. That is the error you will get when you try to publish to a specific endpoint that does not allow direct direct addressing!
http://aws.amazon.com/sns/faqs/#Does_SNS_support_direct_addressing_for_SMS_or_Email
I was having the exact same issue as you. The problem is the TargetArn that you're using, there's not clear documentation about it. Error happens if you try to put the Application ARN in the TargetArn.
That will produce the error: Invalid parameter: TargetArn Reason: >arn:aws:sns:us-west-2:561315416312351:app/APNS_SANDBOX/com.APP_NAME_HERE.app is >not a valid ARN to publish to.
All you need to do is to put the EndpointArn in the TargetArn.
If you need to see the EndpointArn, you can:
Call listPlatformApplications() to get all your applications ARN's.
Call listEndpointsByPlatformApplication() with the App ARN to get the EndpointArn's list.
Enjoy!