Skip to content

Instantly share code, notes, and snippets.

@samuelhei
Created August 12, 2015 14:29
Show Gist options
  • Star 4 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save samuelhei/7371e62ae8f18e8811e6 to your computer and use it in GitHub Desktop.
Save samuelhei/7371e62ae8f18e8811e6 to your computer and use it in GitHub Desktop.
Passport config for local authentication with Amazon Dynamodb
/*
Passport config for local authentication with Amazon Dynamodb
*/
var passport = require("passport");
var LocalStrategy = require('passport-local').Strategy;
var path = require('path');
var User = require(path.join(__dirname, '/class/User'));
passport.serializeUser(function(user, done) {
done(null, user.login);
});
passport.deserializeUser(function(login, done) {
//return the user object to callback done
//Conect to Dynamodb
var ddb = require('dynamodb').ddb({
accessKeyId: process.env.DYNAMODB_ACCESSKEYID,
secretAccessKey: process.env.DYNAMODB_SECRETACCESSKEY,
endpoint: process.env.DYNAMODB_ENDPOINT
});
//return user by login
ddb.getItem('user', login, null, {}, function(err, item, cap) {
done(err, item);
});
});
passport.use(new LocalStrategy(
function(user, pass, done) {
//Conect to Dynamodb
var ddb = require('dynamodb').ddb({
accessKeyId: process.env.DYNAMODB_ACCESSKEYID,
secretAccessKey: process.env.DYNAMODB_SECRETACCESSKEY,
endpoint: process.env.DYNAMODB_ENDPOINT
});
//return user by login
ddb.getItem('user', user, null, {}, function(err, item, cap) {
if (err) {
//return the response from callback when an error happen
return done(err);
} else {
if (item && User.hash(pass, item.salt) === item.hash) {
//return the response from callback when the login is ok
return done(null, item);
} else {
//return the response from callback when the login is invalid
return done(null, false, {
message: 'Login Invalid'
})
}
}
});
}));
module.exports = passport;
@julianbei
Copy link

just as a small improvement suggestion with ES6 and AirBnB styleguide

const passport = require('passport');
const LocalStrategy = require('passport-local').Strategy;
const path = require('path');
const User = require(path.join(__dirname, '/class/User'));

// Conect to Dynamodb
const ddb = require('dynamodb').ddb({
  accessKeyId: process.env.DYNAMODB_ACCESSKEYID,
  secretAccessKey: process.env.DYNAMODB_SECRETACCESSKEY,
  endpoint: process.env.DYNAMODB_ENDPOINT,
});

passport.serializeUser((user, done) => {
  done(null, user.login);
});

passport.deserializeUser((login, done) => {
  // return user by login
  ddb.getItem('user', login, null, {}, (err, item) => {
    done(err, item);
  });
});

passport.use(new LocalStrategy((user, pass, done) => {
  // return user by login
  ddb.getItem('user', user, null, {}, (err, item) => {
    const message = 'Login Invalid';
    if (err) return done(err);
    // return the response from callback when the login is ok
    if (item && User.hash(pass, item.salt) === item.hash) return done(null, item);
    return done(null, false, { message });
  });
}));

module.exports = passport;

@julianbei
Copy link

julianbei commented Sep 9, 2016

you have some vulnerabilities in your code though.
I would not recommend using this snippet.
I recommend Building a User Object like the one used in jareds example:

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); }
      if (!user.verifyPassword(password)) { return done(null, false); }
      return done(null, user);
    });
  }
));

And be careful about salting. If you save a salt per user, and someone is able to steal database objects, he also has the matching salt.
A salt should be saved somewhere else.
Security-wise it does not make a difference if you have the same salt for every user or use an individual salt. The aim of a salt is to increase the entropy of brute attacks on the raw hashes.
So basically, if you save the salt next to the hash, it prevents the hacker from attacking all the passwords at the same time, which is more time consuming but not really safer as long as you use a crappy hash function (unless you use bcrypt or PBKDF2 etc..).
one good explanation of this problem is here

I just wanted to say this to warn unexperienced developers.

@gazoakley
Copy link

In contrast to @julianbei - storing the salt as part of the protected credential is fine according to OWASP:

Scheme security does not depend on hiding, splitting, or otherwise obscuring the salt

https://www.owasp.org/index.php/Password_Storage_Cheat_Sheet

Basically it shouldn't matter if the salt is exposed - it's just meant to make it difficult for someone to quickly recover all passwords from a database. What is important is that every credential should have a unique salt - do not use one salt for all records. If you can build a rainbow table based on the same salt used for all credentials you can more easily recover all credentials en masse. If you're using bcrypt it will generate a unique salt per credential by default (the salt is normally part of the generated output), although you should ideally now use argon2.

@thanniermalai
Copy link

On using the above code. I'm facing the " TypeError: callback.call is not a function issue"

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment