node.js request stuck in middleware function (authentication)? - node.js

I am building a node.js server to handle logins and actions in my iOS app. One of the functions I wrote is checkAuth, which is middleware for most of my requests to check if a user is authenticated and has permission to do what he wants to do.
Now I am facing a problem where sometimes, but not always, the middleware function (checkAuth) is stuck. I receive logs from this function in my console, but nothing from the request (which should happen after authentication is successful).
This is the function I currently wrote. It is not optimized yet as I am testing everything, but it should do what I want it to do (and it does, most of the time):
const saltRounds = process.env.JWT_ROUNDS
const secret = process.env.JWT_SECRET
const checkRefreshTime = 10 // set to 10 seconds for testing, will increase later
function checkAuth(req, res, next) {
var token = req.headers['x-access-token']
jwt.verify(token, secret, (error, decoded) => {
console.log("checking auth")
if(error) {
console.log(error)
res.json({ errorCode: 406 })
} else {
const decoded = jwt.verify(token, secret)
var checkTime = (Date.now() / 1000) - checkRefreshTime;
if (decoded.iat < checkTime) {
console.log("DEC:", decoded)
const userID = decoded.userID
const queryString = "SELECT userRefreshToken, userName, userDisplayName, userProfilePicURL, userDOB, userGender FROM users WHERE userID = ? LIMIT 1"
pool.getConnection(function(error, connection) {
if(error) {
console.log(error)
res.json({ errorCode: 500 })
}
connection.query(queryString, [userID], (error, selectRows, fields) => {
if(error) {
console.log(error)
res.json({ errorCode: 500 })
}
if(selectRows.length > 0) {
if(selectRows[0].userRefreshToken == decoded.userRefreshToken) {
var userAge = moment().diff(selectRows[0].userDOB, 'years');
const payload = {
userID: userID,
userName: selectRows[0].userName,
userDisplayName: selectRows[0].userDisplayName,
userProfilePicURL: selectRows[0].userProfilePicURL,
userRefreshToken: selectRows[0].userRefreshToken,
userAge: userAge,
userGender: selectRows[0].userGender
}
var newToken = jwt.sign(payload, secret, { expiresIn: '21d' });
console.log("new token sent ", newToken)
res.locals.authToken = newToken
console.log("moving to next")
return next()
} else {
console.log("wrong refresh token")
res.json({ errorCode: 405, authResult: false })
}
} else {
console.log("0 results found!")
res.json({ errorCode: 503, authResult: false })
}
connection.release()
})
})
} else {
console.log("moving to next 2")
return next()
}
}
})
}
It probably isn't the most beautiful code you have ever seen. That's not my issue at this moment - I will optimize at a later time. Right now I am concerned about the fact that sometimes the function is stuck after the second check. The last output I then receive is "DEC: " followed by the decoded token in my console (line 16).
Other useful information: I run my server on an Ubuntu 18.04 server from DigitalOcean and use forever to keep it running:
forever start --minUptime 1000 --spinSleepTime 1000 server.js
Anybody who knows why this is happening?
EDIT: as per comment, the definition of pool
var pool = mysql.createPool({
connectionLimit: 100,
host: process.env.DB_HOST,
user: process.env.DB_USER,
password: process.env.DB_PASS,
database: process.env.DB_BASE,
ssl : {
ca : fs.readFileSync('***********'),
key : fs.readFileSync('*********'),
cert : fs.readFileSync('********'),
},
charset : 'utf8mb4',
dateStrings: true
})

I don't see where pool is defined anywhere. That could be throwing an error in the server logs.
Put a console log in the connection function to check that it actually connected to mySQL since that is the next function in the chain.

Related

When I try to run a Post User Registration script in Auth0 it gives "Error! API Error. Please contact support if the problem persists" error

I am trying to use Auth0's actions for post user registration. When I try to test it via UI, it gives me an error like "Error! API Error. Please contact support if the problem persists" and in the console it only writes "Error: {}". The script I wrote for this action looks something like this:
const https = require('https');
const jsonwebtoken = require('jsonwebtoken');
/**
* #param {Event} event - Details about registration event.
*/
exports.onExecutePostUserRegistration = async (event) => {
const TOKEN_SIGNING_KEY = event.secrets.signingKey
const TOKEN_EXPIRATION_IN_SECONDS = 10
const payload = {
email: event.user.email,
name: event.user.given_name,
surname: event.user.family_name
};
const token = jsonwebtoken.sign(payload, TOKEN_SIGNING_KEY,
{ subject: "postUserRegistration",
expiresIn: TOKEN_EXPIRATION_IN_SECONDS });
console.log("Starting post user registration operations for user with email: ", payload.email);
return new Promise((resolve, reject) => {
const request = https.request(url, options,
(res) => {
if (res.statusCode === 200) {
resolve({statusCode: res.statusCode, headers: res.headers})
} else {
reject({
headers: res.headers,
statusCode: res.statusCode
})
}
res.on('error', reject);
});
request.on("error", function (e) {
console.error(e);
reject({message: {e}});
});
request.on("timeout", function () {
reject({message: "request timeout"});
})
request.end();
});
}
Can you help me about what exactly causes this problem?
In order to understand this problem I tried to assign the Promise to a variable and then see what it returned. The funny part was that it was "pending". It couldn't be "pending" in any way, because it everything was true it would be "resolved", else "rejected" and if there is a timeout/error from the request it would be "rejected".
It turns out that Auth0 has some limitations for actions and the endpoint called was stuck to our firewall which caused a timeout in Auth0 before an HTTPS timeout. As the request was broken halfway, it stayed as "pending".

