I'm currently trying to learn JWT and Passport for ExpressJS while trying them out but I can't seem to grasp the idea of how Passport works.
Here's what I have done initially in my ExpressJS application.
/api/login POST API
Accepts username and password
/api/login then creates a JWT with the username and password as the payload
The token is then responded to the client
I think my /api/login API simulates the general idea of JWT of hard authenticate once and respond with token.
/api/test GET API on the other hand
Only authenticated users can access
Simply returns "Hello World!"
What is the problem/s?
My code doesn't seem to validate the token (I tried putting the token in Authenticate header.
Where in my request should I include the token returned from /api/login when requesting to /api/test?
Now to my actual code:
app.js
var express = require("express");
var bodyParser = require("body-parser");
var jwt = require("jsonwebtoken");
var passport = require("passport");
var LocalStrategy = require('passport-local').Strategy;
var mySecret = "mySecret";
var app = express();
var port = process.env.PORT || 3000;
app.use(bodyParser.json());
passport.use(new LocalStrategy(
function (token, done) {
var credentials = jwt.verify(token, mySecret);
if (credentials.username == "test" && credentials.password == "test") {
return done(null, credentials);
} else {
return done(null, false);
}
}
));
app.use(passport.initialize());
app.post("/api/login", function (request, response) {
var user = {
"username": request.body.username,
"password": request.body.password
};
response.send(jwt.sign(user, "mySecret"));
});
app.get("/api/test", passport.authenticate("local", {
"session": false
}), function (request, response) {
response.send("Hello World!");
});
app.listen(port, function () {
console.log("Listening on port: " + port);
});
You need to configure jwtStratagy also to authenticate the user.
here is working example: -
const express = require("express");
const bodyParser = require("body-parser");
const jwt = require("jsonwebtoken");
console.log(jwt.verify);
const passport = require("passport"),
LocalStrategy = require("passport-local").Strategy;
const cors = require("cors");
const app = express();
app.use(cors());
app.use(bodyParser.json());
app.use(passport.initialize());
var secret = '11210646';
var JwtStrategy = require('passport-jwt').Strategy,
ExtractJwt = require('passport-jwt').ExtractJwt;
passport.use(new LocalStrategy({
usernameField: 'username',
passwordField: 'password',
passReqToCallback: true
},
function(req, username, password, done) {
console.log('ohh', username, password);
let err = null;
if (err) { return done(err); }
if (username != 'abhi') {
return done(null, false, { message: 'Incorrect username.' });
}
if (password != 'pass') {
return done(null, false, { message: 'Incorrect password.' });
}
return done(null, username);
}
));
app.post('/login', function(req, res, next) {
passport.authenticate('local', function(err, user, info) {
console.log(err, user, info);
if (err) { return next(err); }
if (!user) { res.send({ "status": info.message }); }
res.send({ "status": user });
})(req, res, next);
});
var opts = {
jwtFromRequest: ExtractJwt.fromAuthHeaderAsBearerToken(),
secretOrKey: secret,
issuer: 'jonu',
audience: 'jonu bhai',
passReqToCallback: false
};
app.post('/me2', function(req, res, next) {
passport.authenticate('jwt', { session: false }, function(err,user, info) {
if (err) { return next(err); }
if (!user) { res.send({ "status": info.message }); }
res.send({ "status": user });
})(req, res, next);
});
//jwt
passport.use(new JwtStrategy(opts, function(jwt_payload, done) {
let err = null;
if (err) {
return done(err, false);
}
if (jwt_payload) {
return done(null, jwt_payload);
}
else {
return done(null, false);
// or you could create a new account
}
}));
app.post('/signup', (req, res) => {
let token = jwt.sign({
user: {
id: "idididid",
name: "Abhishek Singh",
username: "abhishek11210646"
}
},
secret, {
algorithm: 'HS256',
expiresIn: '5h',
issuer: 'jonu',
audience: 'jonu bhai'
});
res.send({ "token": token });
});
app.get('/', (req, res) => {
res.send({ "status": "Up and Running..." });
});
app.listen(8080, () => {
console.log('server running');
});
Related
I have this session middleware:
app.use(session({
secret: "tarek",
resave: true,
saveUninitialized: true,
}));
and in this router i stored a variable this way:
app.post('/login', async(req, res)=>{
const {email, password} = req.body;
try {
const user = await User.findOne({email: email})
if (!user) {
res.json({message: "Can't find an account with this e-mail!"})
}else if(user && (await bcrypt.compare(password, user.pass))){
const token = jwt.sign({
email,
}, process.env.JWT_SECRET, {
expiresIn: process.env.JWT_EXPIRE,
});
req.session.token = token;
console.log(req.session.token)
res.json({token: token, id: user.name + user.lastname + user._id})
}else{
res.json({message: "Invalid password"}).status(400)
}
} catch (err) {
console.log(err.message)
res.json({message: "Error while trying to log in please try again later"})
}
})
and ofc when i console.log the req.session.token it works fine
but when i pass it to another router like this:
app.get('/isUSerAuth', verify.verifyjwt, async(req, res)=>{
console.log(req.session.token)
const token = req.session.token
res.status(200).json({auth: true, token: token, message: "you are authenticated!"})
})
so it says that the req.session.token is undefined
I know this question was asked many times in stack over flow. I tried every accepted answers but can't my local strategy into function. Here is my code
var express = require('express');
var app = express();
const passport = require('passport');
const LocalStrategy = require('passport-local').Strategy;
app.use(cookieParser()); // read cookies (needed for auth)
app.use(bodyParser.json({limit: '50mb'}));
app.use(bodyParser.urlencoded({limit: '50mb', extended: true}));
app.set('trust proxy', 1); // trust first proxy
app.use(session({
secret: '564sdf4as564f56a7s765s4afjkgadxjkbadksj',
resave: true,
saveUninitialized: true,
cookie: { secure: true }
}));
app.use(passport.initialize());
app.use(passport.session());
passport.use(new LocalStrategy({
usernameField:'userName',
passwordField:'password',
passReqToCallback : true
},function(request, userName, password, done) {
console.log(request);
UserAccount.findOne({'userName': userName} , function(err, user) {
if (err) return done(err);
if (!user) return done(null, false, 'Incorrect username.' );
user.verifyPassword(password, function(err, isMatch) {
if (isMatch) {
return done(null, user);
} else {
return done(null, false, 'Incorrect password.');
}
});
});
}));
passport.serializeUser(function(user, done) {
console.log('Serialize user called');
done(null, user.id);
});
passport.deserializeUser(function(id, done) {
console.log('Deserialize user called');
UserAccount.findById(id, function(err, user) {
done(err, user);
});
});
Then I created a router like
var router = express.Router();
require('./controllers/user')(router,passport);
app.use('/api',router);
Then in my user controller I created signIn function like
app.post('/signIn',function (request,response,next){
var variables = request.body;
console.log(variables);
passport.authenticate('local', function(error, user, info) {
console.log(user);
if (error) { console.log(error); return next(err); }
if (!user) { return response.redirect('/login'); }
response.logIn(user, function(err) {
if (err) { return next(err); }
return response.redirect('/users/' + user.username);
});
})(request, response, next);
});
Then I send a request from "Postman"
{
"userName":"karthik#abc.com",
"password":"qwerty"
}
My mongodb userName and password fields are same.
In my db there is an account with this user name and password. But every time it return 'user' as 'false' inside authenticate. I tried to console my request inside local strategy but it never gets called. I don't understand What I done wrong here? Can some one help to solve this? Thank you very much.
You should name local strategy and use it in authenticate.
Use like this passport.use('local-strategy',new LocalStrategy({});
and like passport.authenticate('local-strategy');
On my express js app, I have set a session cookie, however even after logging out, my cookie is not removed from the browser. Hence clicking signing in, I can log back in without valid authentication.
Here is my app.js:
'use strict';
var auth = require('http-auth');
var bodyParser = require('body-parser');
var config = require('./test/lib/utils/config');
var cookieParser = require('cookie-parser');
var express = require('express');
var mysql = require('mysql2');
var passport = require('passport');
var path = require('path');
var session = require('express-session');
var favicon = require('serve-favicon');
var Auth = require('./lib/auth');
var Utils = require('./lib/utils');
var admin = require('./routes/admin');
var committee = require('./routes/committee');
var index = require('./routes/index');
var logout = require('./routes/logout');
var professor = require('./routes/professor');
var roles = require('./routes/roles');
var creds = config.credentials.database;
var connection = mysql.createConnection(creds);
connection.connect();
var authentication = new Auth(connection);
var utils = new Utils(connection);
var basic = auth.basic({
realm: 'Welcome to My App',
file: path.resolve(__dirname, '.private', '.htpasswd')
}, function(username, password, cb) {
utils.getMemberId(username, function(err, id) {
if (err) return cb(err);
utils.isLoggedIn(id, function(err, isLoggedIn) {
if (err) return cb(err);
if (!isLoggedIn) {
authentication.logIn(id, function(err) {
if (err) return cb(err);
return cb(id);
});
} else {
return cb(id);
}
});
});
});
var app = express();
passport.use(auth.passport(basic));
// Setup strategy.
passport.serializeUser(function(user, done) {
done(null, user);
});
passport.deserializeUser(function(user, done) {
done(null, user);
});
app.use(favicon(path.join(__dirname, 'public', 'image', 'favicon.ico')));
app.use(express.static(path.join(__dirname, 'public')));
app.use(bodyParser.json());
app.use(bodyParser.urlencoded({ extended: false }));
app.use(cookieParser());
app.use(session({ name: 'sid', rolling: true, secret: 'keyboard cat', cookie: { maxAge: 30000, httpOnly: true }}));
app.use(passport.initialize());
app.use(passport.session());
// view engine setup
app.set('views', path.join(__dirname, 'views'));
app.set('view engine', 'ejs');
app.use('/', index);
app.use('/roles', [passport.authenticate('http', {session: true}), setUserId,
setUserRoles, setUserFirstName, setUserFullName], roles);
app.use('/roles/admin', admin);
app.use('/roles/committee', committee);
app.use('/roles/professor', professor);
app.use('/logout', performLogout, logout);
// catch 404 and forward to error handler
app.use(function(req, res, next) {
var err = new Error('Not Found');
err.status = 404;
next(err);
});
// error handler
app.use(function(err, req, res) {
// set locals, only providing error in development
res.locals.message = err.message;
res.locals.error = req.app.get('env') === 'development' ? err : {};
// render the error page
res.status(err.status || 500);
res.render('error');
});
function setUserId(req, res, next) {
console.log(JSON.stringify(req.session));
utils.getMemberId(req.session.passport.user, function(err, id) {
if (err) next(err);
req.session.passport.id = id;
next();
});
}
function setUserRoles(req, res, next) {
utils.getRoles(req.session.passport.id, function(err, roles) {
if (err) next(err);
req.session.passport.roles = roles;
next();
});
}
function setUserFullName(req, res, next) {
utils.getMemberFullName(req.session.passport.id, function(err, fname) {
if (err) next(err);
req.session.passport.fullname = fname;
next();
});
}
function setUserFirstName(req, res, next) {
utils.getMemberFirstName(req.session.passport.id, function(err, fname) {
if (err) next(err);
req.session.passport.fname = fname;
next();
});
}
function performLogout(req, res, next) {
authentication.logOut(req.session.passport.id, function(err) {
if (err) next(err);
next();
});
}
module.exports = app;
Here is my logout.js router:
'use strict';
var express = require('express');
var router = express.Router();
router.get('*', function(req, res) {
console.log('Session before logging out: ' + JSON.stringify(req.session));
req.session.destroy(function() {
res.clearCookie('connect.sid', { name: 'sid', rolling: true, secret: 'keyboard cat', cookie: { maxAge: 30000, httpOnly: true }});
res.redirect('/');
console.log('Session after logging out: ' + JSON.stringify(req.session));
});
});
module.exports = router;
I am unsure of what I am doing wrong to not clear the session cookie properly. Any help with guiding the right away will be appreciated.
I'm using express and passport to log users in, in one of my controllers, I want to access the current logged in user, but I'm confused about how to access them. req.user does not seem to exist
You must authenticate each time a user every request and set req.user with strategy. I use a strategy with the token.
Token is sent in the headers.
Instead of the token may be cookies or other strategy.
Below incomplete code, but can you help:
var bodyParser = require('body-parser');
var express = require('express');
var jwt = require('jsonwebtoken');
var LocalStrategy = require('passport-local').Strategy;
var BearerStrategy = require('passport-http-bearer').Strategy;
var app = express();
app.use(bodyParser.json());
//Local Strategy to login user with email and password
passport.use(new LocalStrategy({
usernameField: 'email',
passwordField: 'password'
},
function(email, password, done) {
usersRepository
.getUserByEmail(email)
.then(function(user) {
if (!!user && passwordHelper.verify(password, user.password, user.salt)) {
done(null, user);
} else {
done(null, false);
}
});
}));
//Bearer Strategy to auth user with token - run with every request
passport.use(new BearerStrategy(function(token, done) {
jwt.verify(token, 'secret', function(err, decoded) {
if (!err && decoded) {
done(null, decoded); // !!! here is set req.user - decode is my user from token
} else {
done(null, false);
}
});
}));
app.use(passport.initialize());
var bearerAuth = passport.authenticate('bearer', {
session: false
});
bearerAuth.unless = require('express-unless');
//Adding Bearer Strategy to all routing unless login
app.use(bearerAuth.unless({
path: [
'/login'
]
}));
passport.serializeUser(function(user, done) {
done(null, user);
});
passport.deserializeUser(function(user, done) {
done(null, user);
});
//Login - use Local Strategy
app.post('/login', passport.authenticate('local'), function(req, res) {
var accessToken = jwt.sign(req.user, 'secret', {
expiresIn: '7d'
});
res.send({
id: req.user.id,
accessToken: accessToken,
email: req.user.email,
isAdmin: req.user.is_admin
});
});
I'm trying to build authentication system with ExpressJS and PassportJS. For session store I use Redis. I wanna use Remember Me. Every time when the user signs in and has marked "remember me" check-box, it should automatically sign in by next visit on site. I have downloaded an example app form Github https://github.com/jaredhanson/passport-remember-me and change for my using.
var express = require('express')
, passport = require('passport')
, LocalStrategy = require('passport-local').Strategy
, mongodb = require('mongodb')
, mongoose = require('mongoose')
, bcrypt = require('bcrypt')
, SALT_WORK_FACTOR = 10
, RedisStore = require('connect-redis')(express);
mongoose.connect('localhost', 'test');
var db = mongoose.connection;
db.on('error', console.error.bind(console, 'connection error:'));
db.once('open', function callback() {
console.log('Connected to DB');
});
// User Schema
var userSchema = mongoose.Schema({
username: { type: String, required: true, unique: true },
email: { type: String, required: true, unique: true },
password: { type: String, required: true},
accessToken: { type: String } // Used for Remember Me
});
// Bcrypt middleware
userSchema.pre('save', function(next) {
var user = this;
if(!user.isModified('password')) return next();
bcrypt.genSalt(SALT_WORK_FACTOR, function(err, salt) {
if(err) return next(err);
bcrypt.hash(user.password, salt, function(err, hash) {
if(err) return next(err);
user.password = hash;
next();
});
});
});
// Password verification
userSchema.methods.comparePassword = function(candidatePassword, cb) {
bcrypt.compare(candidatePassword, this.password, function(err, isMatch) {
if(err) return cb(err);
cb(null, isMatch);
});
};
// Remember Me implementation helper method
userSchema.methods.generateRandomToken = function () {
var user = this,
chars = "_!abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890",
token = new Date().getTime() + '_';
for ( var x = 0; x < 16; x++ ) {
var i = Math.floor( Math.random() * 62 );
token += chars.charAt( i );
}
return token;
};
// Seed a user
var User = mongoose.model('User', userSchema);
var usr = new User({ username: 'bob', email: 'bob#example.com', password: 'secret' });
usr.save(function(err) {
if(err) {
console.log(err);
} else {
console.log('user: ' + usr.username + " saved.");
}
});
// Passport session setup.
// To support persistent login sessions, Passport needs to be able to
// serialize users into and deserialize users out of the session. Typically,
// this will be as simple as storing the user ID when serializing, and finding
// the user by ID when deserializing.
//
// Both serializer and deserializer edited for Remember Me functionality
passport.serializeUser(function(user, done) {
var createAccessToken = function () {
var token = user.generateRandomToken();
User.findOne( { accessToken: token }, function (err, existingUser) {
if (err) { return done( err ); }
if (existingUser) {
createAccessToken(); // Run the function again - the token has to be unique!
} else {
user.set('accessToken', token);
user.save( function (err) {
if (err) return done(err);
return done(null, user.get('accessToken'));
})
}
});
};
if ( user._id ) {
createAccessToken();
}
});
passport.deserializeUser(function(token, done) {
User.findOne( {accessToken: token } , function (err, user) {
done(err, user);
});
});
// Use the LocalStrategy within Passport.
// Strategies in passport require a `verify` function, which accept
// credentials (in this case, a username and password), and invoke a callback
// with a user object. In the real world, this would query a database;
// however, in this example we are using a baked-in set of users.
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: 'Unknown user ' + username }); }
user.comparePassword(password, function(err, isMatch) {
if (err) return done(err);
if(isMatch) {
return done(null, user);
} else {
return done(null, false, { message: 'Invalid password' });
}
});
});
}));
var app = express();
// configure Express
app.configure(function() {
app.set('views', __dirname + '/views');
app.set('view engine', 'ejs');
app.engine('ejs', require('ejs-locals'));
app.use(express.logger());
app.use(express.cookieParser());
app.use(express.bodyParser());
app.use(express.methodOverride());
app.use(express.session({
store: new RedisStore({ host: '127.0.0.1', port: 6379, prefix: 'chs-sess' }),
secret: '4Md97L1bL4r42SPn7076j1FwZvAiqube',
maxAge: new Date(Date.now() + 3600000)
}));
// Remember Me middleware
app.use( function (req, res, next) {
if ( req.method == 'POST' && req.url == '/login' ) {
if ( req.body.rememberme ) {
req.session.cookie.maxAge = 2592000000; // 30*24*60*60*1000 Rememeber 'me' for 30 days
} else {
req.session.cookie.expires = false;
}
}
next();
});
// Initialize Passport! Also use passport.session() middleware, to support
// persistent login sessions (recommended).
app.use(passport.initialize());
app.use(passport.session());
app.use(app.router);
app.use(express.static(__dirname + '/../../public'));
});
app.get('/', function(req, res){
res.render('index', { user: req.user });
});
app.get('/account', ensureAuthenticated, function(req, res){
res.render('account', { user: req.user });
});
app.get('/login', function(req, res){
res.render('login', { user: req.user, message: req.session.messages });
});
// POST /login
// Use passport.authenticate() as route middleware to authenticate the
// request. If authentication fails, the user will be redirected back to the
// login page. Otherwise, the primary route function function will be called,
// which, in this example, will redirect the user to the home page.
//
// curl -v -d "username=bob&password=secret" http://127.0.0.1:3000/login
//
/***** This version has a problem with flash messages
app.post('/login',
passport.authenticate('local', { failureRedirect: '/login', failureFlash: true }),
function(req, res) {
res.redirect('/');
});
*/
// POST /login
// This is an alternative implementation that uses a custom callback to
// acheive the same functionality.
app.post('/login', function(req, res, next) {
passport.authenticate('local', function(err, user, info) {
if (err) { return next(err) }
if (!user) {
req.session.messages = [info.message];
return res.redirect('/login')
}
req.logIn(user, function(err) {
if (err) { return next(err); }
return res.redirect('/');
});
})(req, res, next);
});
app.get('/logout', function(req, res){
req.logout();
res.redirect('/');
});
app.listen(3000, function() {
console.log('Express server listening on port 3000');
});
// Simple route middleware to ensure user is authenticated.
// Use this route middleware on any resource that needs to be protected. If
// the request is authenticated (typically via a persistent login session),
// the request will proceed. Otherwise, the user will be redirected to the
// login page.
function ensureAuthenticated(req, res, next) {
if (req.isAuthenticated()) { return next(); }
res.redirect('/login')
}
My app doesn't work with Remember Me, every time when I close the browser, I have to sign-in again. I don't know, what I have done wrong.
My second question is, how doe Remember Me works as usual? I have some idea but not exactly sure.