Getting data from Cognito triggers - javascript

I have an existing table of users. And I need to get data on user login.
Is it possible to use some cognito trigger for it?
I was trying to use postAuthentication:
postAuthentication:
handler: triggers.postAuthentication
timeout: 10
environment:
GET_USER_LAMBDA: ${file(./env.yml):${'GET_USER_LAMBDA'}}
events:
- cognitoUserPool:
pool: ${file(./env.yml):${'POOL'}}
trigger: PostAuthentication
existing: true
module.exports.postAuthentication = (event, context, callback) => {
try {
const firstName = event.request.userAttributes['custom:firstName'];
const lastName = event.request.userAttributes['custom:lastName'];
lambda.invoke({
FunctionName: GET_USER_LAMBDA,
Payload: JSON.stringify({
query: `${firstName}_${lastName}`
}, null, 2)
})
.promise()
.then(data => {
const body = JSON.parse(data['Payload']).body;
if (body && body.Items && body.Items[0]) {
event.request.clientMetadata = {};
event.request.clientMetadata.body = JSON.stringify(body.Items[0]);
callback(null, event);
} else {
callback(new Error(`Couldn't fetch USER`));
}
});
} catch (error) {
context.done(error);
}
};
The lambda.invoke successfully returns data and there is no any errors but I can't find clientMetadata on front-end.
What trigger should I use and how to get user data?

Related

Opening Web Socket connection from app.get (Express)