NodeJS cannot import custom module although it exists

Note: What you see below is the updated description of my problem, because I have been going down a rabbit hole and finding the root cause of a problem.
So, I found what's causing it (read 'OLD DESCRIPTION' below to know the context), but I have zero idea why is it being caused. So, the thing is, apparently Node cannot find the utils.getHash function (I have a separate file called utils.js which exports the getHash function), so it is never called, and execution never moves forward.
utils.js
...
const getHash = (password) => {
return crypto.createHash('sha3-512').update(password).digest('hex')
}
...
module.exports = {
getHash: getHash
}
Someone help please :(
OLD DESCRIPTION
There's a weird problem I am facing. I wrote a backend API server in ExpressJS, and one of the task it performs is user authentication. I am using MongoDB as the database, and Mongoose to connect and perform operations on it.
The problem I am facing is that the checkUserCreds function does not proceed after a point (commented in code), and Express just returns a blank JSON response.
And I say it it's weird, because I tested with the SAME code just 2 days back, it worked correctly like it should.
user.js
userSchema.statics.checkUserCreds = function (email, password) {
return new Promise((resolve, reject) => {
// Execution goes upto '$and' line, then it goes nowhere; no exceptions are raised
User.findOne({
$and: [{ email: email }, { password: utils.getHash(password) }]
}, (err, userDoc) => {
if (err) {
reject({ status: "ERROR", message: err })
} else if (userDoc) { // If valid credential
console.log(`User with email '${email}' logged in`)
resolve({ status: "OK", message: "Login successful!" })
} else { // If invalid credential
reject({ status: "ERROR", message: "Invalid credential!" })
}
})
})
}
api.js
// Route - Login (POST: email, password)
router.post("/login", (req, res) => {
// If user is already logged in, reject further login
if (req.session.email) {
res.json({ status: "ERROR", message: "Already logged in!" }).status(403).end()
} else {
// Get data from body
var form = formidable()
form.parse(req, (err, fields, files) => {
if (err) {
res.json({ status: "ERROR", message: err }).status(500).end()
} else {
// Check if credentials are valid
User.checkUserCreds(fields.email, fields.password).then((result) => {
// This portion of code isn't reached either
req.session.email = fields.email
res.json(result).status(200).end()
}).catch((err) => {
res.json(err).status(401).end()
})
}
})
}
})
Can anyone tell me why this is happening?

Sending message from express backend to vuejs frontend

Hi so it's kind of complicated for me, hope anyone can help.
Here's the situation : i have an app divided server side with node/express and front side with Vuejs,
what I'm doing in the back is creating a user here's the code :
const createUser=(req, res, next) => {
console.log("register");
let con=req.con
let { email,password } = req.body;
console.log(req.body)
con.query(
`SELECT * FROM users
WHERE email = $1`,
[email],
(err, results) => {
if (err) {
console.log(err);
res.status(404).json({error: err});
}
console.log(results);
if (results.rows.length > 0) {
//throw new error_types.InfoError("user already exists");
res.status(200).json({error: "user already exists"});
} else {
const hashedPassword = bcrypt.hashSync(password, parseInt(process.env.BCRYPT_ROUNDS));
con.query('INSERT INTO users (email,password) VALUES ($1, $2)',
[email,password],
(err, results) => {
if (err) {
next(err);
}
res.json({info: "User inseted" });
}
);
}
}
);
}
so im checking if it already exists else register it in DB,all good here.
Now in my Vuejs part i have this :
REGISTER({ commit, dispatch, rootState }, { payload }) {
const {email,password} = payload
console.log(payload)
commit('SET_STATE', {
loading: true,
})
const register = mapAuthProviders[rootState.settings.authProvider].register
register(email,password)
.then(success => {
if (success) {
notification.success({
message: "Succesful Registered",
description: "You have successfully registered!",
})
router.push('/auth/login')
commit('SET_STATE', {
loading: false,
})
}
if (!success) {
commit('SET_STATE', {
loading: false,
})
}
})
},
Now the problem happens here as the registration is done all okay but when i use the same email again for another registration it said the same message successfully registred but do not get saved to DB now what i want is the message user aleady exists that appears.
Anyone can help me please?
Edited :added axios part
export async function register(email,password) {
return axios
.post('/register', {emailpassword,})
.then(response => {
if (response) {
const { token } = response.data
if (token) {
store.set('accessToken', token)
}
return response.data
}
return false
})
.catch(err => console.log(err))
}
Without seeing the actual source code that does the HTTP request from the client, it's hard to say exactly what the error handling looks like. The most obvious culprit is this:
res.status(200).json({error: "user already exists"});
You're responding with HTTP 200 OK when an error occurs. Typically, a client implementation will treat this as success. You should signal to clients that an error has occurred - for example, respond with a "409 Conflict". Also make sure the client's fetch() call (or whatever the client uses for talking to the server) does proper error handling (checks statusCode).
The code has another issue, however - a race condition. This is a case of a TOCTTOU (Time-of-Check to Time-of-Use), where a non-zero amount of time passes between the existence check (SELECT) and the INSERT. If two users are registering for the same e-mail at the same time, they could both get a success.
You should remove the check altogether and use uniqueness constraints offered by the database instead (UNIQUE INDEX). This way, the DB guarantees there can be no duplicates and you don't have to worry about race conditions.

OAuth2 idToken is verified, but Login Required error persists

So cutting right to the chase...
I'm integrating an Angular front-end with an express back-end.
The login happens using angularx-social-login in the front-end(trying to avoid redirects) and the idToken is sent to the back-end for verification. The scopes are added during the login at the front-end.
After using google-auth-library to verify the token everything is good and correct.
But once a service.people.connections.list() for getting google contacts is called a Login Required error persist. I've tried using the access token I get at the front-end, the payload I get from the verification and all that... I'm sure I'm missing a single step, but I have no clue.
req.headers.authorization here is the idToken.
const client = new OAuth2Client(CLIENT_ID);
async function verify() {
const ticket = await client.verifyIdToken({
idToken: req.headers.authorization,
audience: CLIENT_ID
});
const payload = ticket.getPayload();
console.log(payload);
const userid = payload['sub'];
const service = google.people({ version: 'v1' });
service.people.connections.list({
resourceName: 'people/me',
pageSize: 10,
personFields: 'names,emailAddresses',
}, (err, res) => {
if (err) return console.error('The API returned an error: ' + err);
const connections = res.data.connections;
if (connections) {
console.log('Connections:');
connections.forEach((person) => {
if (person.names && person.names.length > 0) {
console.log(person.names[0].displayName);
} else {
console.log('No display name found for connection.');
}
});
} else {
console.log('No connections found.');
}
});
};

Lambda function timing out after 10 seconds

Code:
const knex = require('knex')({
client: 'mysql',
connection: {
host: process.env.database_host,
user: process.env.database_user,
password: process.env.database_pass,
database: process.env.database_db,
charset: 'utf8'
}
});
const bcrypt = require('bcrypt');
const bookshelf = require('bookshelf')(knex);
const User = bookshelf.Model.extend({
tableName: 'users'
});
const checkValues = (values) => {
// todo: add data validation
return true;
};
exports.test = (database) => {
// todo: add tests
};
exports.handler = (event, context, callback) => {
let salt = bcrypt.genSaltSync();
let values = {
first_name: event.firstname,
last_name: event.lastname,
username: event.username,
date_of_birth: event.birthday,
password: bcrypt.hashSync(event.password, salt),
password_salt: salt
};
if (!checkValues(values)) {
callback(null, {
success: false,
error: {
id: 2,
details: 'data validation error'
}
});
context.done(null, "User not created");
return;
}
try {
new User({
'first_name': values.first_name,
'last_name': values.last_name,
'username': values.username,
'date_of_birth': values.date_of_birth,
'password': values.password,
'password_salt': values.password_salt
}).save();
callback(null, {
success: true
});
context.done(null, "User created");
} catch (err) {
console.log(err);
callback(null, {
success: false,
error: {
id: 1,
details: 'error inserting user into database'
}
});
context.done(null, "User not created");
}
};
I am trying to make a basic sign up api endpoint using AWS API Gateway and Lambda functions, however every time I post the information to the api gateway I get the error
{
"errorMessage": "2017-09-07T08:38:50.174Z f2368466-93a7-11e7-b4bc-01142a109ede Task timed out after 10.00 seconds"
}
I have tried using different database libraries but I seem to always be hitting the same problem. The database connection works I know this because the user does infact get added to the users table in the database and the password is successfully hashed..
I have also tried using asynchronous bcrypt but it doesn't make any difference to the result, it still does it but says it times out.
Lambda doesn't seem to be terminating properly, something keeps the process still running and I can't figure out what, any ideas?
i had the similar issue using API gateway invoking my lambda.
The default timeout for API gateway is 30 seconds. If your response is not ready within in 30 seconds, you will be timed out though your lambda would still run!
So may be try to get the response back within 30 seconds. If not have one lambda being invoked from the API and give the response back immediately and let the first lambda invoke your second lambda and that will run upto max time which is 5 mins.
Thanks

Resources