I have a strange problem with my NodeJs - Express server which serves as a back-end for my mobile application.
The thing is that i send post requests to some endpoints like checkmail, checkusername from front-end using axios and it works, but the problem is it doesn't work for any other middleware function. I literally copied the same checkmail and just used different route and I get status 404 while with /checkmail it works!
Also, the /login does not work, im using express. Router in there.
Here is my app.js code:
const express = require("express");
const mongoose = require("mongoose");
const bodyParser = require("body-parser");
var cors = require("cors");
const User = require("./models/user");
var AuthController = require('./auth/authController');
const app = express();
let server = require("http").Server(app);
app.use(cors());
app.use(
bodyParser.urlencoded({
extended: true
})
);
app.use(bodyParser.json());
//Check if e-mail is aready in use, return "success" if not
app.use("/signup", (req, res) => {
User.findOne({
email: req.body.email
},
function (err, user) {
if (user) {
res.send("error");
} else {
res.send("success");
}
}
);
});
//Check if e-mail is aready in use, return "success" if not
app.use("/checkmail", (req, res) => {
User.findOne({
email: req.body.email
},
function (err, user) {
if (user) {
res.send("error");
} else {
res.send("success");
}
}
);
});
app.use('/login', AuthController);
const port = process.env.PORT || 8500;
server.listen(port, () => { });
Middleware should have third parameter next.
app.use("/checkmail", (req,res,next)=>{
//do something
})
You must have third parameter in middleware, it's callback to tell application go to next route.
app.use('/signup', (req, res, next) => { // Normal middleware, it must have 3 parameters
User.findOne({
email: req.body.email
},
function (err, user) {
if (user) {
next(new Error('Some thing wrong')); // Failed, we will go to error-handling middleware functions
} else {
next(); // Success and we will go to next route
}
}
);
});
app.get('/signup', (req, res, next) => { // This is "next route"
res.send('Yay, welcome to signup')
});
app.use((err, req, res, next) => { // error-handling middleware function, It must have 4 parameters
res.status(500).send(err)
});
You can find document in here: https://expressjs.com/en/guide/error-handling.html
Related
I've just included Passport js in my project for auth. But now my POST requests to create a new user hang and return an alert on the client saying "Not authorized" after I close the server. My old code (without Passport) still works so I don't think it's an issue with proxy-ing from client to server
The last console log I see is the first log in the user API, console.log('received request ', req.body)
There are no logged error messages, except when I stop the server I'll get
"Proxy error: Could not proxy request /api/user from localhost:3000 to http://localhost:5000/. [1] See https://nodejs.org/api/errors.html#errors_common_system_errors for more information (ECONNRESET).
///app.js (server)
const express = require("express");
const bodyParser = require("body-parser");
const cors = require('cors');
const mongoose = require("mongoose");
const routes = require("./routes"); //Used to end with /api
const path = require("path");
require("dotenv").config();
const passport = require('passport');
const app = express();
const port = process.env.PORT || 5000;
//database
mongoose
.connect(process.env.DB, { useNewUrlParser: true, useUnifiedTopology: true })
.then(() => console.log("Database connected successfully"))
.catch((err) => console.log(err));
mongoose.Promise = global.Promise;
app.use((req, res, next) => {
res.header("Access-Control-Allow-Origin", "*");
res.header(
"Access-Control-Allow-Headers",
"Origin, X-Requested-With, Content-Type, Accept"
);
next();
});
require('./config/passport');
app.use(cors());
app.use(bodyParser.json());
app.use(passport.initialize());
app.use("/", routes); //Load API - this folder has an index.js file
app.use((err, req, res, next) => {
console.log("$!$", err);
next();
});
app.listen(port, () => {
console.log(`Server running on port ${port}`);
});
///users.js (API for User model)
const passport = require('passport');
const express = require("express");
const router = express.Router();
const User = require("../models/User");
//Old - This works, which makes me think the issue is with Passport js
// router.post("/user", (req, res, next) => {
// try {
// User.create(req.body)
// .then((data) => res.json(data))
// .catch(next);
// } catch {
// res.json({
// error: "Failed to upload new user",
// });
// }
// });
//New
router.post("/user", (req, res, next) => {
console.log('received request ', req.body);
passport.authenticate('register', (err, hashedPassword, info) => {
if (err) {
console.log("Passport err on register ", err);
}
else if (info != undefined) {
console.log("Defined err ", info.message);
res.send(info.message);
} else {
req.login(user, err => {
const newUser = {
firstName: req.body.firstName,
lastName: req.body.lastName,
email: req.body.email,
password: hashedPassword,
};
User.create(newUser)
.then((data) => res.json(data))
.catch(next);
});
}
});
});
//passport.js (for auth)
const bcrypt = require('bcrypt');
const BCRYPT_SALT_ROUNDS = 12;
const passport = require('passport'),
localStrategy = require('passport-local').Strategy,
User = require('../models/User'),
JWTstrategy = require('passport-jwt').Strategy,
ExtractJWT = require('passport-jwt').ExtractJwt;
const opts = {
jwtFromRequest: ExtractJWT.fromAuthHeaderWithScheme('JWT'),
secretOrKey: 'secret key',
};
passport.use(
'register',
new localStrategy(
{
usernameField: 'email',
passwordField: 'password',
},
(email, password, done) => {
try {
console.log('searching for User');
User.findOne({ email })
.then(user => {
if (user != null) {
console.log('email already taken');
return done(null, false, { message: 'email already in use' });
} else {
console.log('email is available');
bcrypt.hash(password, BCRYPT_SALT_ROUNDS).then(hashedPassword => {
console.log('hashed password created for new user');
return done(null, hashedPassword, null);
});
}
})
} catch(err) {
done(err);
}
}
)
)
passport.authenticate in your code isn't used as a middleware but just like a regular function called inside the request handler. As of passport documentation you should call the returning function when used this way, like this:
passport.authenticate('register', (err, hashedPassword, info) => {
// ...
})(req, res, next); // <- ADD ARGUMENTS AND CALL
I'm having issues setting up passport authentication with my server. I've used passportjs before but with mongodb. I'm currently trying to setup my local strategy with postgressql but have had no luck. When going to a login POST route with passport.authenticate(), I'm not getting a cookie sent back to me. I'm not sure if I setup my server correctly with passportJS and with my postgres database hosted via Heroku.
require('dotenv').config(); //In order to gain access to our .env file
//process.env.YELP_API_KEY
const express = require("express");
const app = express();
const bodyParser = require("body-parser");
app.use(bodyParser.json()); //This is going to allow us to access data stored in 'body' when making a post or put request
app.use(bodyParser.urlencoded({extended: true}));
const { Pool } = require('pg');
const fs = require("fs"); //This is needed to convert sql files into a string
let port = process.env.PORT || 5000;
//session-related libraries
const session = require("express-session");
const passport = require("passport"); //This is used for authentication
const LocalStrategy = require("passport-local").Strategy;
const bcrypt = require("bcrypt");
//Setting up our session
app.use(session({
secret: process.env.SECRET,
resave: false,
saveUninitialized: false
}));
//Connection to database
const pool = new Pool({
connectionString: process.env.DATABASE_URL,
ssl: true
}); //This is used to connect to our remote postegres database hosted via Heroku
//initializing our session
app.use(passport.initialize());
app.use(passport.session()); //Telling our app to use passport for dealing with our sessions
//setting up our local strategy
passport.use('local', new LocalStrategy({passReqToCallBack: true},( username, password, cb )=> {
console.log("this is being executed");
pool.query("SELECT id, username, password from users where username=$1", [username], (err, result) => {
if(err){
return cb(err);
}
if(result.rows.length > 0){
const first = result.rows[0];
bcrypt.compare(password, first.password, (err, res) => {
if(res){
cb(null, {
id: first.id,
user: first.username
})
}
else {
cb(null, false);
}
})
}
else {
cb(null, false);
}
})
}));
passport.serializeUser(function(user, done){
console.log("serialize user is executing")
done(null, user.id);
})
passport.deserializeUser(function(id, done){
pool.query('SELECT id, username FROM users WHERE id = $1', [parseInt(id, 10)], (err, results) => {
if(err) {
return done(err)
}
done(null, results.rows[0])
});
});
app.post("/api/login", (req, res) => {
passport.authenticate('local', function(err, user, info){
console.log(user);
});
})
app.listen(port, function(){
console.log("Your app is running on port " + port);
});
Expected Results: user should be able to login with the post route "/api/login" but passport.authenticate is not working? Passport local strategy should also be correctly setup.
In your route app.post("/api/login", .... the passport.authenticate needs an access to the req and res.
There's more than one way to make it work.
app.post("/api/login", (req, res) => {
passport.authenticate('local', function(err, user, info){
console.log(user);
// make sure to respond to the request
res.send(user);
})(req, res); // <= pass req and res to the passport
})
// or
// use it as a middleware
app.post("/api/login", passport.authenticate('local'), (req, res) => {
console.log(req.user);
// make sure to respond to the request
res.send(req.user);
})
I would like to apologize in advance.
I'm not good at English. Also I'm not good at Node. Some "words" may be unsuited or wrong. I can not find any solutions in my language sphere. I'm writing this questions with GoogleTranslation's help.
MY EQUIPMENT
Ubuntu 16.04 local and virtualized on OSX
Node.js 8.11.4
Express 4.16.0
Passport 0.4.0
If you need more informations, I will answer.
MAIN QUESTION
I'm coding web application with two auth system. I want to auth these two auth work together at the same time.
My image is below.
Admin auth browser once. Then different users log-in and log-out. Without Admin, users can access limited page.
My code withdrown below.
var express = require("express");
var app = express();
var fs = require("fs");
var https = require("https");
var body_parser = require("body-parser");
var crypto = require("crypto");
app.use(body_parser.urlencoded({ extended: true }));
var admin_passport = require("passport");
var admin_passport_local = require("passport-local");
var admin_express_session = require("express-session");
app.use(admin_express_session({secret: 'admin_secret',resave: false,saveUninitialized: true, cookie: { secure: true }}));
app.use(admin_passport.initialize());
app.use(admin_passport.session());
var admin_LocalStrategy = admin_passport_local.Strategy;
admin_passport.use(new LocalStrategy({passReqToCallback: true,},
(req, username, password, done) => {
//not coding yes but not probrem
if(false){
return done("ERROR");
}else if(false){
return done(null, false);
}else if(true){
return done(null, username);
}
}
));
admin_passport.serializeUser(function(user, done) {
done(null, user);
});
admin_passport.deserializeUser(function(user, done) {
done(null, user);
});
function admin_isAuthenticated(req, res, next){
//here is probrem
if (req.isAuthenticated()) {
return next();
}
else {
res.redirect('/admin_login');
}
}
app.use((req,res,next)=>{
//here is probrem
app.locals.isAuthenticated = req.isAuthenticated();
next();
});
var user_passport = require("passport");
var user_passport_local = require("passport-local");
var user_express_session = require("express-session");
app.use(user_express_session({secret: 'user_ecret', resave: false,saveUninitialized: true, cokkie:{secure: true}}));
app.use(user_passport.initialize());
app.use(user_passport.session());
var user_LocalStrategy = user_passport_local.Strategy;
user_passport.use(new user_LocalStrategy({passReqToCallback: true,},
(req, username, password, done) => {
if(false){
return done("ERROR");
}else if(false){
return done(null, false);
}else if(true){
return done(null, username);
}
}
));
user_passport.serializeUser(function(user, done) {
done(null, user);
});
user_passport.deserializeUser(function(user, done) {
done(null, user);
});
function user_isAuthenticated(req, res, next){
if (req.isAuthenticated()) {
return next();
}
else {
res.redirect('/user_login');
}
}
app.use((req,res,next)=>{
app.locals.isAuthenticated = req.isAuthenticated();
next();
});
var ssl_options = {
key: fs.readFileSync('./cert/key.pem'),
cert: fs.readFileSync('./cert/cert.pem'),
};
var server = https.createServer(ssl_options, app);
app.get('/', (req, res) => {res.render('index', {});});
app.use('/admin_login', require('./admin_login'));
app.use('/admin_logout', (req, res) => {req.logout();res.redirect('./');})
app.use('/user_top', admin_isAuthenticated, require('./user_top'));
app.use('/user_login', admin_isAuthenticated,require('./user_login'));
app.use('/user_logout', (req, res) => {req.logout();res.redirect('./');})
server.listen(443);
and
var express = require('express');
var router = express.Router();
var passport = require('passport');
router.use((req, res, next) => {
next();
});
router.get('/', (req, res) => {
res.render('login',{});
});
router.post('/', passport.authenticate('local',{successRedirect: '/',failureRedirect: '/login',failureFlash: true,}),(req, res) =>{
});
module.exports = router;
I want to know how fix or change. If there are other way to solve this problem, welcome.
I would like to ask for cooperation.
Passport works only one login system?
req.login(), req.logout() req.Authenticated(), Passport-session ....
Many functions don't identify difference between two login system.
I have this simple enough express application which tries to use Active Directory to authenticate the user. Here is my setup:
const express = require('express');
const session = require('express-session');
const passport = require('passport');
const ActiveDirectoryStrategy = require('passport-activedirectory');
const PORT = process.env.PORT || 8080;
// AD configuration. Real values omitted.
const config = {
url: 'ldaps://...',
baseDN: '...',
username: '...',
password: '...'
};
const app = express();
app.use(session({
secret: 'mysessionsecret',
resave: true,
saveUninitialized: true
}));
app.use(passport.initialize());
app.use(passport.session());
passport.use('ad', new ActiveDirectoryStrategy(
{
ldap: config
},
(profile, ad, done) => {
// The problem is here! This never gets called!
console.log('ActiveDirectoryStrategy activated.');
done('ActiveDirectoryStrategy not implemented.');
}
));
// route middleware to ensure user is logged in
const isLoggedIn = (req, res, next) =>
req.isAuthenticated() ? next() : res.redirect('/auth');
app.get('/', isLoggedIn, (req, res) => {
res.end('You are logged in.');
});
app.get('/unsecured', (req, res) => {
res.end('You are not logged in.');
});
app.get('/auth', passport.authenticate('ad', {
successRedirect: '/',
failureRedirect: '/unsecured'
}));
app.listen(PORT);
console.log('Listening on port ' + PORT);
However, the verifier function I pass for the constructor ActiveDirectoryStrategy never gets called. (This is the function with the signature (profile, ad, done)).
I am sure that there is no problem with the LDAP configuration, because I can access the active directory just fine with activedirectory module with the same parameters:
const ActiveDirectory = require('activedirectory');
const ad = new ActiveDirectory(config);
ad.findUser('username', (err, user) => {
if (err) {
return console.log(err);
}
console.log(JSON.stringify(user));
// prints an object with user's info
});
So there must be a problem with my routing. What am I doing wrong? Why is my verifier function not getting called?
The problem was a misconception I had. I thought the Passport.js's authenticate middleware would perform the NTLM handshake for me. This is not the case. passport-activedirectory actually needs something like IISNode to run in front of it in order to perform the NTLM handshake. Since the request does not contain the authentication information,
I settled on using express-ntlm middleware as a result. express-ntlm gives you the UserName, DomainName, and Workstation properties you can use. But if you want to acquire the full AD profile for some reason, you could setup a custom passport strategy like so:
const ActiveDirectory = require('activedirectory');
const CustomStrategy = require('passport-custom');
passport.use('ntlm-ad-backend', new CustomStrategy((req, done) => {
let username = req.ntlm.UserName;
AD.findUser(username, (err, profile) => {
if (err) {
console.log(err);
done(err);
}
if (!profile) {
done(new Error(`User ${req.ntlm.UserName} not found in Active Director.`));
} else {
done(null, profile);
}
});
}));
And then,
app.get('/auth', passport.authenticate('ntlm-ad-backend', {
successRedirect: '/',
failureRedirect: '/unsecured'
}));
Note that you will also have to implement serializeUser and deserializeUser.
I have a tremendous headche with a problem when I try to login using Passport.
I'm making a post request to /login with an email and password. Passport authenticates it correctly, err isn't called and then return res.redirect('/user') gets called too but it returns 404 and doesn't redirect.
I don't know why this is happening. Here's my code:
Server (index.js):
const
express = require('express'),
app = express(),
mongoose = require('mongoose'),
passport = require('passport'),
cookieSession = require('cookie-session'),
bodyParser = require('body-parser'),
keys = require('./config/keys'),
user = require('./models/User'),
passportService = require('./services/passport'),
authRoutes = require('./routes/auth');
mongoose.connect(keys.mongoURI);
app.use(bodyParser.json());
app.use(
cookieSession({
maxAge: 15 * 24 * 60 * 60 * 1000,
keys: [keys.cookieKey]
})
);
app.use(passport.initialize());
app.use(passport.session());
passportService(passport);
authRoutes(app);
const PORT = process.env.PORT || 5000;
app.listen(PORT, () => {
console.log(`App listening on port ${PORT}`);
});
passportService (passport.js):
const LocalStrategy = require('passport-local').Strategy;
const mongoose = require('mongoose');
const User = mongoose.model('users');
module.exports = (passport) => {
passport.serializeUser((user, done) => {
done(null, user._id);
});
passport.deserializeUser((id, done) => {
User.findById(id).then(user => {
done(null, user);
});
});
passport.use(
new LocalStrategy(
{
usernameField: 'emailaddr',
passwordField: 'passwd'
},
function(username, password, done) {
User.findOne({ email: username }, function(err, user) {
if(err){
return done(err);
}
if(!user) {
console.log('User not found with email: ', username);
return done(null, false);
}
if(user.password != password) {
console.log('Invalid password');
return done(null, false)
}
return done(null, user);
});
}));
}
Authentication route:
const passport = require('passport');
module.exports = app => {
app.post('/api/login', function(req, res, next) {
passport.authenticate('local', function(err, user, info) {
if (err) { return next(err); }
if (!user) { return res.redirect('/login'); }
req.logIn(user, function(err) {
if (err) { return next(err); }
return res.redirect('/user');
});
})(req, res, next);
});
}
There is not route for /user because I'm working with React and React Router in the client.
Please I need your help!!!
I would suggest not using the custom callback on your authenticate function. Instead, check to see if after the api is called and authentication is run if you have a user attached to the request object.
// calling redirects from your api will be problematic with react router
// allow your front end to decide what route to call based on the response
app.post('/api/login', passport.authenticate('local'), function(req, res, next) {
if(req.user) {
res.json(req.user);
} else {
// handle errors here, decide what you want to send back to your front end
// so that it knows the user wasn't found
res.statusCode = 503;
res.send({message: 'Not Found'})
}
});