I am running the Bitfinex Websocket connection from node.js server using express.
I have an API endpoint so that I ask for the specific book from the link (e.g http://localhost:4000/BTC-USD/buy/100)
The problem is that when I try to make the connection from the app.get the websocket doesn't respond
It only responds from outside. The problem is that, that way I cant pass the parameter so I can establish the proper connection. The code I can't perform
app.get("/:pair/:type/:amount", async (req,res) => {
let { pair, type, amount } = req.params;
try {
wsConnection(pair);
wsMessageHandler()
const result = await simulateEffectivePrice({ pair, type, amount })
res.send({ "effectivePrice" : result })
} catch (error) {
res.send({"error" : error.message})
}
})
The code that works:
wsConnection();
wsMessageHandler()
app.get("/:pair/:type/:amount", async (req,res) => {
let { pair, type, amount } = req.params
try {
// if (type !== "buy" || type !== "sell") throw new Error ("wrong type input")
const result = await simulateEffectivePrice({pair,type,amount})
res.send({ "effectivePrice" : result })
} catch (error) {
res.send({"error" : error.message})
}
})
The wsConnection functions is this, (it requires the book you want to receive information from)
const wsConnection = async () => {
let msg = JSON.stringify({
event: 'subscribe',
channel: 'book',
symbol: 'tBTCUSD',
// len: "25"
})
try {
w.on('open', () => {
w.send(JSON.stringify({ event: 'conf', flags: 65536 + 131072 }))
w.send(msg)
})
} catch (error) {
throw new Error("BUILD CUSTOM ERROR")
}
}
"symbol" would need to be the parameter specified on the endpoint by the user
Thank you very much

Getting following error while fetching data in react Uncaught (in promise) TypeError: Failed to fetch

I have create backend using express and mongodb database. I am trying to fetch data in react but getting an error while fetching the data as show. Please can anyone tell what the solution of above error is and how can i fetch data from the backend
const Register = () => {
const [values, setValues] = useState({
name: "",
age: "",
country: "",
email: "",
});
const setData = (e) => {
console.log(e.target.value);
const { name, value } = e.target;
setValues((val) => {
return {
...val,
[name]: value,
};
});
};
const addData = async (e) => {
e.preventDefault();
const { name, age, country, email } = values;
const res = await fetch("/register", {
method: "POST",
headers: {
"Content-Type": "application/json",
},
body: JSON.stringify({
name,
age,
country,
email,
}),
});
const data = await res.json();
console.log(data);
if (res.status === 404 || !data) {
console.log("Error");
} else {
console.log("Data added successfully");
}
};
Here below is the backend code where the post function is performed.
router.post("/register", async (req, res) => {
const { name, age, country, email } = req.body;
if (!name || !age || !country || !email) {
res.status(404).send("Some data is missing");
}
try {
const preuser = await Crud.findOne({ email: email });
console.log(preuser);
if (preuser) {
res.status(404).send("The user already exists");
} else {
let addUser = new Crud({
name,
age,
country,
email,
});
addUser = await addUser.save();
res.status(201).json(addUser);
console.log(addUser);
}
} catch (error) {
res.status(404).send(error);
}
});
await fetch leads to an exception when the HTTP status is ≥ 400. You must add a try-catch block to handle such exceptions:
try {
const res = await fetch("/register", {...});
} catch(exception) {
// Handle the exception
}
Also, HTTP status 404 should be used when a resource is not found. You use it when a user already exists (where status 400 would be more appropriate) or in case of a database error (when 500 would be more appropriate).

React Native - How to deal with asynchronism with AsyncStorage

I am facing to asynchronism problem :
I create a user in firebase, generating a unique ID for it.
I get this unique ID.
I call an async function to persist this ID with AsyncStorage method.
Problem : The asyncStorage method is called before I get back the generated ID from my user creation. How to deal with this ?
This is my code :
class Subscription extends Component {
constructor() {
super();
this.state = {
email: '',
password: ''
}
}
persistUserId = (userID) => {
try {
AsyncStorage.setItem('userId', userID); // Here, user ID is undefined
} catch (error) {
console.log(error.message);
}
};
updateInputValue = (value, prop) => {
const state = this.state;
state[prop] = value;
this.setState(state);
}
registerUser = () => {
var generatedUserId = '';
firebase
.auth()
.createUserWithEmailAndPassword(this.state.email, this.state.password) // Authentication
.then((res) => {
var user = { // Set Javascript Object to insert
email: this.state.email
}
database.collection("users").add({ // Create the new user generating an ID
'email': user.email,
}).then(function(docRef) {
generatedUserId = docRef.id; // Get the generated ID (The one to persist witch asyncstorage)
}).then(function() {
this.persistUserId(generatedUserId) // Call the AsyncStorage to persist the ID
})
this.props.navigation.navigate('AppPage') // Go to next page.
})
.catch(error => {
alert(error.message)
})
}
For persisting data. According to react-native doc. You need to use async await keyword:
_storeData = async () => {
try {
await AsyncStorage.setItem(
'#MySuperStore:key',
'I like to save it.'
);
} catch (error) {
// Error saving data
}
}
for your case:
persistUserId = async (userID) => {
try {
await AsyncStorage.setItem('userId', userID); // Here, user ID is undefined
} catch (error) {
console.log(error.message);
}
};
Note: Persisting data is async process. That's why you need to use async await
You need to update your firebase then catch as well. Either use bind or use arrow function. Here is updated version:
firebase
.auth()
.createUserWithEmailAndPassword(this.state.email, this.state.password) // Authentication
.then((res) => {
var user = {
// Set Javascript Object to insert
email: this.state.email,
};
database
.collection("users")
.add({
// Create the new user generating an ID
email: user.email,
})
.then( (docRef) => {
generatedUserId = docRef.id; // Get the generated ID (The one to persist witch asyncstorage)
})
.then( () => {
this.persistUserId(generatedUserId); // Call the AsyncStorage to persist the ID
});
this.props.navigation.navigate("AppPage"); // Go to next page.
})
.catch((error) => {
alert(error.message);
});

Firebase function to fetch data from Firebase DB to make Push notification

I have chat app with firebase database and Firebase cloud messaging. I can send firebase notification via console but in real scenario it should be automatic. To make automatic notification,My friend wrote Index.js (Added in cloud functions) file for me but its not sending notifications.
As per our logic function should trigger whenever there is any new entries (in any node or in any room) and fetch these values by firebase function and make post request to FCM server to make notification to receiver device (get value of receiver device from token_To).
Message
Message_From
Time
Type
token_To
Index.js
var functions = require('firebase-functions');
var admin = require('firebase-admin');
var serviceAccount = require('./demofcm-78aad-firebase-adminsdk-4v1ot-2764e7b580.json');
admin.initializeApp({
credential: admin.credential.cert(serviceAccount),
databaseURL: "https://demofcm-78aad.firebaseio.com/"
})
// // Create and Deploy Your First Cloud Functions
// // https://firebase.google.com/docs/functions/write-firebase-functions
//
// exports.helloWorld = functions.https.onRequest((request, response) => {
// response.send("Hello from Firebase!");
// });
exports.setUserNode = functions.auth.user().onCreate(event => {
// ...
});
exports.notifyMsg = functions.database.ref('/{chatroom}/{mid}/')
.onWrite(event => {
if (!event.data.val()) {
return console.log('Message Deleted');
}
const getDeviceTokensPromise = admin.database().ref('/{chatroom}/{mid}/token_to').once('value');
return Promise.all([getDeviceTokensPromise]).then(results => {
const tokensSnapshot = results[0];
if (!tokensSnapshot.hasChildren()) {
return console.log('There are no notification tokens to send to.');
}
const payload = {
notification: {
title: 'You have a new Message!',
body: event.data.val().Message
}
};
const tokens = Object.keys(tokensSnapshot.val());
return admin.messaging().sendToDevice(tokens, payload).then(response => {
const tokensToRemove = [];
response.results.forEach((result, index) => {
const error = result.error;
if (error) {
console.error('Failure sending notification to', tokens[index], error);
if (error.code === 'messaging/invalid-registration-token' ||
error.code === 'messaging/registration-token-not-registered') {
tokensToRemove.push(tokensSnapshot.ref.child(tokens[index]).remove());
}
}
});
return Promise.all(tokensToRemove);
});
});
});
Firebase function Log
How can i fetch above mentioned values of any newly added node in same room(9810012321-9810012347) or any other room(9810012321-9810012325) from database and send it to FCM to make notification
Thanks in Advance.
What i did is created a Message node and I believe doing this by users key. ie, having the receiver(toId) and sender (fromId) key to send the notification.
Hope it helps.
exports.sendMessageNotification = functions.database.ref('/messages/{pushId}')
.onWrite(event => {
let message = event.data.current.val();
console.log('Fetched message', event.data.current.val());
let senderUid = message.fromId;
let receiverUid = message.toId;
let promises = [];
console.log('message fromId', receiverUid);
console.log('catch me', admin.database().ref(`/users/${receiverUid}`).once('value'));
if (senderUid == receiverUid) {
//if sender is receiver, don't send notification
//promises.push(event.data.current.ref.remove());
return Promise.all(promises);
}
let messageStats = message.messageStatus;
console.log('message Status', messageStats);
if (messageStats == "read") {
return Promise.all(promises);
}
let getInstanceIdPromise = admin.database().ref(`/users/${receiverUid}/pushToken`).once('value');
let getSenderUidPromise = admin.auth().getUser(senderUid);
return Promise.all([getInstanceIdPromise, getSenderUidPromise]).then(results => {
let instanceId = results[0].val();
let sender = results[1];
console.log('notifying ' + receiverUid + ' about ' + message.text + ' from ' + senderUid);
console.log('Sender ', sender);
var badgeCount = 1;
let payload = {
notification: {
uid: sender.uid,
title: 'New message from' + ' ' + sender.displayName,
body: message.text,
sound: 'default',
badge: badgeCount.toString()
},
'data': {
'notificationType': "messaging",
'uid': sender.uid
}
};
badgeCount++;
admin.messaging().sendToDevice(instanceId, payload)
.then(function (response) {
console.log("Successfully sent message:", response);
})
.catch(function (error) {
console.log("Error sending message:", error);
});
});
});
const getDeviceTokensPromise = event.data.child('token_To');
should be there instated of getting data from database reference.
or
with fixed path without wildcard like below
const getDeviceTokensPromise = admin.database().ref('/${chatroom}/${mid}/token_to').once('value');
where chatroom and mid is variable which contain value
Second thing:
if (!tokensSnapshot.exists()) {
should in replace of
if (!tokensSnapshot.hasChildren()) {
third thing:
I am not sure about push notification tokenId but
is it required to do?
const tokens = Object.keys(tokensSnapshot.val());
may be we can use directly like below to send push notification
const tokens = tokensSnapshot.val();
You could store all device tokens in a node called tokens like in my example. Tokens could be an array if you would like one user to be able to get notifications on multiple devices. Anyway, store them by their UID.
This works for both Andriod and iOS.
Here is my code:
function loadUsers() {
let dbRef = admin.database().ref('/tokens/' + recieveId);
console.log(recieveId)
let defer = new Promise((resolve, reject) => {
dbRef.once('value', (snap) => {
let data = snap.val();
console.log("token: " + data.token)
//userToken = data.token
resolve(data.token);
}, (err) => {
reject(err);
});
});
return defer;
}
Next we create the notification. I created a lastMessage node to capture just the last message sent in the chat. It is just updated every time a new message is sent in a chat between two users. Makes it easy to get the value. Also makes it easy to show the message on the Conversations screen where there is a list of users who are in a conversation with the current user.
exports.newMessagePush =
functions.database.ref('/lastMessages/{rcId}/{sendId}').onWrite(event => {
if (!event.data.exists()) {
console.log("deleted message")
return;
}
recieveId = event.params.rcId
//let path = event.data.adminRef.toString();
// let recieveId = path.slice(53, 81);
return loadUsers().then(user => {
console.log("Event " + event.data.child("text").val());
let payload = {
notification: {
title: event.data.child("name").val(),
body: event.data.child("text").val(),
sound: 'default',
priority: "10",
}
};
return admin.messaging().sendToDevice(user , payload);
});
});
To implement this logic on your current data structure, just change this line:
let dbRef = admin.database().ref('/tokens/' + recieveId);
and this line:
exports.newMessagePush =
functions.database.ref('/lastMessages/{rcId}/{sendId}').onWrite(event
=> {
to your token location:
let dbRef =
admin.database().ref('/${chatroom}/${mid}/token_to');
and your conversation location:
exports.notifyMsg = functions.database.ref('/{chatroom}/{mid}/')
.onWrite(event => {
Then just change the notification payload be the message you want to display and throw in your error handling on the end of the sendToDevice function, as you did in your code.
Hopefully you figured all this out already but if not maybe this will help you or others trying to use Cloud Functions for notifications.
let payload = {
notification: {
uid: sender.uid,
title: 'New message from' + ' ' + sender.displayName,
body: message.text,
sound: 'default',
badge: badgeCount.toString()
},
'data': {
'notificationType': "messaging",
'uid': sender.uid
}
};
There are two types of FCMs.
1) Data
2) Notification
For detailed overview : FCM Reference
You have to fix your payload for both FCMS. And for Data FCM you have to extract Data in your FCM Service (Client) and generate a push notification according to your need.

ReactJS/AWS Cognito getting user input mid-execution

I have a simple login page built with React that uses the AWS Cognito API to authenticate the user. There are some authentication scenarios (password needs to be updated, need to enter an MFA code, etc.) that require me to get user input mid-execution of the authenticateUser workflow. I'm trying to find a way to get the user input dynamically without using the built-in prompt() method, especially when a user is entering a new password. Based on how the authenticateUser workflow is structured, I'm trying to get all user input within the workflow.
Perhaps I'm not thinking about this problem in the right way, but how can I have another React component dynamically render, get user input (new password, MFA code, etc), and then use that input within the authenticateUser workflow?
The main Login component has a form that upon clicking the Submit button triggers the following function:
handleSubmit = async (event) => {
event.preventDefault();
this.setState({ isLoading: true, loginError: null });
try {
await this.login(this.state.username, this.state.password);
this.props.userHasAuthenticated(true);
}
catch(e) {
//alert(e);
this.setState({ isLoading: false, loginError: e.toString() });
}
}
And then we have the login function that goes through the authenticateUser workflow:
login(username, password) {
const userPool = new CognitoUserPool({
UserPoolId: config.cognito.USER_POOL_ID,
ClientId: config.cognito.APP_CLIENT_ID
});
const authenticationData = {
Username: username,
Password: password
};
const user = new CognitoUser({ Username: username, Pool: userPool });
const authenticationDetails = new AuthenticationDetails(authenticationData);
return new Promise((resolve, reject) => (
user.authenticateUser(authenticationDetails, {
onSuccess: (result) => {
// User authentication was successful
resolve();
},
onFailure: (err) => {
var error = err.toString();
// If password expired
if (error.includes('Password reset required for the user')) {
var verificationCode = prompt('Password reset required. Please enter the verification code sent to your trusted device.' ,'');
var newPassword1 = '';
var newPassword2 = '';
while (newPassword1 !== newPassword2 || newPassword1.length < 8) {
newPassword1 = prompt('Please enter a new password.','');
newPassword2 = prompt('Please confirm your new password','');
}
user.confirmPassword(verificationCode, newPassword1, {
onSuccess: (result) => {
//Not sure if this handleSubmit does anything
//this.handleSubmit;
this.setState({ loginError: 'Password updated successfully! Please login with new password.', loginAlert: "success", updatePasswordUpdateAttribute: true });
return;
},
onFailure: (err) => {
this.setState({ loginError: err.toString() });
reject(err)
}
});
}
// User authentication was not successful
console.error(err);
reject(err)
},
mfaRequired: (codeDeliveryDetails) => {
// MFA is required to complete user authentication.
// Get the code from user and call
var verificationCode = prompt('Please enter the multi-factor code sent to your trusted device.' ,'');
user.sendMFACode(verificationCode, this);
},
newPasswordRequired: (userAttributes, requiredAttributes) => {
// User was signed up by an admin and must provide new
// password and required attributes, if any, to complete
// authentication.
// Get these details and call
// newPassword: password that user has given
// attributesData: object with key as attribute name and value that the user has given.
user.completeNewPasswordChallenge(newPassword1, null, {
onSuccess: (result) => {
this.updatePasswordAttribute(user);
resolve()
},
onFailure: (err) => {
this.setState({ loginError: err.toString() });
reject(err)
}
})
}
})
));
}

Categories

Resources