How to authenticate user with AWS Cognito in Vue.js using Javascript - javascript

I am facing the issue that I cannot fully undertsand how to call a function that uses a callback and get the results of the callback returned in a way where the data from the response becomes available to a Vue application.
async loginUser(username, password) {
AWS.config.update({region : region});
const payload = {
AuthFlow: "USER_PASSWORD_AUTH",
ClientId: clientId,
AuthParameters : {
USERNAME: username,
PASSWORD: password
}
}
var cognito = new AWS.CognitoIdentityServiceProvider();
return cognito.initiateAuth(payload, function(err,data) {
if (err) {
alert("Error: " + err);
return null;
}
else {
tokenData = data["AuthenticationResult"];
console.log(tokenData);
return tokenData;
}
})
}
I want the tokenData to be what is returned when calling loginUser, but no matter what I try to do the data from the cognito.initiateAuth, that should be used in my application to validate a sign in, will just be null and not usable.
Hope you guys can help!
Thanks
I tried different solutions using promises, async/await, while loops trying to wait for the response to contain data, but nothing seems to work. I can see the data being printed correctly in the callback, but how do it get this data to pass on to the return of loginUser.

Related

Receiving [Error: Internal] in RN app when triggering a Cloud Function

I have a Google Cloud Function which I am calling from my RN app but it is returning
[Error: Internal]
I have set the permission to Unauthenticated users so anyone can call it - for testing purposes only. When I set to Authenticated users permission, it throws another error [Error: Unauthenticated] eventhough I am authenticated and I can get the currentUser id in my app.
Tried searching for this error but it didnt send me to any possible solutions so decided to post here and hopefully recieve responses that will help me fix it.
const functions = require('firebase-functions');
const admin = require('firebase-admin');
admin.initializeApp();
exports.createUser = functions.region('europe-west1').https.onCall(async (data, context) => {
try {
//Checking that the user calling the Cloud Function is authenticated
if (!context.auth) {
throw new UnauthenticatedError('The user is not authenticated. Only authenticated Admin users can create new users.');
}
const newUser = {
email: data.email,
emailVerified: false,
password: data.password,
disabled: false
}
const role = data.role;
const userRecord = await admin
.auth()
.createUser(newUser);
const userId = userRecord.uid;
const claims = {};
claims[role] = true;
await admin.auth().setCustomUserClaims(userId, claims);
return { result: 'The new user has been successfully created.' };
} catch (error) {
if (error.type === 'UnauthenticatedError') {
throw new functions.https.HttpsError('unauthenticated', error.message);
} else if (error.type === 'NotAnAdminError' || error.type === 'InvalidRoleError') {
throw new functions.https.HttpsError('failed-precondition', error.message);
} else {
throw new functions.https.HttpsError('internal', error.message);
}
}
});
in my RN app I am calling it like this:
var user = {
role: role
}
const defaultApp = firebase.app();
const functionsForRegion = defaultApp.functions('europe-west1');
const createUser = await functionsForRegion.httpsCallable('createUser');
createUser(user)
.then((resp) => {
//Display success
});
console.log(resp.data.result);
})
.catch((error) => {
console.log("Error on register patient: ", error)
});
I think the way I am calling it in my RN app is correct because I have tested it with a testFunction and I returned a simple string. So, I believe the problem is somewhere in the function itself.
EDIT: I just tested by simply calling the function and returning the context and it always returns Internal error:
exports.registerNewPatient = functions.region('europe-west3').https.onCall((data, context) => {
return context; //this is returned as INTERNAL error.
}
I just cant get to understand whats going on here, why does it return Internal error when I am authenticated as a user and it should return the authenticated user data, isn't that right?
Try some console.log(context) ; console.log(data) statements in your registerNewPatient function and take a look at the logs. What do they say?
Some other things to consider might include that in your client code you use europe-west1 while your function code has europe-west3. Try to have those line up and see if it works? From my experience, if a specified function isn't found to exist, the client receives an INTERNAL error.

Trying to use bcrypt,compare and returns Promise { <pending>}

I am trying to create a login api by using aws lambda, mongoose, and bcryptjs.
My lambda handler uses async and I am just trying to compare the user typed password with already hashed password that is in the database by using the bcrypt.compare() function in the bcryptjs module. However, my code keeps giving me Promise { } so I have tried a bunch of ways to fix this but still have an issue. I am new to using async so I might be doing totally wrong so please do not be harsh on me :)
I am getting the user account data with the encrypted password from MongoDB atlas by using the below code and it works perfectly.
let user = await User.findOne(query).select('_id name email password');
I also have a mongoose method that I have created in a user.js file just like below.
UserSchema.methods.comparePassword = function(password) {
return bcrypt.compare(password, this.password);
};
so the above method gets called and prints the result with console.log with the following code.
let passwordValid = user.comparePassword(parameters.password);
console.log('Password is validated', passwordValid);
and it gives me
INFO Password is validated Promise { <pending> }
in the lambda console.
I have done many searches so I tried using await before comparePassword like below and still not working.
let passwordValid = await user.comparePassword(parameters.password);
I have also tried resolving the returned promise by using then() like below
let passwordValid = user.comparePassword(parameters.password);
passwordValid.then(function(err, result) {
callback(null, {
"statusCode": 200,
"headers": {
"Content-Type": "application/json"
},
"body": JSON.stringify({
"success": false,
"content": result
})
});
});
However, this still does not work as I want. Only respond I receive is
{
"message": "Internal server error"
}
Have you seen this https://www.npmjs.com/package/bcrypt?
In this line, await is neede:
return bcrypt.compare(password, this.password);
As the above link suggests:
async function checkUser(username, password) {
//... fetch user from a db etc.
const match = await bcrypt.compare(password, user.passwordHash);
if(match) {
//login
}
//...
}

