Some questions:
Is this implementation of Passport for Node.js (Express) secure?
Is it a correct implementation of REST?
Can I store state in the session in this way?
/************************************
* Module dependencies.
************************************/
var express = require('express')
, passport = require('passport')
, BasicStrategy = require('passport-http').BasicStrategy
, crypto = require('crypto')
, http = require('http')
, path = require('path')
, fs = require('fs')
, app = express()
, service = require('./service');
var privateKey = fs.readFileSync('privatekey.pem').toString();
var certificate = fs.readFileSync('certificate.pem').toString();
var credentials = crypto.createCredentials({key: privateKey, cert: certificate});
/************************************
* Passport
************************************/
// User object supplied by MongoDB from the `service` object
//
// {
// id: 1,
// password: 'otherTestPass',
// salesmanId: 'A015',
// email: 'otheruser#email.com'
// }
passport.use(new BasicStrategy(
function(username, password, done) {
User.findOne({ username: username }, function (err, user) {
if (err) { return done(err); }
if (!user) { return done(null, false); }
if (!user.validPassword(password)) { return done(null, false); }
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);
});
});
/************************************
* Config
************************************/
// all environments
app.set('port', process.env.PORT || 8000);
app.set('views', __dirname + '/views');
app.set('view engine', 'jade');
app.use(express.favicon());
app.use(express.logger('dev'));
app.use(express.cookieParser());
app.use(express.bodyParser());
app.use(express.session({ secret: 'keyboard cat' })); // would normally have a randomly generated string
app.use(passport.initialize());
app.use(passport.session());
app.use(passport.authenticate('basic', {session: true}));
app.use(express.compress());
app.use(app.router);
app.use(express.static(path.join(__dirname, 'dist'))); // backbone SPA that talks to the '/api/' routes
// development only
if ('development' == app.get('env')) {
app.use(express.errorHandler());
}
/************************************
* Routes
************************************/
app.get('/api/session/user', function(req,res){
var user = req.user;
delete user.password;
res.json(user);
});
app.get('/api/customers', function(req,res){
service.findAllCustomersBySalesmanId(req.user.salesmanId, function(result){
res.json(result);
});
});
app.get('/api/products', function(req,res){
service.findAllProducts(function(result){
res.json(result);
});
});
app.get('/api/orders', function(req,res){
service.findAllOrders(function(result){
res.json(result);
});
});
// ... other routes
/************************************
* Server Start
************************************/
http.createServer(app)
.setSecure(credentials)
.listen(app.get('port'), function(){
console.log('Express server listening on port ' + app.get('port'));
}
);
Extra credit to anyone that can suggest:
Good way of moving the api routes to a different file and 'mounting' them at /api/ as part of the middleware configuration.
Internally securing the app by checking if a user is allowed to perform a certain operation.
Related
I've started to convert express project to nestjs. How should it work in Nestjs. Here is working code from Express.
(Code below just redirects to steam sign-in page)
/* eslint-disable space-before-function-paren */
// Require all the installs
var express = require('express');
var passport = require('passport');
var session = require('express-session');
var passportSteam = require('passport-steam');
var SteamStrategy = passportSteam.Strategy;
var app = express();
// Let's set a port
var port = 4000;
// Spin up the server
app.listen(port, () => {
console.log('Listening, port ' + port);
});
// Set up the SteamStrategy
// Required to get data from user for sessions
passport.serializeUser((user, done) => {
done(null, user);
});
passport.deserializeUser((user, done) => {
done(null, user);
});
// Initiate Strategy
passport.use(
new SteamStrategy(
{
returnURL: 'http://localhost:' + port + '/api/auth/steam/return',
realm: 'http://localhost:' + port + '/',
apiKey: 'My API key',
},
function (identifier, profile, done) {
process.nextTick(function () {
profile.identifier = identifier;
return done(null, profile);
});
}
)
);
app.use(
session({
secret: 'Whatever_You_Want',
saveUninitialized: true,
resave: false,
cookie: {
maxAge: 3600000,
},
})
);
app.use(passport.initialize());
app.use(passport.session());
// Routes
app.get('/', (req, res) => {
res.send(req.user);
});
app.get(
'/api/auth/steam',
passport.authenticate('steam', { failureRedirect: '/' }),
function (req, res) {
res.redirect('/');
}
);
app.get(
'/api/auth/steam/return',
passport.authenticate('steam', { failureRedirect: '/' }),
function (req, res) {
res.redirect('/');
}
);
The question is how to implement same in the nestjs???
Or if I want to implement middlewares for passport lib (serializeUser, deserializeUser), how should it happen, in nest official docs I found this examples of custom middlewares
export function logger(req: Request, res: Response, next: NextFunction) {
console.log(`Request...`);
next();
};
But how I should use passport middlware
Trying to use passport-saml connecting to ADFS.
The SAML Response is coming back with Successful status codes.
We get the following success code back:
"<samlp:StatusCode Value="urn:oasis:names:tc:SAML:2.0:status:Success" /></samlp:Status>"
But our Passport IsAuthenicated is always generating a false.
I have listed out all of our files used for this below and would appreciate any help.
server.js file:
const express = require('express');
const http = require('http');
const path = require('path');
const passport = require('passport');
const morgan = require('morgan');
const cookieParser = require('cookie-parser');
const bodyParser = require('body-parser');
const session = require('express-session');
const errorhandler = require('errorhandler');
var env = process.env.NODE_ENV || 'development';
const config = require('./config/config')[env];
console.log('Using configuration', config);
require('./config/passport')(passport, config);
var app = express();
app.set('views', __dirname + '/views');
app.set('view engine', 'jade');
app.use(cookieParser());
app.enable('trust proxy'); // add this line
app.use(bodyParser.json());
app.use(bodyParser.urlencoded({extended: false}));
app.use(session(
{
resave: true,
saveUninitialized: true,
secret: 'default',
proxy: true // add this line
}));
app.use(passport.initialize());
app.use(passport.session());
app.use(morgan('combined'));
function ensureAuthenticated(req, res, next) {
if (//req.isAuthenticated()
true
) {
console.log('req.isAuthenticated = ' + req.isAuthenticated());
return next(); }
else{
console.log('req.isAuthenticated = ' + req.isAuthenticated());
res.redirect('/login');
}
}
app.set('port', config.app.port);
require('./config/routes')(app, config, passport);
//ensure that ensureAuthenticated is in the get function call before master build
//ie app.get('/*', ensureAuthenticated, (req, res)
app.use(express.static(path.join(__dirname, 'public')));
app.get('/*', ensureAuthenticated, (req, res) => {
res.sendFile(path.join(__dirname, 'public/index.html'));
});
app.listen(app.get('port'), function () {
console.log('Express server listening on port ' + app.get('port'));
});
routes.js
module.exports = function (app, config, passport) {
app.get('/', function (req, res) {
res.redirect('/home')
});
app.get('/login',
passport.authenticate(config.passport.strategy,
{
successRedirect: '/',
failureRedirect: '/login'
})
);
app.post('/',
passport.authenticate(config.passport.strategy,
{
failureRedirect: '/',
failureFlash: true
}),
function (req, res) {
res.redirect('/');
}
);
app.get('/logout', function (req, res) {
req.logout();
// TODO: invalidate session on IP
res.redirect('https://redrectsite.com/?wa=signout1.0');
});
};
config.js
module.exports = {
development: {
app: {
name: 'Passport SAML strategy example',
port: process.env.PORT || 80
},
passport: {
strategy: 'saml',
saml: {
callbackUrl: process.env.SAML_CALLBACK_URL || 'https://oursite.com',
entryPoint: process.env.SAML_ENTRY_POINT || 'https://oursite.com/adfs/ls/idpinitiatedsignon',
issuer: process.env.SAML_ISSUER || 'https://oursite.com',
identifierFormat: null,
signatureAlgorithm: 'sha256',
authnContext: 'http://schemas.microsoft.com/ws/2008/06/identity/authenticationmethod/windows',
disableRequestedAuthnContext: true
//cert: process.env.SAML_CERT || null
}
}
}
};
passport.js
const SamlStrategy = require('passport-saml').Strategy;
module.exports = function (passport, config) {
passport.serializeUser(function (user, done) {
done(null, user);
});
passport.deserializeUser(function (user, done) {
done(null, user);
});
passport.use(new SamlStrategy(
{
callbackUrl: config.passport.saml.callbackUrl,
entryPoint: config.passport.saml.entryPoint,
issuer: config.passport.saml.issuer,
cert: config.passport.saml.cert,
identifierFormat: config.passport.saml.identifierFormat,
signatureAlgorithm: config.passport.saml.signatureAlgorithm,
authnContext: config.passport.saml.authnContext,
disableRequestedAuthnContext: config.passport.saml.disableRequestedAuthnContext
},
function (profile, done) {
return done(null,
{
id: profile.uid,
email: profile.email,
displayName: profile.cn,
firstName: profile.givenName,
lastName: profile.sn
});
})
);
};
I had a similar issue. If you look at what isAuthenticated() does, it's actually just checking a property within the request.session object.
https://github.com/jaredhanson/passport/blob/2327a36e7c005ccc7134ad157b2f258b57aa0912/lib/http/request.js#L86
req.isAuthenticated = function() {
var property = 'user';
if (this._passport && this._passport.instance) {
property = this._passport.instance._userProperty || 'user';
}
return (this[property]) ? true : false;
};
I'm not sure if it's passport or express-session, but once you get to the authentication method, the user object is stored at request.session.passport.user so if you like, you can directly verify that it's non-null instead of using the packaged isAuthenticated() method, which seems to check the wrong path.
My code started working after changing it to the following.
if (_.get(req, 'session.passport.user', null)) {
return next();
}
(lodash _.get for easier null-checking of the nested properties)
I am working on user login page with node.js, passport, and postgres. I thought I got user authentication working. However, when I try to change pages I get this error and my server won't load anything. {"message":"log is not defined","error":{}}. I have been stuck on this error for hours now and can't figure out what is causing it. I am guessing that the session is somehow getting messed up but I could be wrong. I am hoping someone knows the answer. Here is my relevant code.
I am extremely new to web development and because of that I am sure it is probably something simple I am missing.
main-config.js
(function(appConfig) {
'use strict';
// *** main dependencies *** //
const path = require('path');
const cookieParser = require('cookie-parser');
const bodyParser = require('body-parser');
const session = require('express-session');
const flash = require('connect-flash');
const morgan = require('morgan');
const nunjucks = require('nunjucks');
const passport = require('passport');
// *** view folders *** //
const viewFolders = [
path.join(__dirname, '..', 'views')
];
// *** load environment variables *** //
require('dotenv').config();
appConfig.init = function(app, express) {
// *** view engine *** //
nunjucks.configure(viewFolders, {
express: app,
autoescape: true
});
app.set('view engine', 'html');
// *** app middleware *** //
if (process.env.NODE_ENV !== 'test') {
app.use(morgan('dev'));
}
app.use(cookieParser());
app.use(bodyParser.json());
app.use(bodyParser.urlencoded({ extended: false }));
app.use(session({
secret: 'anything',
resave: false,
saveUninitialized: true
}));
app.use(passport.initialize());
app.use(passport.session());
app.use(flash());
app.use(express.static(path.join(__dirname, '..', '..', 'client')));
};
})(module.exports);
local.js
passport.use(new LocalStrategy({
usernameField: 'email',
passwordField: 'password'
},
(username, password, done) => {
return db.one("SELECT * " +
"FROM Users " +
"WHERE Email=$1", [username])
.then((result)=> {
return done(null, result);
})
.catch((err) => {
return done(null, false, {message:'Wrong user name or password'});
});
}));
passport.js
module.exports = () => {
passport.serializeUser((user, done) => {
done(null, user.userid);
});
passport.deserializeUser((id, done)=>{
log.debug("deserialize ", id);
db.one("SELECT * FROM User " +
"WHERE user_id = $1", id)
.then((user)=>{
done(null, user);
})
.catch((err)=>{
done(new Error(`User with the id ${id} does not exist`));
})
});
};
auth.js
router.post('/login', authHelpers.loginRedirect, (req, res, next) => {
passport.authenticate('local', (err, user, info) => {
if (err) { handleResponse(res, 500, 'error'); }
if (!user) {
console.log("User Not Found");
handleResponse(res, 404, 'User not found');
}
if (user) {
req.logIn(user, function (err) {
if (err) { handleResponse(res, 500, 'error'); }
handleResponse(res, 200, 'success');
});
}
})(req, res, next);
});
It's a clear error message - log is not defined in the following line in passport
log.debug("deserialize ", id);
Define your log object with a logger and everything should be good. Or just remove it for now.
I am using this Express Server to authenticate users by signing in by their steam account.
everything works now as expected.
What i want to know now, is how i can integrate this to my Angular2 Project, so i can use in the HomeComponent a Login Button which calls the Express Server.
Is it better to work with an Server.js or with a Server.ts Backend Server?
and will it be visible ? if yes, where should be its destination to garantuee security ?
Im new to this, thats why im so curious :)
thanks in advance
const express = require('express');
const session = require('express-session');
const FirebaseStore = require('connect-session-firebase')(session);
const firebase = require('firebase-admin');
const firebaseinit = firebase.initializeApp({
credential: ('**************************************'),
databaseURL: '**************************************'
});
// *************************************************************************************************************
var passport = require('passport'),
util = require('util'),
SteamStrategy = require('./').Strategy;
// *************************************************************************************************************
passport.serializeUser(function(user, done) {
done(null, user);
});
// *************************************************************************************************************
passport.deserializeUser(function(obj, done) {
done(null, obj);
});
// *************************************************************************************************************
passport.use(new SteamStrategy({
returnURL: 'http://localhost:3000/auth/steam/return',
realm: 'http://localhost:3000/',
apiKey: '**************************************'
},
function(identifier, profile, done) {
process.nextTick(function () {
profile.identifier = identifier;
return done(null, profile);
});
}
));
// *************************************************************************************************************
var app = express();
app.set('views', __dirname + '/signon/views');
app.set('view engine', 'ejs');
app.use(session({
store: new FirebaseStore({
database: firebaseinit.database()
}),
secret: 'keyboard cat',
name: 'name of session id',
resave: true,
saveUninitialized: true
}));
// *************************************************************************************************************
// *************************************************************************************************************
app.use(passport.initialize());
app.use(passport.session());
app.use(express.static(__dirname + '/../../public'));
// app.post('/auth/openid', passport.authenticate('openid'));
// *************************************************************************************************************
app.get('/auth/steam',
passport.authenticate('steam', { failureRedirect: '/' }),
function(req, res) {
res.redirect('/');
});
// *************************************************************************************************************
app.get('/auth/steam/return',
passport.authenticate('steam', { failureRedirect: '/' }),
function(req, res) {
res.redirect('/');
var db = firebase.database();
var ref = db.ref("/UserID");
var SteamUserID = ref.child(req.user.id);
SteamUserID.update({
Username : req.user.displayName,
UserImg : req.user.photos[2].value
});
});
// *************************************************************************************************************
app.get('/logout', function(req, res){
req.logout();
res.redirect('/');
});
// *************************************************************************************************************
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.listen(3000, function () {
console.log('listening on port 3000!')
})
function ensureAuthenticated(req, res, next) {
if (req.isAuthenticated()) { return next(); }
res.redirect('/');
}
console.log('Listening on port ' + app.listen);
I am new to javascript and node. I followed the guide from passportJS and I am getting the error "local strategy cannot be found". I do not know why. my code, basically taken from the website at this point.
var express = require('express')
, routes = require('./routes')
, user = require('./routes/user')
, http = require('http')
, path = require('path')
, mongoose = require('mongoose')
, passport = require('passport')
, LocalStrategy = require('passport-local').Strategy;
app.use(passport.initialize());
//to configure the passport
app.use(new LocalStrategy({
usernameField: 'username',
passwordField: 'password'
},
function(username, password, done){
console.log(username);
console.log(password);
People.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);
});
}
));
//route to authenticate the user
app.post('/login',
passport.authenticate('local', { successRedirect:'/accessed',
failureRedirect: '/access'})
);
My error is that "local strategy not found", I looked inside the local-strategy module and found the function that defines it. So I assume the error lies somewhere in the way I am attempting to access that variable.
My server is set up like
var app = express();
app.configure(function(){
app.set('port', process.env.PORT || 3000);
app.set('views', __dirname + '/views');
app.set('view engine', 'jade');
app.use(express.favicon());
app.use(express.logger('dev'));
app.use(express.bodyParser());
app.use(express.methodOverride());
app.use(passport.initialize());
app.use(app.router);
app.use(express.static(path.join(__dirname, 'public')));
});
app.configure('development', function(){
app.use(express.errorHandler());
});
Here's a boilerplate for using passport-local. The order in which the middleware is configured matters. It also implements serializeUser/deserializeUser which seem to be missing from your code.
var express = require('express')
, http = require('http')
, path = require('path')
, passport = require('passport')
, LocalStrategy = require('passport-local').Strategy;
var app = express();
passport.use(new LocalStrategy(function(username, password, done) {
// insert your MongoDB check here. For now, just a simple hardcoded check.
if (username === 'foo' && password === 'bar')
{
done(null, { user: username });
}
else
{
done(null, false);
}
}));
passport.serializeUser(function(user, done) {
// please read the Passport documentation on how to implement this. We're now
// just serializing the entire 'user' object. It would be more sane to serialize
// just the unique user-id, so you can retrieve the user object from the database
// in .deserializeUser().
done(null, user);
});
passport.deserializeUser(function(user, done) {
// Again, read the documentation.
done(null, user);
});
app.use(express.bodyParser());
app.use(express.cookieParser());
app.use(express.session({ secret: 'secret' }));
app.use(passport.initialize());
app.use(passport.session());
// route to authenticate the user
app.post('/login', passport.authenticate('local', {
successRedirect: '/accessed',
failureRedirect: '/access'
}));
// app.listen(3012);
When you use curl -v -d "username=foo&password=bar" http://127.0.0.1:3012/login you see you'll get redirected to /accessed, meaning the authentication worked.