I have used many services from AWS, some were easy, while some were a bit difficult. After 2 days of searching everywhere, I can say documentation for this service is misleading.
I have simple task to do. I want to change a user attribute in the Cognito pool. And to make things easy, I just need to change an Email, and thats it. Application is an Backoffice (Express/Node), where admins can change user's email.
After reading and reading, I am getting more confused. Apparently, the aws-sdk library, the one I am familiar with, has some Cognito API's that I could use. Getting a working example on how to use them, turned out to be a nightmare.
Then I found out there is a library, but only to be used on the client side. After some tweaks I got it running in Node.js. The tweak was to expose a fetch library in global Node.js namespace.
I was able to add a new user. But for all my intentions, I can't change any of the attributes (like email). The library wants me to provide Username (real user) and a password.
I do have a Username (in this case an email), but I don't have the password.
All I need to do is to connect to the service, and send new attribute for the user and thats it.
This is what I have so far (mainly hacked code samples, from variety of places), and I cant get it to work:
var poolData = {
UserPoolId : 'euXXXXXXX',
ClientId : 'XXXXXXXXXXXX'
};
var userPool = new AmazonCognitoIdentity.CognitoUserPool(poolData);
Ok The above line makes a connection to the existing user pool.
Now if I were to do this:
var attributeList = [];
var dataEmail = {
Name : 'email',
Value : 'email#mydomain.com'
};
var dataPhoneNumber = {
Name : 'phone_number',
Value : '+15555555555'
};
var attributeEmail = new AmazonCognitoIdentity.CognitoUserAttribute(dataEmail);
var attributePhoneNumber = new AmazonCognitoIdentity.CognitoUserAttribute(dataPhoneNumber);
attributeList.push(attributeEmail);
attributeList.push(attributePhoneNumber);
userPool.signUp('username', 'password', attributeList, null, function(err, result){
if (err) {
alert(err.message || JSON.stringify(err));
return;
}
cognitoUser = result.user;
console.log('user name is ' + cognitoUser.getUsername());
});
I can see in AWS console that the user is being added. Great.
Now how to change the attributes of the existing user?
All of examples like this and this
Suggest the following:
Use case 8. Update user attributes for an authenticated user.
var attributeList = [];
var attribute = {
Name : 'nickname',
Value : 'joe'
};
var attribute = new AmazonCognitoIdentity.CognitoUserAttribute(attribute);
attributeList.push(attribute);
cognitoUser.updateAttributes(attributeList, function(err, result) {
if (err) {
alert(err.message || JSON.stringify(err));
return;
}
console.log('call result: ' + result);
});
The problem here is I cant authenticate the user. I can't know user's password, only his email. This is after all a simple Backoffice program, where I just need to change users email.
What can I do in this case?
To update the attributes of a Cognito User Pool-user as an admin, you should use adminUpdateUserAttributes function from the aws-sdk class CognitoIdentityServiceProvider.
let AWS = require('aws-sdk');
let cognitoISP = new AWS.CognitoIdentityServiceProvider({ region: 'your-region-here' });
function updateUserAttribute(name, value, username, userPoolId){
return new Promise((resolve, reject) => {
let params = {
UserAttributes: [
{
Name: name, // name of attribute
Value: value // the new attribute value
}
],
UserPoolId: userPoolId,
Username: username
};
cognitoISP.adminUpdateUserAttributes(params, (err, data) => err ? reject(err) : resolve(data));
});
}
Related
I am attempting to send a json file created from fields in a webpage to a node function in AWS Lambda to add it to a DynamoDB table. I have the JSON made but I don't know how to pass it from the js used for the page to the lambda function. As this is for a class project, my group and I have decided to forego amazon's gateway API, and are just raw calling lambda functions using amazon's js sdk. I've checked Amazon's documentation and other various examples, but I haven't been able to find a complete solution.
Node function in lambda
const AWS = require('aws-sdk');
const db = new AWS.DynamoDB.DocumentClient({region: 'us-east-1'});
exports.handler = async (event) => {
const params = {
TableName : 'characterTable',
Item: {
name : 'defaultName'
}
};
const userID = 'placeholder';
params.Item.userID = userID;
return await db.put(params).promise();
};
//}
Webpage js:
var lambda = new AWS.Lambda();
function makeJSON(){
var userID = "";
var name = document.forms["characterForm"]["characterName"].value;
var race = document.forms["characterForm"]["race"].value;
var playerClass = document.forms["characterForm"]["class"].value;
var strength = document.forms["characterForm"]["strength"].value;
var dexterity = document.forms["characterForm"]["dexterity"].value;
var constitution = document.forms["characterForm"]["constitution"].value;
var intelligence = document.forms["characterForm"]["intelligence"].value;
var wisdom = document.forms["characterForm"]["wisdom"].value;
var charisma = document.forms["characterForm"]["charisma"].value;
characterSheetObj = {userID: userID, name: name, race: race, class: playerClass, strength: strength, dexterity: dexterity, constitution: constitution, intelligence: intelligence, wisdom: wisdom, charisma: charisma}
characterSheetJSON = JSON.stringify(characterSheetObj);
alert(characterSheetJSON);
var myParams = {
FunctionName : 'addCharacterSheet',
InvocationType : 'RequestResponse',
LogType : 'None',
Payload : characterSheetJSON
}
lambda.invoke(myParams, function(err, data){
//if it errors, prompts an error message
if (err) {
prompt(err);
}
//otherwise puts up a message that it didnt error. the lambda function presently doesnt do anything
//in the future the lambda function should produce a json file for the JavaScript here to do something with
else {
alert("Did not error");
}
});
}
The html page for the raw javascript includes the proper setup for importing the sdk and configuring the region/user pool
I just don't know how to get the payload from the invocation in my node function, as this is my first time working with lambda and amazon's sdk, or doing any web development work at all, to be honest.
i would do it with async await. It's better to read.
lambda.invoke = util.promisify(lambda.invoke);
const result = await lambda.invoke(yourParams);
const payload = JSON.parse(result.Payload);
I read a book where an example deploys a javascript object in Node.js , in order to save and edit a User.
Is it safe to save the hashed password to an object, even if it is back-end? I think is a good practice to hash it and save it, not keep it around in an object, because I guess it will maybe be accesible.
In a simpler project I used Apache and synchronous PHP, so I got the password, hash it and save it in the database. It feels risky to me to store it in an object. I am used to keeping the id of the user in a session, so I know who she is, and thats it.
So, is it safe to keep it in an object or am I overeacting here?
This is the book's code
function User(obj){
for (var key in obj)
{this[key]=obj[key;]}
}
User.prototype.save = function (fn){
var user = this;
user.hashPassword(function (err){
var id = user.id;
//save user on redis...
})
}
User.prototype.hashPassword = function (fn){
var user = this;
bcrypt.genSalt(12, function(err,salt){
user.salt = salt;
bcrypt.hash(user.pass, salt, function(){
user.pass = hash;
fn();
})
})
To test this I add
var tobi = new User({
name:'tobi',
pass:'im a ferret'
});
tobi.save(function (err){
if (err)throw error;
})
and then hit on the console node folder/filename.js
The book is "Node.js in Action",
Mike Cantelon, Marc Harter, T.J. Holowaychuk and Nathan Rajlich, Manning Publications, ©2014, pp. 233-236
Thanks!
I'm making a request but it doesn't seem to work. If I copy code into my browser it works good, but in my console it shows up this :
{
"status" : "success",
"data" : {
"error_message" : "API access enabled, but unable to verify two-factor authentication code. If you need help with this, please contact support#bitskins.com."
}
}
What am I doing wrong? It's based on two-factor authentication that as I said works good while printing the url itself and when i'm copying it into my browser.
var url = 'https://bitskins.com/api/v1/get_item_price/?api_key='+bitskins.apikey+'&code='+bitskins.code+'&names='+encodeURIComponent(items[i].market_hash_name)+'&delimiter=!END!';
console.log(url);
request(url, function (error, response, body) {
if (!error) {
console.log(body)
}
});
In case you want, here is my api key module to generating it (api key deleted for security)
var TOTP = require('onceler').TOTP;
//Create a TOTP object with your secret
var totp = new TOTP('deleted');
// print out a code that's valid right now
// console.log(totp.now());
var code = totp.now();
module.exports = {
code: code,
apikey: 'deleted'
}
Founder of BitSkins, Inc. here. You need to have the following:
1) Your API Key
2) Your Secure Access Secret
You see the Secret when you enable Secure Access. If you do not have this, just disable/re-enable Secure Access and note the Secret down. The TOTP code you generate is with that Secret. Generate the TOTP code right before every API call and you'll be fine.
I think it should work. For me it works fine.
var API_KEY = ''; //It is very important
var SECRET_KEY = ''; //It is very important
var totp = new TOTP(SECRET_KEY);
var code = totp.now();
var options = {
url: 'https://bitskins.com/api/v1/get_item_price',
form: {
'api_key': API_KEY,
'names': 'Tec-9%20%7C%20Sandstorm%20(Minimal%20Wear)',
'delimiter': '!END!',
'code': code
}
};
function callback(error, response, body) {
if (!error) {
var info = JSON.parse(body);
console.log(info);
}
}
request.post(options, callback);
What npm package do you use to create 2FA code? I'm using "onceler" from example but I think it creates wrond codes. Here is my code:
var API_KEY = ''; //correct key from settings page
var SECRET_KEY = ''; // correct key which I copied from form with QR code.
var totp = new TOTP("SECRET_KEY");
var code = totp.now();
This code doesn't equal code which I can see in my mobile device and with this code I get error message like in author's question. But if I put code from my mobile in programm code - it works fine. So what package should I use to get correct codes?
In my web application when the user logs in they should see all the documents they made. However when I test my application, I see all the documents including the ones that I didn't make. I believe this is because I am not using Parse.query.equalTo correctly.
Here is my code:
router.post('/login', function (req, res) {
var tempname = req.body.username;
var username = tempname.toLowerCase();
var password = req.body.password;
var login = Promise.promisify(Parse.User.logIn);
var promerror = Promise.reject("error");
var Files = Parse.Object.extend("File");
var query = new Parse.Query(Files);
query.include('user');
var finds = query.find;
var doc = {};
var name, url;
return login(username, password).catch(function (error) {
}).then(function (user, error) {
query.equalTo("user", Parse.User.current());
console.log(JSON.stringify(Parse.User.current()));
if (user) {
res.render('logins', { Message: "Username and/or password don't match" });
}
var temp;
}).catch(function (err) {
res.render('logins');
}).then(query.find(function (results) {
for (var i = 0; i < results.length; i++) {
var object = results[i];
var userID = object.get('user');
console.log("current user");
var codefile = object.get('javaFile');
temp = codefile.name();
name = temp.split("-").pop();
url = codefile.url();
doc[name] = url;
}
})).catch(function (error) {
promerror(error);
}).finally(function () {
res.render('filename', { title: 'File Name', FIles: JSON.stringify(doc) });
}).done();
});
When the user logs in, it should go to the database retrieve all the documents the user made, and save it to doc, the doc is then send to the client side to be displayed.
here is the code for user saving a file:
newFile.save({ user: Parse.User.current(), fileName: newnames, javaFile: parseFile })
I'm not sure, but try setting the File user field to the object id instead of the user object itself. Parse objects sometimes will return a pointer, sometimes the id, etc. So you might be getting unexpected behavior where you're setting the field to Parse.User.current(). Besides that, you look like you're using equalTo correctly.
Otherwise, basically the only way to debug cloud code is to console.log and view the logs.
As a side note, you should be setting the ACL of each File before you save it... that way, a user wouldn't be able to view another user's files anyway (unless these files are meant to be public).
i just writing a error notification panel in meteor, here i create a client side mongodb, but i cant push Meteor.Error message in to that client side db by throwError function, currently it's shows inside an alert box
collection/signup.js
signupDB = new Meteor.Collection('signup');
Meteor.methods({
signupSubmit : function(postData) {
var signinEmailExist = signinDB.findOne({
email : postData.email
});
if (postData.email && signinEmailExist)
throw new Meteor.Error(422, "exist in signinDB");
var signupEmailExist = signupDB.findOne({
email : postData.email
});
if (postData.email && signupEmailExist)
throw new Meteor.Error(422, "exist in signupDB"); //
var user = _.extend(_.pick(postData, 'email', 'password'), {
insert_time : new Date().getTime() });
var userId = signupDB.insert(user);
return userId;
}
});
client/error/error.js
errorDB = new Meteor.Collection(null);
throwError = function(data) {
errorDB.insert({data: "in throwError", del: "N"})
}
errorDB.insert({data: "in signup", del: "N"}) code is working anywhere inside client folder
here throwError function can't called, but signupSubmit method errors shows in a alert box
is the problem of publication/subscription like thinks (not wrote for signup db) ?
how i catch and insert Meteor.Error alerts From Meteor.Methods in to a client side db ?
is there any other function like throwError to trap Meteor.Methods errors ?
How are you calling the method? You need to do something like:
Meteor.call('signupSubmit', user, function(err) {
errorDB.insert(err);
});
However, you seem to be implementing a custom, insecure authentication system. You shouldn't do this; Meteor has a great, secure built-in Accounts package. All you need to do is (on the client side):
errors = new Meteor.Collection;
Accounts.createUser({
email: email,
password: password
}, function(err) {
errors.insert(err);
});
The Accounts.createUser method automatically returns an error if the username/email is duplicated.