AWS Cognito Identity JS: Forget/Remember/Do Not Remember Device

I'm working with the AWS Cognito Identity JS SDK (https://github.com/aws/amazon-cognito-identity-js) and I'm trying to set up a few buttons to test the setDeviceStatusRemembered, setDeviceStatusNotRemembered, and forgetDevice functionality but I keep getting an error saying:
MissingRequiredParameter: Missing required key 'DeviceKey' in params
Here is an implementation of one of the functions:
forgetDevice = function(){
var cognitoUser = userPool.getCurrentUser();
if (cognitoUser != null) {
cognitoUser.getSession(function(err, session) {
if (err) {
alert(err);
signOut();
return;
}
console.log('session validity: ' + session.isValid());
cognitoUser.forgetDevice({
onSuccess: function (result) {
console.log('call result: ' + result);
},
onFailure: function(err) {
alert(err);
}
});
});
}
}
If I change up the function like this:
forgetDevice = function(cognitoUser){
cognitoUser.forgetDevice({
onSuccess: function (result) {
console.log('call result: ' + result);
},
onFailure: function(err) {
alert(err);
}
});
}
and I call the function from the cognitoUser.authenticateUser success callback function, passing cognitoUser as argument into the forgetDevice function above everything works perfectly.
From looking at the session object in the first implementation it appears that the session object does not contain the DeviceKey property so the cognitoUser.forgetDevice() call fails.
What I'm trying to figure out is, should I just be calling the setDeviceStatusRemembered, setDeviceStatusNotRemembered, and forgetDevice functions on login, or should I be able to call them any time within my application? Hopefully that makes sense. Any help would be greatly appreciated. Thanks!
Does this help:
Note that if device tracking is enabled for the user pool with a setting that user opt-in is required, you need to implement an onSuccess(result, userConfirmationNecessary) callback, collect user input and call either setDeviceStatusRemembered to remember the device or setDeviceStatusNotRemembered to not remember the device.
http://docs.aws.amazon.com/cognito/latest/developerguide/amazon-cognito-user-pools-device-tracking.html
Also can you attempt calling getCachedDeviceKeyAndPassword to populate the deviceKey in the CognitoUser object?

Promise either never get called, or is rejected (Parse JS SDK)

I am trying to write a function that add or edit some fields on a User object.
The problem come when I try to save the user, if I use user.save, the Promise is rejected with error 206 UserCannotBeAlteredWithoutSessionError.
However, if I get the session id (and documentation about that is scarce), the promise never get resolve, nor rejected. The app seems to just jump to the callback.
My function:
function update(user, callback) {
let query = new Parse.Query(Parse.User);
query.equalTo("username", user.email);
query.find().then(
(users) => {
if(users.length === 0) {
callback('Non existent user');
} else {
let user = users[0];
// user.set('some', 'thing');
console.log('save');
user.save(/*{
sessionToken: user.getSessionToken()
}*/).then(
(test) => {
console.log('OK - ' + test);
callback();
}, (err) => {
console.log('ERR- ' + require('util').inspect(err));
// console.log(callback.toString());
callback(error.message);
}
);
}
},
(error) => {
callback(error.message);
}
);
}
Called with:
var async = require('async'),
baas = require('./baas.js');
async.waterfall([
(callback) => {
callback(null, {
email: 'user#test.com',
password: 'password'
});
},
(user, callback) => {
console.log('connect');
baas.connect(() => { //Initialize the connection to Parse, and declare use of masterKey
callback(null, user);
});
},
(user, callback) => {
console.log('update');
baas.update(user, (err) => {
callback(err);
});
}
], (err) => {
console.log('Error: ' + err);
});
The logs become:
Without session token:
connect
update
save
ERR- ParseError { code: 206, message: 'cannot modify user sA20iPbC1i' }
With session token:
connect
update
save
I do not understand how it is possible that the promise just callback without printing anything, nor why no error are raised anywhere.
Edit:
Following #user866762 advice, I tried to replace the query with Parse.User.logIn and use the resulting User object.
While this solution give me a sessionToken, the end result is the same, parse crash if I don t provide the session token, or give me a error if I do.
According to the Parse Dev guide:
...you are not able to invoke any of the save or delete methods unless the Parse.User was obtained using an authenticated method, like logIn or signUp.
You might also try becoming the user before saving, but I have my doubts that will work.
When you're "get[ting] the session id" my guess is that you're really breaking something. Either Parse is having a heart attack at you asking for the session token, or when you're passing it in save you're causing something there to explode.

