Login routes:
router.post("/login", async (req, res) => {
try {
const user = await User.findOne({
mobileNo: req.body.mobileNo,
});
if (!user) {
res.status(401).json("You are not registerd");
}
const password = res.body.password;
if (password === user.password) {
return res.status(200).json("You are logged in");
} else {
return res.status(501).json("Naah! wrong pass");
}
} catch {
(err) => {
res.status(500).json(err);
};
}
});
module.exports = router;
index.js:
app.use("/api/auth", authRoute);
import:
const authRoute = require("./routes/auth");
My postman image, I am not getting any result.
Your try-catch syntax is wrong, correct would be
try {
...
} catch(err) {
res.status(500).json(err);
}
With your syntax, when the catch block is reached, the res.status(500).json(err) statement is not executed, therefore the request never comes back.
In your try block, there are 3 responses available. If the first condition in the if block is also executed with another one of the responses in the below if-else condition this problem may occur. Because at a time, sending 2 responses is impossible. Therefore, you should return that response and terminate.
if (!user) {
return res.status(401).json("You are not registered");
}
Related
I have a react native project in which I'm calling some API's using redux-saga mechanism. Now when I added response interceptor for axios my saga api's are not working anymore. Does any knows how I can fix this?
here is the code for my axios instance class and response interceptor
const getLoggedInUser = async () => {
const savedUser = JSON.parse(
await getDataFromAsyncStorage(APP_CONSTANTS.SAVED_USER)
)
if (savedUser?.user_id != null) {
return savedUser
}
return null
}
const baseapi = axios.create({
baseURL: APP_CONSTANTS.BASE_URL,
headers: {},
})
baseapi.interceptors.request.use(
async (config) => {
const token = await getLoggedInUser()
const userId = token?.user_id
const authToken = token?.token
if (token) {
baseapi.defaults.headers.common['userId'] = token
baseapi.defaults.headers.common['token'] = authToken
}
return config
},
(error) => {
return Promise.reject(error)
}
)
// Response interceptor for API calls
baseapi.interceptors.response.use(
(response) => {
return response
},
async function (error) {
const originalRequest = error.config
if (error.response.status === 403 /* && !originalRequest._retry */) {
return baseapi(originalRequest)
}
return Promise.reject(error)
}
)
This is my saga class code and it fails directly when I add a response interceptor
function* getTopicList(action) {
try {
yield put({type: ACTION_TYPES.START_TOPIC_LIST})
const {payload} = action
const res = yield call(getAllTopicsOfBatch, payload)
if (res?.status == APP_CONSTANTS.SUCCESS_STATUS) {
yield put({
type: ACTION_TYPES.SET_TOPIC_LIST,
payload: {data: res?.data?.topics},
})
} else {
alert('OOPS Something went wrong! Please try again')
yield put({
type: ACTION_TYPES.ERROR_TOPIC_LIST,
payload: 'Something Went Wrong Please Try Again',
})
}
} catch (error) {
console.log('RESPONES error', error)
alert('OOPS Something went wrong! Please try again')
yield put({
type: ACTION_TYPES.ERROR_TOPIC_LIST,
payload: 'Something Went Wrong Please Try Again',
})
}
}
The code looks mostly fine, the only two things I found that are likely causing problems are:
In the request interceptors you are likely wrongly passing the whole token as userId instead of userId
baseapi.defaults.headers.common['userId'] = token // 'token' should be 'userId'
In the response interceptors error handler, you are not guaranteed to have 'response' property on error.
if (error.response.status === 403) // use error?.response
If neither of these things will fix your problem my guess is you have a problem in your endpoint and so you should examine the response errors you get to guide you.
Sending a logout request to my server but I'm never getting a reply. The logout function is being called and the userID key is being deleted from my redis cache but I never get a response. Here's my code.
export const logout = async (req, res) => {
console.log("logout called");
const { userID } = req.user;
client.del(userID.toString, (err, reply) => {
console.log("inside client.del");
if (err) {
return res.status(500);
} else {
return res.status(200);
}
});
};
Because of callback, you should use promise
export const logout = async (req, res) => {
return new Promise((resolve,reject) => {
console.log("logout called");
const { userID } = req.user;
client.del(userID.toString, (err, reply) => {
console.log("inside client.del");
if (err) {
reject(res.status(500));
} else {
resolve(res.status(200));
}
});
});
}
res.status() does not send a response from the server. All it does is set the status as a property on the response object that will go with some future call that actually sends the response.
It is meant to be used in something like this:
res.status(500).send("Database error");
If you look at the Express doc for res.status(), you will see these examples:
res.status(403).end()
res.status(400).send('Bad Request')
res.status(404).sendFile('/absolute/path/to/404.png')
And, see that they all are followed by some other method that actually causes the response to be sent.
And, if you still had any doubt, you can look in the Express code repository and see this:
res.status = function status(code) {
this.statusCode = code;
return this;
};
Which shows that it's just setting a property on the response object and not actually sending the response yet.
You can use res.sendStatus() instead which will BOTH set the status and send the response:
export const logout = (req, res) => {
console.log("logout called");
const { userID } = req.user;
client.del(userID.toString, (err, reply) => {
console.log("inside client.del");
if (err) {
res.sendStatus(500);
} else {
res.sendStatus(200);
}
});
};
Note, I removed the two return keywords since they don't accomplish anything useful in this particular context.
I also removed the async keyword from the function definition since it was not doing anything useful in this context.
I have this little code snippet executed during the user logout.
async function logoutAction(props) {
removeUser();
props.logoutUser();
}
The function inside removeUser() is as :
export const removeUser = async () => {
try {
await AsyncStorage.removeItem(Constant.storage.user_data);
await AsyncStorage.removeItem(Constant.storage.token);
await AsyncStorage.removeItem(Constant.storage.notification_token);
return true;
} catch (exception) {
return false;
}
}
This clears user related data from local storage.
Similarly, props.logoutUser() is a reference call to reducer which sets loggedIn status to false.
I'm having this issue that if the removeUser() function is called once, the axios http requests do not enter the interceptors anymore and every request catches an error 'undefined'. If this method is removed though, everything works fine.
I can get it to working state then by removing the interceptors once, performing a request and then adding the interceptors again, which I found after hours of here and there.
My interceptors are:
export const requestInterceptor = axios.interceptors.request.use(
async config => {
const token = await getToken();
if (token != '') {
config.headers.Authorization = token;
}
console.log('axios request', config);
return config;
},
error => {
// console.warn('on request error')
return Promise.reject(error);
},
);
export const responseInterceptor = axios.interceptors.response.use(
function(response) {
console.log('axios response', response);
// console.warn('on response success', response.status)
return response;
},
async function(error) {
if (error.response.status === 401) {
//logout user
return;
}
return Promise.reject(error);
},
);
I am using the #react-native-community/AsyncStorage package for maintaining local storage. I suspect that the issue might be in the removeItem method but I'm unsure as the official docs don't contain the removeItem method, or in the interceptor which doesn't seem faulty to me anyways.
What am I doing wrong here?? Please show me some light..
Or maybe try add a await before removeUser(); ?
async function logoutAction(props) {
await removeUser();
props.logoutUser();
}
The issue was quite silly and did not even concern AsyncStorage or removeItem and as Matt Aft pointed out in the comment, it was due to the call for token in the interceptor after it had been removed while logging out. So, replacing
const token = await getToken();
if (token != '') {
config.headers.Authorization = token;
}
by
await getToken()
.then(token => {
config.headers.Authorization = token;
})
.catch(_ => {
console.log('no token');
});
in the interceptor and returning promise from the getToken method did the thing.
Thanks to Matt and 高鵬翔.
I am having trouble with the bcrypt.compare portion of my code. My route is able to hash the password and store the password in the database. The database is able to store 255 characters and I have verified that the password is 60 characters long. Every time I compare the password to the hashed password on the db, I get a false returned on from bycrypt.compare.
Has anyone encountered this and know what I may be doing wrong?
Auth Route for creating the user in the database:
app.post('/register/local', async (req, res) => {
const hashedPassword = await bcrypt.hash(req.body.password, 10) || undefined
const existingLocalUser = await db.User.findOne({ where: { email: req.body.email } }) || undefined
if (!existingLocalUser) {
try {
const newUser = await db.User.create({
given_name: req.body.given_name,
family_name: req.body.family_name,
email: req.body.email,
password: hashedPassword,
}
)
res.redirect('/login')
} catch {
res.redirect('/register')
}
} else if (existingLocalUser.dataValues.google_id) {
const updateUser = await db.User.update(
{ password: hashedPassword },
{ where: { email: req.body.email } }
)
} else {
console.log("You already have an account. Please login.")
res.redirect('/login');
}
})
Local Strategy from Passport:
passport.use(new LocalStrategy( async (username, password, done) => {
const existingLocalUser = await User.findOne({ where: { email: username }})
if (!existingLocalUser) {
console.log("No user exisits")
return done(null, false)
}
console.log("password", password)
console.log("existingLocalUser.password", existingLocalUser.password)
await bcrypt.compare(password, existingLocalUser.dataValues.password, function(error, result) {
if (error) {
return done(error)
} else if (result) {
return done(null, existingLocalUser)
} else {
return done(null, false)
}
})
}
));
bcrypt.compare(password, existingLocalUser.password, function(error, result) {
if (error) {
return done(error)
} else if (result) {
return done(null, existingLocalUser)
} else {
return done(null, false)
}
})
You are trying to use callback and await together, kindly remove await and stick to callbacks or you refactor and use async-await alone
As #cristos rightly pointed out the problem could be that you are mixing up async/await and callbacks. Stick to one pattern.
Here's how your code would be with async/await,
try {
const result = await bcrypt.compare(password, existingLocalUser.password);
if (result) {
return done(null, existingLocalUser);
} else {
return done(null, false);
}
} catch (error) {
return done(error);
}
Also on another note, are you sure you are comparing the correct values?
Going by the code sample you have provided I can see that you are logging,
console.log("password", password)
console.log("existingLocalUser.password", existingLocalUser.password)
However, the values compared in bcrypt.compare() is different,
bcrypt.compare(password, existingLocalUser.dataValues.password)
I figured out why it wasn't working... React or Redux was masking the password with asterisks so changing it to a sting of asterisks which gets hashed..
I am trying to shorten my Express/Connect middleware pipeline by only calling certain middleware functions based on the requested path.
However, the following will fail:
_cookieParser(req, res, function(err) {
if(err) return next(err);
_session(req, res, function(err) {
if(err) return next(err);
_csrf(req, res, function(err) {
if(err) return next(err);
loadUserFromSession(req, res, function(err) {
if(err) return next(err);
if(req.method == "POST") {
_bodyParser(req, res, next);
} else {
next();
}
});
});
});
});
But this will work fine:
_cookieParser(req, res, function(err) {
if(err) return next(err);
_session(req, res, function(err) {
if(err) return next(err);
_csrf(req, res, function(err) {
if(err) return next(err);
_bodyParser(req, res, function(err) {
if(err) return next(err);
loadUserFromSession(req, res, next);
});
});
});
});
Where loadUserFromSession is:
function loadUserFromSession(req, res, next) {
if(req.session && req.session.userId) {
userFunctions.getUserById(req.session.userId, function(err, user) {
if(err) return next(err);
if(user) {
req.user = user;
return next();
} else {
req.session.destroy();
return next(new Error('Unauthenticated'));
}
});
} else {
return next(new Error('Unauthenticated'));
}
};
Why can I not call bodyParser() after loadUserFromSession()?
EDIT
Sorry for the lack of detail on the failure/unexpected outcome.
If I put bodyParser() or just json() (since the POST content is json) after loadUserFromSession(), the calls never return inside of json(). If I put breakpoints in node inspector on either res.on('data') or res.on('end') neither get tripped.
The source of the json middleware is below:
exports = module.exports = function(options){
var options = options || {}
, strict = options.strict !== false;
var limit = options.limit
? _limit(options.limit)
: noop;
return function json(req, res, next) {
if (req._body) return next();
req.body = req.body || {};
if (!utils.hasBody(req)) return next();
// check Content-Type
if ('application/json' != utils.mime(req)) return next();
// flag as parsed
req._body = true;
// parse
limit(req, res, function(err){
if (err) return next(err);
var buf = '';
req.setEncoding('utf8');
req.on('data', function(chunk){
buf += chunk <==BREAKPOINT NEVER GETS CALLED
});
req.on('end', function(){
var first = buf.trim()[0]; <==BREAKPOINT NEVER GETS CALLED
if (0 == buf.length) {
return next(400, 'invalid json, empty body');
}
if (strict && '{' != first && '[' != first) return next(400, 'invalid json');
try {
req.body = JSON.parse(buf, options.reviver);
next();
} catch (err){
err.body = buf;
err.status = 400;
next(err);
}
});
});
}
};
OK, consider this advice/code review as opposed to a specific answer, but hopefully this helps.
First, my guess is understanding the following will solve your problem and unconfuse you, although I can't say exactly why your second example above behaves differently from the first example given your isolated snippets, but do some experiments base on this information: For a given request, the series of events (data, end, etc) will fire once and only once. If a listener is not attached when they fire, that listener never gets called. If a listener gets attached after they fire, it will never get called. The bodyParser has code to avoid attempting to re-parse the same request body more than once (because the necessary events would never fire and the code would hang without responding to the request).
SO, I suspect that in your app you have app.use(express.bodyParser()) and that middleware is getting called and running prior to your custom stuff above. Thus, my suggestion, which also addresses your file upload security question is:
Don't install the bodyParser globally like you see in the examples: app.use(express.bodyParser()). This code is neat for day-1 example apps, but it's totally inappropriate for a production site. You should instead do something like:
app.post('/upload/some/file', express.bodyParser(), myUploadHandler);
And just use the body parser exactly where it's needed and nowhere else. Don't go further down your stack of bizarre nesty middleware. There are clean ways to get the functionality, security, and efficiency you need that work in HARMONY with the middleware stack design built into connect. In general, understand that you can use different middleware stacks on different paths and express/connect can be configured to nicely match what you want to happen on a path-by-path basis.