I'm having difficulty setting up passport-facebook with MySQL.
Everywhere I look I find mongodb, and using mongodb one can post the profile._json object into the database but with MySQL you can't.
passport.use(new FacebookStrategy({
clientID : "",
clientSecret : "",
callbackURL : ""
}), function (accessToken, refreshToken, profile, done){
db.query("SELECT * FROM users WHERE facebook_id = ?", [profile.id], function(err, user){
if (err){
return done(err);
}
else if (user.length == 0){
var name = profile.displayName;
var email = profile.emails[0].value;
var username = profile.username;
var provider = "facebook";
var facebook = profile._json;
db.query("INSERT INTO users (name, email, username, provider, token) VALUES (?, ?, ?, ?, ?)",
[name, email, username, provider, /* profile._json?? */], function(err){
})
}
else{
return done(err, user);
}
});
});
What is the contents of profile._json that need to be saved and their types so that I can create fields in MySQL database?
Is there a possibility that maybe I should set up mongodb and save that info there and use MySQL databse for the rest? I am also using google authentication for my API.
Try this
let user= {Facebook_ID: profile.id, First_Name: profile.displayName, FB_Private_Chat_ID: '000001100'};
mysqlconnection.query('INSERT INTO users SET ?', user, (err, res) => {
if(err) throw err;
console.log('Last insert ID:', profile.id);
})
Related
When I insert data into my DB it says that it was successful but upon checking my DB, all of my fields are null. I am getting undefined values on my firstName,lastName, mobile, and email columns.
I'm really having a hard time debugging as I'm pretty new to JS. I hope anyone can point out what's the problem.
This is the model for Guest
var Guest = function(guest) {
this.firstName = guest.firstName;
this.lastName = guest.lastName;
this.mobile = guest.mobile;
this.email = guest.email;
}
// Insert Guest Data to DB
Guest.createGuest = (guestRequestData, result) => {
db.query('INSERT INTO guest SET ? ', guestRequestData, (error, response) => {
if(error){
console.log('Error while inserting data');
result(null, error);
}else{
console.log('Guest created successfully');
result(null, response);
}
})
}
module.exports = Guest;
And this the controller
// Create New Guest
exports.createNewGuest = (request, response) => {
const guestRequestData = new GuestModel(request.body);
console.log('Request Data', guestRequestData);
// Check Null
if(request.body.constructor === Object && Object(request.body).length === 0){
response.send(400).send({success: false, message: 'Please fill all fields'});
}else{
GuestModel.createGuest(guestRequestData, (error, guest) => {
if(error)
response.send(error);
response.json({status: true, message: 'Guest Created Successfully', data: guest.insertId})
})
}
}
There is a clear example how to insert data into MySQL database using node.js: https://www.mysqltutorial.org/mysql-nodejs/insert/
So in your case would look something like this:
db.query('INSERT INTO guest (firstName, lastName, mobile, email) VALUES (?, ?, ?, ?)', guestRequestData, (error, response) => {
if(error){
console.log('Error while inserting data');
result(null, error);
}else{
console.log('Guest created successfully');
result(null, response);
}
})
where the guestRequestData has the following structure:
["Your first name", "Your last name", "Your mobile", "Your email"]
Make sure you convert the guestRequestData to this format.
I want to build login limit in NodeJs
I want to prevent attempts to login multiple times with client IP
How should I modify this part?
This is my login function
var login = function(id, password, callback) {
pool.getConnection(function(err, poolConn){
if (err)
{
if(poolConn){
poolConn.release();
}
callback(err, null);
return;
}
console.log(poolConn.threadId);
var tablename = 'users';
var columns = ['id', 'nickname'];
var exec = poolConn.query("select ?? from ?? where id = ? and password = ?", [columns, tablename, id, password], function(err, rows){
poolConn.release();
if (err){
callback(err, null);
return;
}
if (rows.length > 0){
console.log(rows);
callback(null, rows);
}
else{
callback(null, null);
}
});
});
};
If you want to do it per user you can have a loginAttemps column in your database
If you want to do it per clientIP you can use a rate limiter like this one
This package stores the ips in memory but you can set up a way to store ips in a database and hook it up to it
I am trying to send users to two different pages based on whether an SQL query is successful or not on an express backend. But only the success route is showing when I use this code.
I previously did not have the await statement in, but had the same issue. I'm not sure whether the react side is taking in the error message as a response, as it's still logging into the console from the backend.
Here is the method in the frontend which is called when the form is submitted:
e.preventDefault();
console.log(this.state);
const newPost = {
pet_name : this.state.pet_name,
content : this.state.content,
content : this.state.content,
owner : 'testOwner',
email : 'test#gmail.com',
img_path : this.state.upload_image
};
//fetch instead of this to talk about in diss
try {
const postData = await axios.post('http://localhost:3306/reportpet', newPost)
.then(res=>console.log(res.data));
this.props.history.push('/postsubmitted')
} catch(error) {
console.log("Catch = ", error.response);
this.props.history.push('/posterror')
}```
The route on the backend is as follows:
```router.post('/reportpet', function (req, res) {
var pet_name = req.body.pet_name,
content = req.body.content,
date = req.body.date,
owner = req.body.owner,
email = req.body.email,
img_path = req.body.img_path;
const query = "INSERT INTO `posts` (`post_id`, `pet_name`, `content`, `date`, `owner`, `email`, `img_path`) VALUES (?, ?, ?, UTC_TIMESTAMP(),?, ?, ?);"
console.log(query);
connection.query(query, [pet_name, pet_name, content, owner, email, img_path ], function(err, result) {
(err)?res.send(err+'error was created'):res.json(result);
if (err) throw err;
console.log('rows inserted')
})
})
module.exports = router
When the data is not added to the database, I expect the user to be sent to the error component. When it is successful, I expect the success component to display.
Try to skip using .then() in await.
And be sure that your backend returns the response with proper HTTP error code (4xx or 5xx) so the axios knows that error happened.
try {
const postData = await axios.post('http://localhost:3306/reportpet', newPost)
console.log(postData);
this.props.history.push('/postsubmitted')
} catch(error) {
console.log("Catch = ", error.response);
this.props.history.push('/posterror')
}
Mykola Prymak answered this. I had a response sending the error instead of throwing it, removing that and adding the response underneath the throw fixed it.
Code in the backend is now this:
const query = "INSERT INTO `posts` (`post_id`, `pet_name`, `content`, `date`, `owner`, `email`, `img_path`) VALUES (null, ?, ?, UTC_TIMESTAMP(),?, ?, ?);"
console.log(query);
connection.query(query, [pet_name, content, owner, email, img_path ], function(err, result) {
// (err)?res.send(err+'error was created'):res.json(result); {removed code}
if (err) throw err;
res.json(result);
console.log('rows inserted')
})
```
I have a put route which can be used to update the user. Everything works fine unless the user will only provide only some params instead of all. How I can fix this? Are there some "simple" solutions for this problem? Because if the user only update his email everything else will be inserted empty..
const id: number = req.params.id;
const password: string = req.body.password;
const email: string = req.body.email;
const lastname: string = req.body.lastname;
const firstname: string = req.body.firstname;
const phoneNumber: string = req.body.phoneNumber;
const permissionID: number = req.body.permissionID;
const imageUrl: string = String(imagePath);
const passwordHash = bcrypt.hashSync(password, 10);
const insertData: [string, string, string, string, string, string, number, number] = [email, passwordHash, phoneNumber, firstname, lastname, imageUrl, permissionID, id];
const query = `UPDATE Users SET email = ?, password = ?, phone_number = ?, first_name = ?, last_name = ?, image_url = ?, permission_id = ? WHERE user_id = ?;`;
connection.query(query, insertData, (err: MysqlError | null) => {
if (!err) {
res.status(200);
res.json( { "Message": "Successfull user was updated" } );
} else {
res.status(500);
res.json( { "Database Error ": err.message } );
}
});
Okay I wrote something I hope this post will help someone. First of course it's possible to save the complete user data model in the client and to resend the complete data to the server. But why should I do this? I don't think this is effecient. If the user just want to change his lastname why I should send the whole payload...Anyway this is the way I solve it.
First I define my possible data I will receive if the user will update some attributes.
enum Validate {
password = 'password',
email = 'email',
firstname = 'first_name',
lastname = 'last_name',
phoneNumber = 'phone_number',
permissionID = 'permission_id'
}
So my function will check the received params and will return the insertData and query. As I'm using password hashing it will check as well if the user wants to update his password.
function updateParams(body: {}, options: [Validate], callBack: (insertData: string[], query: string) => void) {
const insertData: string[] = [];
let query = "";
for (const index in options) {
if (!(body[`${options[index]}`] === '' || body[`${options[index]}`] === undefined || body[`${options[index]}`] === null)) {
query += `${options[index]} = ?, `;
// If user will update password hash it
`${options[index]}` === 'password' ? insertData.push(bcrypt.hashSync(body[`${options[index]}`], 10)) : insertData.push(body[`${options[index]}`]);
}
}
callBack(insertData, query.slice(0, -2));
}
For the next step I'm using promises because there are some if/else statements. The user has the possibilities to just update his picture for example.
const updateUser = (req, res, insertData, query) => {
const promise = new Promise((resolve, reject) => {
let endQuery = '';
if (req.file) {
image.uploadImageToStorage(req.file)
.then((imagePath) => {
if (Object.keys(req.body).length === 0) {
endQuery = `UPDATE Users SET image_url = ? WHERE user_id = ?;`;
insertData.push(String(imagePath));
insertData.push(req.params.id);
resolve([endQuery, insertData]);
} else {
endQuery = `UPDATE Users SET ${query}, image_url = ? WHERE user_id = ?;`;
insertData.push(String(imagePath));
insertData.push(req.params.id);
resolve([endQuery, insertData]);
}
}).catch((error) => {
reject(error.message );
});
} else {
endQuery = `UPDATE Users SET ${query} WHERE user_id = ?;`;
insertData.push(req.params.id);
resolve([endQuery, insertData]);
}
});
return promise;
};
Now I can just use my route.
app.put('/api/v1/users/:id', image.multerMiddleware.single('image'), (req, res) => {
if (((Object.keys(req.body).length !== 0) || req.file) && !isNaN(req.params.id)) {
updateParams(req.body, [Validate.password, Validate.email, Validate.lastname, Validate.firstname, Validate.phoneNumber, Validate.permissionID], (insertData, query) => {
updateUser(req, res, insertData, query)
.then((result) => {
connection.query(result[0], result[1], (err: MysqlError | null) => {
if (!err) {
res.status(200);
res.json({ "Message": "Successfull user was updated" });
} else {
res.status(500);
res.json({ "Database Error ": err.message });
}
});
}).catch((error) => {
res.status(500);
res.json({ "Error ": error.message });
});
});
} else {
res.status(400);
res.json({ "Error": "Please provide the correct paramaters" });
}
});
So now
The user can update only some params
The user can update some params and his picture
The user can update only his picture
It work's fine now.
What I do for when someone is editing a user (or other type of data) is that I retrieve the entire data for the user and show it on the editing form. Then when they make the updates, I send all the data up. This way when I do the SQL update, it will re-save the unchanged data as well as the changed data.
Your other option is a series of conditionals which add to the update statement based off what fields are sent in to update.
You either set only those values that were provided, or, if you really insist on updating all columns (why not the PK while you're at it) you qould query them first.
I am using koa-passport for authentication and credentials are stored on a mySQL server. Each username has a unique ID in the database table. I am using a very simple scheme where I just use IDs for both serialization and deserialization instead of having a whole user object.
For authentication, there is a stored procedure defined in the database which returns either null or the userID if found in the database for the credentials supplied (the stored procedure looks at both username and password so no need to handle on nodeJS. Please assume that the code snippet below directly gives us the values without formatting the results from the mySQL database for sake of simplicity)
const localStrategy = require('passport-local').Strategy;
passport.serializeUser((ctx, ID, done) => {
done(null, ID);
});
passport.deserializeUser(async (ctx, ID, done) => {
mySQLPool.getConnection(function (err, thisConnection) {
let sqlQuery = "select * from table where userID = " + ID + "";
thisConnection.query(sqlQuery, function (error, results) {
thisConnection.release();
console.log("De-serializing User");
done(error, results.userID);
})
})
});
passport.use(new localStrategy({
usernameField: 'username',
passwordField: 'password',
passReqToCallback: true
}, (async function (ctx, username, password, done) {
mySQLPool.getConnection(function (err, thisConnection) {
let sqlQuery = "CALL " + leadingSQLStoredProc + username + "', '" + password + trailingSQLStoredProc;
console.log("Authenticating user....")
thisConnection.query(sqlQuery, function (error, results) {
thisConnection.release();
if (error)
return done(error);
if (results.userID !== null)
return done(null, results.userID);
else
return done(null, false);
})
})
})))
Is there a way to avoid accessing the database on each deserialization?
Yes, you can serialize and store entire user object instead of its id.
But I don't recommend you doing so because data in the session can become obsolete. It is much better to fetch it from the database on every request.