Parse.com Cloud code Error: success/error was not called when trying to update a user

ive never used cloud code/javascript and I am trying to write some parse cloud code to find a user using a objectId passed in to the cloud function, and then update that users relation that holds friends and finally save that user.
below is the function im using:
Parse.Cloud.define("addFriendToFriendsRelation", function(request, response) {
Parse.Cloud.useMasterKey();
var fromUserObjectId = request.params.fromUserObjectId;
var acceptingUser = request.params.user;
var query = new Parse.Query(Parse.User);
// find the user the request was from using the objectId
query.get(fromUserObjectId, {
success: function(user) {
var fromUser = user
var relation = fromUser.relation("friends");
relation.add(acceptingUser);
fromUser.save({
success: function() {
response.success("Successfully saved the users relation")
},
error: function() {
response.error("Save failed");
}
});
},
error: function() {
response.error("Save failed");
}
});
});
I managed to piece this together using the Parse docs. but Im really not following it to well. Never used javascript and am finding the syntax confusing.
then im calling the function with
//fromUser is a PFUser object defined further up
[PFCloud callFunctionInBackground:#"addFriendToFriendsRelation" withParameters:#{#"fromUserObjectId" : fromUser.objectId} block:^(id object, NSError *error) {
}
however whenever this function is called I get a success/error was not called error. Though im calling response.success and response.error in the function so I dont know why that is? Can anyone lend a hand?
edit: after doing some more searching it looks like response.success and response.error should only be called once each, so I modified my function to look like this:
arse.Cloud.define("addFriendToFriendsRelation", function(request, response) {
Parse.Cloud.useMasterKey();
var fromUserId = request.params.fromUserObjectId;
console.log("fromUserId:");
console.log(fromUserId);
var acceptingUser = request.params.user;
console.log("acceptingUser:")
console.log(acceptingUser);
var query = new Parse.Query(Parse.User);
query.get(fromUserId, {
success: function(user) {
console.log("found user:");
console.log(user);
var fromUser = user;
var relation = fromUser.relation("friends");
relation.add(acceptingUser);
console.log("added accepting user to relation");
fromUser.save({
success: function() {
response.success("successfully saved user")
},
error: function() {
response.error("error saving user");
}
});
console.log("found a user");
},
error: function() {
console.log("error finding user");
}
});
});
An old question, but since it's been up-voted, maybe answering can help someone else :).
First off, there is an error in how you are saving fromUser.
fromUser.save({ success: ...
If you look at the api you can see that it should be of the form:
fromUser.save(null, { success: ...
But the larger problem that kept you from finding your bug is that errors are getting eaten 'cause you are using the old style method of dealing with async code instead of using promises.
Below, I have re-written to use promises. Note:
I always return promise generating calls (there are other options for catching errors in async code, but start with this.)
Put a .catch at the end. The .catch is effectively the same things as .then(null, response.error) but either way, it is imperative that there is final backstop to catch errors. In your code above, the error was in a success block, that was running async, so when there was an error, it failed with no one to hear it :).
Parse.Cloud.define("addFriendToFriendsRelation", (request, response) => {
const fromUserId = request.params.fromUserObjectId;
console.log("fromUserId:", fromUserId);
const acceptingUser = request.user;
console.log("acceptingUser:", acceptingUser)
new Parse.Query(Parse.User);
.get(fromUserId, { useMasterKey: true })
.then((fromUser) => {
console.log("found fromUser:", fromUser);
const relation = fromUser.relation("friends");
relation.add(acceptingUser);
console.log("added accepting user to relation");
return fromUser.save(null, { useMasterKey: true })
})
.then(response.success)
.catch(response.error);
});

Categories

Resources