I am using passport-cas to implement CAS 2.0 on my web server. I am trying to create a dummy environment where I just implement login functionality. It seems that the CAS login implementation is successful, I get redirected to the CAS login page but upon logging in the URL I am getting is http://localhost:3000/cas?ticket=ST-439273-mNA1Jeea4lEjK5vzJJuJmHHZ72w-vm-cas6prdapp-01 instead of redirecting to a page. I am not sure what is going on and can't find any similar answers online.
Here is my code containing the configuration of passport.
const app = express()
const CasStrategy = require('passport-cas').Strategy
const cors = require('cors')
const passport = require('passport')
const session = require('express-session')
const authRoute = require("./routes/auth.js");
app.use(session({
secret: "this is totally secret",
resave: false,
saveUninitialized: false,
}))
app.use(passport.initialize());
app.use(passport.session());
app.use(cors({
origin: "http://localhost:3000",
methods: "GET, POST,PUT, DELETE",
credentials: true,
}))
// app.use("/auth", authRoute)
passport.use(new CasStrategy({
version: 'CAS3.0',
ssoBaseURL: 'https://secure.its.yale.edu/cas',
serverBaseURL: 'http://localhost:3000/cas',
validateURL: '/validate'
}, function(profile, done) {
var login = profile.user;
User.findOne({login: login}, function (err, user) {
if (err) {
return done(err);
}
if (!user) {
return done(null, false, {message: 'Unknown user'});
}
return done(null, user);
});
}));
passport.serializeUser(function (user, done) {
done(null, user.netId);
});
passport.deserializeUser(function (netId, done) {
done(null, {
netId,
});
});
const casLogin = function (
req, res, next
) {
passport.authenticate('cas', function (err, user, info) {
if (err) {
return next(err);
}
if (!user) {
req.session.messages = info.message;
return res.redirect('/check');
}
req.logIn(user, function (err) {
if (err) {
return next(err);
}
req.session.messages = '';
return res.redirect('/after');
});
})(req, res, next);
};
app.get("/cas", casLogin)
app.listen("5000", () =>{
console.log("Server is running on port 5000");
})
Related
I have followed the steps described in the documentation of passportJS to configure local authentication. I pretty much copy-pasted their code to test if it works.
However, I now have 2 (related?) problems:
deserializeUser is never being called
req.user is undefined
I tried pretty much everything (changing the middleware orders, putting withCredentials: true on the client side, ...) that is mentioned in related posts, but nothing seems to work. Does someone maybe see what I'm doing wrong here?
index.js
app.use(express.json()); // to send data to the database
app.use(express.urlencoded({ extended: true }));
app.use(
cors({
origin: "http://localhost:3000", // location of the react app
credentials: true,
})
);
app.use(
session({
secret: "secretcode",
resave: false,
saveUninitialized: false,
})
);
app.use(passport.session());
app.use(cookieParser("secretcode"));
app.use(passport.initialize());
config(passport);
app.post("/login", passport.authenticate('local', { failureRedirect: '/login', failureMessage: true }),
function(req, res) {
res.redirect('/~' + req.user.username);
});
app.get("/user", (req, res) => {
res.send(req.user);
});
config.js
export const config = (passport) => {
passport.use(new LocalStrategy(function verify(username, password, cb) {
db.query('SELECT * FROM users WHERE username = ?', [ username ], function(err, user) {
if (err) { return cb(err); }
if (!user) { return cb(null, false, { message: 'Incorrect username or password.' }); }
bcrypt.compare(password, user[0].password, (err, result) => {
if (err) { return cb(err); }
if (result === true) {
return cb(null, user[0]);
}
return cb(null, false, { message: 'Incorrect username or password.' });
});
});
}));
passport.serializeUser(function(user, cb) {
process.nextTick(function() {
return cb(null, user.id);
});
});
passport.deserializeUser(function(id, cb) {
db.query('SELECT * FROM users WHERE id = ?', [ id ], function(err, user) {
if (err) { return cb(err); }
return cb(null, user);
});
});
};
I believe that your problem may be because you have not included app.use(passport.authenticate('session')). Try that.
You can also debug further by passing a path into the ensureLogIn middleware as I've had problems with that in the past. It helps debug if you know what is being redirected.
Please find a working barebones attached here for a working passport-local and express example without all of the bloat. I think comparing this with your application would be the best bet.
Let me know if this helps.
const express = require('express')
const passport = require('passport')
const LocalStrategy = require('passport-local')
const session = require('express-session')
const ensureLogIn = require('connect-ensure-login').ensureLoggedIn
const cookieParser = require('cookie-parser')
const app = express()
const port = 3000
const ensureLoggedIn = ensureLogIn('/not-logged-in')
app.use(express.json())
app.use(express.urlencoded({ extended: true }))
app.use(session({
secret: 'keyboard cat',
resave: false,
saveUninitialized: false
}))
app.use(passport.authenticate('session'))
app.use(passport.initialize())
app.use(cookieParser())
app.get('/', (req, res) => {
res.json({ hello: 'world' })
})
app.all('/secure', ensureLoggedIn, (req, res) => res.json({ is: 'secure' }))
app.listen(port, () => {
console.log(`Example app listening on port ${port}`)
})
passport.use(new LocalStrategy({
usernameField: 'username',
passwordField: 'password',
session: true
},
function (username, password, cb) {
if (username !== 'hello' && password !== 'world') {
console.log('verify', username, password, 'fail')
return cb(null, false)
} else {
console.log('verify', username, password, 'pass')
return cb(null, { username: 'hello' })
}
}
))
passport.serializeUser(function (user, cb) {
process.nextTick(function () {
console.log('serializeUser', user)
cb(null, user)
})
})
passport.deserializeUser(function (user, cb) {
process.nextTick(function () {
console.log('deserializeUser', user)
return cb(null, user)
})
})
app.post('/logins',
passport.authenticate('local', {
// successReturnToOrRedirect: '/',
failureRedirect: '/login-fail',
failureMessage: true
}),
function (req, res) {
console.log('logged in', req.params, req.user)
// res.send('Logged in')
res.redirect('/secure')
}
)
I am working on a full stack web application and am setting up the register and login routes I have connected to a local mongodb for the user model and am now working on setting up a passport config to make authentication is easy (and just for practice in the library). The thing is, that even when I hard code return done(null, user); into the config, it still returns false.
To begin with, this is my login POST route for the login page:
router.post('/', async function(req, res, next) {
if (!req.isAuthenticated()) {
passport.authenticate('local', function(err, user, info) {
if (err) { return next(err); }
if (!user) { return res.redirect('/'); }
req.logIn(user, function(err) {
if (err) { return next(err); }
return res.redirect('/account');
});
})(req, res, next);
}
});
This is my server.js code:
if (process.env.NODE_ENV !== "production") {
require('dotenv').config({ path: '.env' });
}
// Dependencies
const express = require('express');
const mongoose = require('mongoose');
const bodyParser = require('body-parser');
const session = require('express-session');
const app = express();
// Setup
app.set('view engine', 'ejs')
app.use(express.urlencoded({ extended: false }))
app.use(express.static(__dirname + '/public'));
app.use(session({ secret: 'yeet', resave: false, saveUninitialized: false }));
// Passport Setup
const passport = require('passport');
require('./config/passport')(passport);
app.use(passport.initialize());
app.use(passport.session());
// MongoDB
mongoose.connect(process.env.MONGO_URL, { useNewUrlParser: true, useUnifiedTopology: true, 'useCreateIndex': true });
mongoose.connection.on('error', error => console.log(error));
mongoose.connection.once('open', () => console.log("Connected To Database"));
// Routes
const indexRouter = require('./routes/index');
const trendingRouter = require('./routes/trending');
const accountRouter = require('./routes/account');
app.use('/', indexRouter);
app.use('/trending', trendingRouter);
app.use('/account', accountRouter);
// Port
app.listen(3030);
and this is my config file itself:
const LocalStrategy = require('passport-local').Strategy;
const passport = require('passport');
const bcrypt = require('bcryptjs');
const User = require('../models/User');
module.exports = function(passport) {
passport.use(new LocalStrategy(
function(username, password, done) {
User.findOne({ userName: username }, function (err, user) {
if (err) return done(err);
if (!user) return done(null, false, { message: 'Incorrect username.' });
bcrypt.compare(password, user.userPassword, (err, isMatch) => {
if (err) throw err;
if (isMatch) return done(null, user);
return done(null, false, { message: 'Password incorrect' });
});
return done(null, user);
});
}
));
passport.serializeUser(function(user, done) {
done(null, user.id);
});
passport.deserializeUser(function(id, done) {
User.findById(id, function(err, user) {
done(err, user);
});
});
};
Now something interesting is, that I don't think my local config file is ever called. I have tried adding a console.log("test") into it, and that message never appears in the terminal. I have followed the docs though as closely as I can and have even gone through a series of tutorials to find my answers, but have come up empty handed.
What's my problem? Thank you for your time and answers.
I figured it out. The problem was my input fields on my view were not called username and password as they should be per the docs. That will make it so that the config file can actually retrieve the necessary information from the input fields.
I'm implementing a simple login for my node app with Passport Local, Express, Next.js, and MongoSession store.
Everything works well, except my app runs deserializeUser for every single request. This results in my db being hit 10+ times for any app interaction
Based on this post https://github.com/jaredhanson/passport/issues/14#issuecomment-4863459 I know that my requests for static assets are hitting the middleware stack.
Most of the requests are for the path /_next/static*
I have tried and failed to implement express.static as shown in the above example. Please help me figure out how avoid calling deserializeUser on every request.
Thanks!
Here's my code:
app.js
const app = next({ dev });
const handle = app.getRequestHandler();
app.prepare().then(async () => {
const server = express();
server.use(helmet());
server.use(express.static(path.join(__dirname, '_next', 'static')));
server.use(express.json());
auth({ ROOT_URL, server });
api(server);
routesWithSlug({ server, app });
sitemapAndRobots({ server });
server.get('*', (req, res) => {
const url = URL_MAP[req.path];
if (url) {
app.render(req, res, url);
} else {
handle(req, res);
}
});
server.listen(port, (err) => {
if (err) throw err;
logger.info(`> Ready on ${ROOT_URL}`);
});
});
module.exports = { app };
auth.js
function auth({ ROOT_URL, server }) {
const dev = process.env.NODE_ENV !== 'production';
const MongoStore = mongoSessionStore(session);
const sess = {
name: 'builderbook.sid',
secret: process.env.sessSecret,
store: new MongoStore({
mongooseConnection: mongoose.connection,
ttl: 14 * 24 * 60 * 60, // expires in 14 days
}),
resave: false,
saveUninitialized: false,
cookie: {
httpOnly: true,
maxAge: 14 * 24 * 60 * 60 * 1000, // expires in 14 days
},
};
if (!dev) {
server.set('trust proxy', 1);
sess.cookie.secure = true;
}
server.use(session(sess));
server.use(passport.initialize());
server.use(passport.session());
server.use(bodyParser.urlencoded({ extended: false }));
passport.serializeUser((user, done) => {
console.log('serializeUser');
done(null, user.id);
});
passport.deserializeUser((id, done) => {
console.log(`deserializeUser, id: ${id}`);
User.findById(id, User.publicFields(), (err, user) => {
done(err, user);
});
});
const verifyLocal = async (req, email, password, done) => {
console.log({ email, password, req });
const { firstName, lastName } = req.body;
try {
// signInOrSign up the user to MongoDb
const user = await User.signInOrSignUp({
email,
password,
firstName,
lastName,
});
console.log(user);
if (!user) {
return done(null, false);
}
if (!User.verifyPassword(email, password)) {
return done(null, false);
}
return done(null, user);
} catch (err) {
console.log(err); // eslint-disable-line
return done(err);
}
};
passport.use(
new LocalStrategy(
{
usernameField: 'email',
passReqToCallback: true,
},
verifyLocal,
),
);
}
module.exports = auth;
authroutes.js
router.post('/login', passport.authenticate('local', { failureRedirect: '/fail' }), (req, res) => {
res.redirect('/');
});
router.get('/logout', (req, res) => {
req.logout();
res.redirect('/login');
});
module.exports = router;
This code seemed to solve the problem. Thanks to Tima over at Builderbook!
https://github.com/builderbook/builderbook/issues/229
server.get('/_next*', (req, res) => {
handle(req, res);
});
server.get('/static/*', (req, res) => {
handle(req, res);
});
I'm new to NodeJS and I try to build a login/registration system. Registration works fine but I'm currently unable to login.
I find a example app using passport and nodejs, so based on this example I build the registration form and the login form.
http://blog.robertonodi.me/node-authentication-series-email-and-password/
When I try to login I get an 'Unknown authentication strategy "local" error'. Can anybody explain what I'm doing wrong?
My code
(edit: added some changes from answers/comments and filenames)
Express config (config/express.config.js)
app.use(session({
store: new MongoStore({
url: 'mongodb://' + config.url + ':' + config.port + '/' + config.name
}),
secret: 'secretkey',
key: 'skey.sid',
resave: false,
saveUninitialized: false,
cookie : {
maxAge: 604800000 //7 days in miliseconds
}
}));
app.use(passport.initialize());
app.use(passport.session());
require(path.join(__dirname, 'auth.config'))(passport); //Load passport config
app.use(function(req, res, next) {
req.resources = req.resources || {};
// res.locals.app = config.app;
res.locals.currentUser = req.user;
res.locals._t = function (value) { return value; };
res.locals._s = function (obj) { return JSON.stringify(obj); };
next();
})
Passport config (config/auth.config.js)
var path = require('path');
var passport=require('passport');
var User = require(path.join(__dirname, '..', 'models', 'user.model'));
module.exports = function(passport) {
passport.serializeUser(function(user, done){
done(null, false);
});
passport.deserializeUser(function(id, done){
console.log("deserializeUser called", id);
User.findById(id, function (err, user) {
done(err, user);
});
});
//load strategy files
require(path.join(__dirname, 'strategies', 'local-strategy'));
//TODO: Facebook
//TODO: Twitter
//TODO: Google
}
Local strategy (/config/strategies/local-strategy.js)
var passport = require('passport');
var LocalStrategy = require('passport-local').Strategy;
var mongoose = require('mongoose');
var User = mongoose.model('User');
module.exports = function () {
console.log("LocalStrategy called");
passport.use(new LocalStrategy({
usernameField : 'username',
passwordField : 'password'
},
function(username, password, done) {
User.authenticate(username, password, function(err, user) {
if (err) {
return done(err);
}
if(!user) {
return done(null, false, {message: 'Invalid username or password'});
}
return done(null, user);
})
}))
}
Auth Controller (login only) (/controllers/auth.controller.js)
module.exports.loginUser = function(req,res, next) {
console.log("Auth.config", path.join(__dirname, 'strategies', 'local-strategy'))
passport.authenticate('local', function (err, user, info) {
if (err || !user) {
console.log("Error", info);
return res.status(400).send(info);
}
req.logIn(user, function(err) {
if (err) {
return next(err);
// return res.status(404).send("Username or password incorrect");
}
})
res.status(200).json(user);
})(req, res, next);
}
You forgot to import passport config file in your app.js.
import passport config after initializing passport.
app.use(passport.initialize());
app.use(passport.session());
// Add the line below, which you're missing:
require('./path/to/passport/config/file')(passport);
i have done like this and it worked
$npm install passport-local
var passport = require('passport');
var LocalStrategy = require('passport-local').Strategy; /* this should be after passport*/
passport.use(new LocalStrategy(
function(username, password, done) {
User.findOne({ username: username }, function(err, user) {
if (err) { return done(err); }
if (!user) {
return done(null, false, { message: 'Incorrect username.' });
}
if (!user.validPassword(password)) {
return done(null, false, { message: 'Incorrect password.' });
}
return done(null, user);
});
}
));
this fix the error. need to use 'local' when you create a new LocalStrategy
passport.use('local', new LocalStrategy({
usernameField:'useName',
passwordField: 'password',
passReqToCallback: true
I am trying to use passport local auth with sequelize . When I submit login form, the request/respond cycle never end and there is no error message in the terminal .
Here are all of my codes:
app.js
var Sequelize = require('sequelize'),
express = require('express'),
bodyParser = require('body-parser'),
cookieParser = require('cookie-parser'),
passport = require('passport'),
LocalStrategy = require('passport-local').Strategy,
User = require('./models/users');
........ and other imports.....
//route import , model injection
var auth = require('./routes/auth')(User);
.......................
app.use(session({
store: new RedisStore(),
secret: 'keyboard cat',
resave: false,
saveUninitialized: false
}));
app.use(passport.initialize());
app.use(passport.session());
passport.serializeUser(function(user, done) {
console.log(user);
done(null, user.id);
});
passport.deserializeUser(function(id, done) {
User.findById(id).then(function(user){
done(null, user);
}).catch(function(e){
done(e, false);
});
});
passport.use(new LocalStrategy(
function(username, password, done) {
User.findOne({where: {username: username}}).then(function(err, user) {
if (err) { return done(err); }
if (!user) {
console.log('Incorrect username.');
return done(null, false, { message: 'Incorrect username.' });
} else if (password != user.password) {
console.log('Incorrect password');
return done(null, false, { message: 'Incorrect password.' });
} else {
console.log('ok');
done(null, user);
}
});
}
));
and routes/auth.js :
var express = require('express'),
passport = require('passport');
var routes = function(User) {
var router = express.Router();
// routes for registration
router.route('/register')
.get(function(req, res) {
res.render('register');
})
.post(function(req, res) {
User.count().then(function(number) {
if (number >= 1) {
res.redirect('/auth/login');
} else {
User.create({
username: req.body.username,
password: req.body.password
});
res.redirect('/auth/login');
}
});
});
//routes for login
router.route('/login')
.get(function(req, res) {
res.render('login');
})
.post(function(req, res) {
passport.authenticate('local', { successRedirect: '/dashboard',
failureRedirect: '/auth/login' });
});
return router;
};
module.exports = routes;
Why does the request/response cycle never end?
Your current middleware definition for './login' POST is incorrect and does not send a response, which is why it doesn't end (until it times out).
Instead of calling passport.authenticate in a middleware function, the result of calling passport.authenticate should be used as middleware itself. I suggest the following:
router.route('/login')
.get(function(req, res) {
res.render('login');
})
.post(passport.authenticate('local', { successRedirect: '/dashboard',
failureRedirect: '/auth/login' });
);
See http://passportjs.org/docs/authenticate for an example.
Race condition in registration code
You didn't ask about this, but there is a race condition in your middleware for './register' POST.
User.create returns a promise for saving the created user. Until that promise is resolved there is no guarantee that the user exists in the backing datastore. However, immediately after calling create, your code redirects to the login endpoint which would query the database for that user.
Here is some code that avoids this problem:
User.create({ ... })
.then(function() {
res.redirect('/auth/login');
})
.catch(function(err) {
// Handle rejected promise here
})
The catch is included because it is always good practice to handle rejected promises and thrown exceptions.