I am trying to authenticate using google plus api and trying to save the google user details in the callback function from google plus api but due to some reason i am unable to pass values from google plus api response to callback request.
Snippet from Router.js
router.get("/auth/google", passport.authenticate('google', { scope: ['profile','email'] }));
router.get("/auth/google/callback" , passport.authenticate('google') , googleInsert);
Snippet from User.controller.js
const {
getGoogleUserByEmail,
createGoogleUser,
} = require("./user.service.js");
module.exports = {
googleInsert: (req, res) => {
body = req.body;
body.googleId = req.body.profile.id;
body.firstName = req.body.profile.name.givenName;
body.lastName = req.body.profile.name.familyName;
body.email = req.body.profile.emails[0].value;
body.photoUrl = req.body.profile.photos[0].value;
//const salt = genSaltSync(10);
//body.password = hashSync(body.password, salt);
//verify if email id exists
getGoogleUserByEmail(body.email, (err, results) => {
if (err) {
console.log(err);
}
//Email id already registered and exists in db
if (results) {
console.log("Google Email already exists");
return res.status(409).json({
success: 0,
data: "Google Email already exist",
});
}
console.log(
"Google Email id is not registered, proceed with Google User Insert"
);
if (!results) {
createGoogleUser(body, (err, createResults) => {
console.log(body);
if (err) {
console.log(err);
return res.status(500).json({
success: 0,
message: "Database connection error",
});
}
if (createResults.affectedRows == 1) {
console.log("inside succcess is 1");
//Insert into UserRole Table
createUserRole(
createResults.insertId,
(body.role_id = 2),
(err, results) => {
console.log(results);
if (err) {
console.log(err);
return res.status(500).json({
success: 0,
message: "DB error",
});
}
if (!err) {
console.log("Google User created successfully");
return res.status(200).json({
success: 1,
data: createResults,
});
}
}
);
}
});
}
});
},
};
Passport.js
const passport = require("passport");
const GoogleStrategy = require("passport-google-oauth20").Strategy;
const keys = require('../config/keys');
passport.use(
new GoogleStrategy(
{
clientID: process.env.clientID,
clientSecret: process.env.clientSecret,
callbackURL: "http://localhost:1111/api/users/auth/google/callback",
//passReqToCallback: true
},
(accessToken, refreshToken, profile, callback) => {
console.log("access token", accessToken);
console.log("refresh token", refreshToken);
console.log("profile", profile);
console.log("callback", callback);
}
)
);
The issue i am getting is i am not sure how to get the value from google authentication to be used in callback request and am not sure if callback is working or not. After i select the email id from google plus api client screen, it just goes into infinite loop and no data is getting inserted into db.
body = req.body;
body.googleId = req.body.profile.id;
body.firstName = req.body.profile.name.givenName;
body.lastName = req.body.profile.name.familyName;
body.email = req.body.profile.emails[0].value;
body.photoUrl = req.body.profile.photos[0].value;
Related
trying the user Auth first time and able to create the users, it seems the bcrypt password hash is working when registering the user as I can see the hashed password in the DB, However when I am trying to login with the same credential, getting an error Invalid email or password based on my code below:
const {
create,
getUserByUserId,
getUserByUserEmail,
} = require('./user-services')
const {genSaltSync, hashSync, compareSync} = require('bcrypt')
const {sign} = require('jsonwebtoken')
module.exports = {
createUser: (req, res) => {
const body = req.body;
const salt = genSaltSync(10);
body.password = hashSync(body.password, salt);
create(body, (err, results) => {
if (err) {
console.log(err);
return res.status(500).json({
success: 0,
message: "Database connection errror"
});
}
return res.status(200).json({
success: 1,
data: results
});
});
},
login: (req, res) => {
const body = req.body;
console.log(body.user_email)
getUserByUserEmail(body.user_email, (err, results) => {
if (err) {
console.log(err);
}
if (!results) {
return res.json({
success: 0,
data: "* Invalid email or password *"
});
}
const result = compareSync(body.password, results.password);
console.log(result)
console.log(results.password)
console.log(body.password)
if (result) {
results.password = undefined;
const jsontoken = sign({ result: results }, "test1234", {
expiresIn: "1h"
});
return res.json({
success: 1,
message: "Login successfully",
token: jsontoken
});
} else {
return res.json({
success: 0,
data: "Invalid email or password"
});
}
});
},
}
When console log, I can see the body. password and response from DB. Here is what I am getting in the console.log
Solved it. Modified MySQL column for Password to VARCHAR(1024). it was
limited to VARCHAR(56)
Hey guys I am currently am trying to do something similar to what is posted here:
How to authenticate Supertest requests with Passport?
as I would like to test other endpoints that require authentication but in addition need to pass in a jwt. Right now, I tested it on POSTMAN and on the browser and it seems like it's working fine, but my test cases keep on breaking. I have a login POST route that is setup like so:
AccountService.js
// Login POST route
router.post('/account_service/login', (req, res, next) => {
passport.authenticate('local-login', (err, user, info) => {
try {
if (err) {
const error = new Error('An Error occurred: Cannot find user');
return next(error);
} else if (!user) {
return res.redirect('/account_service/login');
}
req.login(user, { session: false }, (error) => {
if (error) {
return next(error);
}
const email = req.body.email;
const role = req.user[0].role;
const id = req.user[0].id;
const user = {
email: email,
role: role,
id: id
};
const accessToken = jwt.sign(user, config.ACCESS_TOKEN_SECRET, {
expiresIn: 28800 // expires in 8 hours
});
const cookie = req.cookies.cookieName;
if (cookie === undefined) {
// set a new cookie
console.log('setting new cookie');
res.cookie('jwt', accessToken, { maxAge: 900000, httpOnly: true });
res.send({ token: accessToken });
} else {
// cookie was already present
console.log('cookie exists', cookie);
}
res.redirect('/account_service/profile');
});
} catch (error) {
return next(error);
}
})(req, res, next);
});
After the user is authenticated, I assign a JSON web token to the user and place it in the cookie so it gets stored within the headers for authorized requests. Here is an example:
AccountService.js
// Get all users
router.get('/account_service/all_users', passport.authenticate('jwt', { session: false }), (req, res, next) => {
const sql = 'select * from user';
const params = [];
db.all(sql, params, (err, rows) => {
if (err) {
res.status(500).json({ error: err.message });
return;
}
res.json({
message: 'success',
data: rows
});
});
});
I use passport.authenticate to ensure that the jwt is valid. This GET request only works after I login with admin user account.
Within my passport file I have it setup like so:
passport.js
const LocalStrategy = require('passport-local').Strategy;
const db = require('../database.js');
const bcrypt = require('bcrypt');
const config = require('../config/config.js');
const JwtStrategy = require('passport-jwt').Strategy;
const cookieExtractor = function (req) {
var token = null;
if (req && req.cookies) token = req.cookies.jwt;
return token;
};
module.exports = function (passport) {
passport.serializeUser(function (user, done) {
done(null, user);
});
passport.deserializeUser(function (user, done) {
done(null, user);
});
passport.use('local-login', new LocalStrategy({
usernameField: 'email',
passwordField: 'password',
passReqToCallback: true
}, (req, email, password, done) => {
try {
const sql = `select * from user WHERE email = "${email}"`;
const params = [];
db.all(sql, params, (err, row) => {
if (err) {
return done(err);
}
if (!row.length || !bcrypt.compareSync(password, row[0].password)) {
return done(null, false, req.flash('loginMessage', 'Inavalid username/password combination. Please try again.'));
}
return done(null, row);
});
} catch (error) {
return done(error);
}
}));
const opts = {};
opts.jwtFromRequest = cookieExtractor; // check token in cookie
opts.secretOrKey = config.ACCESS_TOKEN_SECRET;
// eslint-disable-next-line camelcase
passport.use(new JwtStrategy(opts, function (jwtPayload, done) {
try {
const sql = `select * from user WHERE email = "${jwtPayload.email}"`;
const params = [];
db.all(sql, params, (err, row) => {
if (err) {
return done(err);
}
if (!row.length || !bcrypt.compareSync('admin', jwtPayload.role)) {
return done(null, false, { message: '403 Forbidden' });
}
return done(null, row);
});
} catch (error) {
return done(error);
}
}));
};
Here's where I get confused as my test cases break. I am trying to login before my test cases to allow my other test cases to run but I end up getting a 401 error. Here are my test cases:
accountservice.test.js
const app = require('../../app');
const supertest = require('supertest');
const http = require('http');
describe('Account Service', () => {
let server;
let request;
beforeAll((done) => {
server = http.createServer(app);
server.listen(done);
request = supertest.agent(server);
request.post('/account_service/login')
.send({ email: 'admin#example.com', password: 'admin' })
.end(function (err, res) {
if (err) {
return done(err);
}
console.log(res);
done();
});
});
afterAll((done) => {
server.close(done);
});
it('Test request all users endpoint | GET request', async done => {
const response = await request.get('/account_service/all_users');
expect(response.status).toBe(200);
expect(response.body.message).toBe('success');
expect(response.body.data.length).toBe(3);
done();
});
});
But my test cases fail as I get a 401 error when it expects a 200 success code.
I tried thinking of a way to extract the jwt from a cookie after the login call so that I can set up the headers for the /account_service/all_users GET request code but was unable to find a way using Supertest. I saw this post: Testing authenticated routes with JWT fails using Mocha + supertest + passport but saw that it gets the token from the body.
After messing around with my code, I ended up having issues with in-memory storage and running asynchronous db.run functions that would call every time I ran my server. So I used a file to store my data and ran my tests again and it ended up working!
Here was the faulty code:
const sqlite3 = require('sqlite3').verbose();
const md5 = require('md5');
const DBSOURCE = ':memory:';
const db = new sqlite3.Database(DBSOURCE, (err) => {
if (err) {
// Cannot open database
console.error(err.message);
throw err;
} else {
db.run(`CREATE TABLE user (
id INTEGER PRIMARY KEY AUTOINCREMENT,
name text,
email text UNIQUE,
password text,
status text,
comments text,
photos text,
CONSTRAINT email_unique UNIQUE (email)
)`,
(err) => {
if (err) {
// Table already created
console.log('Table already created');
} else {
// Table just created, creating some rows
const insert = 'INSERT INTO user (name, email, password, status, comments, photos) VALUES (?,?,?,?,?,?)';
db.run(insert, ['user_delete', 'user_delete#example.com', md5('admin123456'), 'pending_deleted', 'comment1,comment2', 'https://giphy.com/gifs/9jumpin-wow-nice-well-done-xT77XWum9yH7zNkFW0']);
db.run(insert, ['user_no_delete', 'user#example.com', md5('user123456'), 'active', 'comment1', 'https://giphy.com/gifs/cartoon-we-bare-bears-wbb-NeijdlusjcduU']);
db.run(insert, ['mikey', 'mikey#example.com', md5('mikey123'), 'pending_deleted', 'comment1', 'https://giphy.com/gifs/wwe-shocked-vince-mcmahon-gdKAVlnm3bmKI']);
}
});
}
});
module.exports = db;
I simply stored this data within a file and used this code instead:
const sqlite3 = require('sqlite3').verbose();
const DBSOURCE = 'mockdb.sqlite';
// Data inserted inside file
/*
db.run(insert, ['user_delete', 'user_delete#example.com', bcrypt.hashSync('admin123456', saltRounds), 'pending_deleted', 'comment1,comment2', 'https://giphy.com/gifs/9jumpin-wow-nice-well-done-xT77XWum9yH7zNkFW0', bcrypt.hashSync('user', saltRounds)]);
db.run(insert, ['user_no_delete', 'user#example.com', bcrypt.hashSync('user123456', saltRounds), 'active', 'comment1', 'https://giphy.com/gifs/cartoon-we-bare-bears-wbb-NeijdlusjcduU', bcrypt.hashSync('user', saltRounds)]);
db.run(insert, ['mikey', 'mikey#example.com', bcrypt.hashSync('mikey123', saltRounds), 'pending_deleted', 'comment1', 'https://giphy.com/gifs/wwe-shocked-vince-mcmahon-gdKAVlnm3bmKI', bcrypt.hashSync('user', saltRounds)]);
db.run(insert, ['admin', 'admin#example.com', bcrypt.hashSync('admin', saltRounds), 'active', 'admincomments', 'adminphoto', bcrypt.hashSync('admin', saltRounds)]);
console.log('last hit in database');
});
*/
const db = new sqlite3.Database(DBSOURCE, (err) => {
if (err) {
// Cannot open database
console.error(err.message);
throw err;
}
console.log('Connection successful!');
});
module.exports = db;
I also ended up using supertest.agent.
const app = require('../../app');
const supertest = require('supertest');
const http = require('http');
const db = require('../../database/database.js');
describe('Account Service', () => {
let server;
let request;
// Find cookie management option.
beforeAll(async (done) => {
server = http.createServer(app);
server.listen(done);
request = supertest.agent(server);
done();
});
And it ended up working and successfully solving my issue!
I've successfully set up the registration and login functionality using Express, MongoDB and Mongoose.
I would like to log when the user last visited the site once the user's credential is accepted in a lastConnection property of the user document,
I tried but "lastConnection" is null (see the line below where I add a comment)
router.post("/login", async function(req, res) {
const { errors, isValid } = validateLoginInput(req.body);
if (!isValid) {
return res.status(400).json(errors);
}
const email = req.body.email;
const password = req.body.password;
const user = await User.findOne({ email }).then(user => {
if (!user) {
errors.email = "Email already exists";
}
console.log("user ", user); <-- returns an object with the datas of user
bcrypt.compare(password, user.password).then(isMatch => {
if (isMatch) {
const payload = {
id: user.id,
name: user.name
};
user.lastConnection = new Date(); <-- doesn't work
jwt.sign(
payload,
keys.secretOrKey,
{
expiresIn: 7200
},
(err, token) => {
res.json({
success: true,
token: "Bearer " + token
});
}
);
} else {
errors.password = "Password is not correct";
// return res
// .status(400)
// .json({ passwordincorrect: "Password incorrect" });
}
});
});
return {
errors,
isValid: isEmpty(errors)
};
});
Any ideas? I think I have to do an update but I don't know where to put it
Try replacing user.lastConnection = new Date(); with
user.update({ lastConnection: new Date() })
.then( updatedUser => {
console.log(updatedUser)
// put jwt.sign code here
})
Not sure what I'm doing wrong, other than writing really messy code for a project I'm doing to learn Nodejs.
This used to be an async function/object but decided to get rid of try catch because my code ran twice for some reason I couldn't figure out.
Eliminating the try catch hasn't really stopped it from still running twice I think.
So the question: Why is my results.rows[0].email returning as undefined?
Sometimes it works sometimes it doesn't. I don't know why. Any help would rock.
router.post('/', (req, res, next) => {
const {password, email} = req.body
//var LoginPwd = await bcrypt.hash(password, 5);
const loginPlainPwd = password;
pool.query("SELECT password, id, email FROM companies_admins WHERE email=$1", [email], (err, results) => {
if (err)
{
throw err;
}
const dbemail = results.rows[0].email
const dbPwd = results.rows[0].password
const dbid = JSON.stringify(results.rows[0].id)
console.log('results.rows[0] = ' + results.rows[0])
console.log('loginPlainPwd = ' + loginPlainPwd)
console.log('dbPwd = ' + dbPwd)
//console.log(JSON.stringify(results.rows[0]))
//res.cookie('userId', id)
//res.sendFile(path.join(__dirname, './views/account.html'));
//bcrypt.compare(loginPlainPwd, dbPwd, (err, res) => {
if (loginPlainPwd != dbPwd)
{
console.log("loginPlainPwd != dbPwd")
/////////////////////////////////////////////?SHOULD THIS BE OUTSIE POOL.QUERY??????
console.log('err')
return res.status(401).json({
message: 'Auth failed'
});
}
else if (loginPlainPwd == dbPwd)
{
//token variable signage/creation with user data and expiration (i also included .env)
const token = jwt.sign(
{
email: dbemail,
userId: dbid,
},
process.env.JWT_KEY,
{
expiresIn: "1h"
},
);
console.log("passwords match: token created:" + token)
res.cookie('userId', token,)
console.log('cookie should be sent')
databaseJWTin(err, token, dbemail); // database function to store jwttoken from below to store jwt in database
console.log('databaseJWT function should have fired')
//had to use ../ below because path was going into routes directory for some reason
res.sendFile(path.join(__dirname, '../views/account.html'))
//return res.status(200).json({
// message: "Auth successful",
// token: token
//});
}
//res.sendFile(path.join(__dirname, './views/account.html'))
});
//res.sendFile(path.join(__dirname, './views/account.html'));
})
Please check whether result contains data in it.
router.post('/', (req, res, next) => {
const { password, email } = req.body
//var LoginPwd = await bcrypt.hash(password, 5);
const loginPlainPwd = password;
pool.query("SELECT password, id, email FROM companies_admins WHERE email=$1", [email], (err, results) => {
if (err) {
throw err;
}
if (results && results.length>0) {
const dbemail = results.rows[0].email
const dbPwd = results.rows[0].password
const dbid = JSON.stringify(results.rows[0].id)
console.log('results.rows[0] = ' + results.rows[0])
console.log('loginPlainPwd = ' + loginPlainPwd)
console.log('dbPwd = ' + dbPwd)
//console.log(JSON.stringify(results.rows[0]))
//res.cookie('userId', id)
//res.sendFile(path.join(__dirname, './views/account.html'));
//bcrypt.compare(loginPlainPwd, dbPwd, (err, res) => {
if (loginPlainPwd != dbPwd) {
console.log("loginPlainPwd != dbPwd")
/////////////////////////////////////////////?SHOULD THIS BE OUTSIE POOL.QUERY??????
console.log('err')
return res.status(401).json({
message: 'Auth failed'
});
}
else if (loginPlainPwd == dbPwd) {
//token variable signage/creation with user data and expiration (i also included .env)
const token = jwt.sign(
{
email: dbemail,
userId: dbid,
},
process.env.JWT_KEY,
{
expiresIn: "1h"
},
);
console.log("passwords match: token created:" + token)
res.cookie('userId', token)
console.log('cookie should be sent')
databaseJWTin(err, token, dbemail); // database function to store jwttoken from below to store jwt in database
console.log('databaseJWT function should have fired')
//had to use ../ below because path was going into routes directory for some reason
res.sendFile(path.join(__dirname, '../views/account.html'))
//return res.status(200).json({
// message: "Auth successful",
// token: token
//});
}
//res.sendFile(path.join(__dirname, './views/account.html'))
}
});
//res.sendFile(path.join(__dirname, './views/account.html'));
})
My React application is logging in successfully, and the backend is also working successfully.
Question: What is going wrong here? This works fine in Postman and retains my cookie/session information.
How do I make sure react retains the necessary authentication details for fetching from the API?
However, when rendering a new page and accessing my API to get user information, I'm received a 401 Unauthorized error.
I am using an emitter to fetch my data.
My API works fine using Postman but does not work in React. Code below:
Passport login & User serialization:
passport.serializeUser(function(user, done) {
done(null, user.id);
});
passport.deserializeUser(function(id, done) {
connection.query("select * from users where id = "+ id, function(err,user) {
done(err, user[0]);
});
});
passport.use('local-signup', new LocalStrategy({
usernameField : 'email',
passwordField : 'password',
passReqToCallback : true
},
function(req, email, password, done) {
connection.query("select * from users where email = '"+ email +"'",function(err,user) {
console.log(user);
console.log("above row object");
if (err)
return done(err);
if (user.length) {
return done(null, false);
} else {
var passwordHash = sha1(req.body.password)
var newUserMysql = new Object();
newUserMysql.name = req.body.name;
newUserMysql.password = passwordHash;
newUserMysql.email = req.body.email;
newUserMysql.group_id = req.body.group_id;
var insertQuery = "INSERT INTO users (name, password, email, group_id) values ('"+ req.body.name +"','"+ passwordHash +"','"+ req.body.email +"','"+ req.body.group_id +"')";
console.log(insertQuery);
connection.query(insertQuery,function(err,user){
newUserMysql.id = user.insertId;
return done(null, newUserMysql);
});
}
});
}));
My react emitter for profile information:
export default class ProfileController {
constructor(emitter) {
this.Emitter = emitter;
}
init() {
this.Emitter.on('GetMyUser', this.getMyUser.bind(this));
}
getMyUser() {
fetch(`${domain}/api/user/self`, {//This is where it appears to be failing!!!
method: 'GET',
credentials: 'include'
})
.then((res) => {
return res.json();
})
.then((res) => {
this.Emitter.emit('OnGetMyUser', res.data);
})
.catch((e) => {
this.Emitter.emit('OnGetMyUser', 'Failed to load User from Controller.');
Alert.error('Failed to load user.');
});
}
}
And finally, the route itself in my API:
app.get('/api/user/self', function(req, res) {//Get current user by id profile information
if (!req.isAuthenticated()) {
return res.status(401).send({code: 401, message: "Unauthorized"});
}
connection.query("SELECT id, name, email, group_id FROM users WHERE id = ?", [
req.user.id
], function(error, result) {
if (error) {
console.log(error);
return res.status(500).send({code: 500, message: "Internal Server Error"});
}
else if (result.rowCount === 0) {
return res.status(404).send({code: 404, message: "Not found"});
}
console.log(result);
return res.send(userFull(result));
});
});