Skip to content

Instantly share code, notes, and snippets.

@alonbardavid
Created April 22, 2013 05:48
Show Gist options
  • Star 4 You must be signed in to star a gist
  • Fork 1 You must be signed in to fork a gist
  • Save alonbardavid/5432646 to your computer and use it in GitHub Desktop.
Save alonbardavid/5432646 to your computer and use it in GitHub Desktop.
var config = {
"database": {
"connection": "mongodb://localhost/scrapService"
},
"cookieSecret": "EhAIj3NmtJ1sem5StHXV0Mk"
};
require("./user")
var express = require('express')
, mongoose = require('mongoose')
, User = mongoose.models["User"]
, loginApp = require('./login')
, _ = require("underscore");
var app = express();
mongoose.connect(config.database.connection);
app.set("json spaces", true);
app.set("json replacer", function (key, value) {
if (value && value._id) {
value.id = value._id;
value._id = undefined;
}
if (key == "__v" || key == "updatedAt" || key == "createdAt" || key == "_id") return undefined;
return value;
})
app.configure(function () {
app.set('views', __dirname + '/views');
app.set('view engine', 'jade');
app.use(express.bodyParser());
app.use(express.cookieParser(config.cookieSecret));
app.use(express.session());
app.use(express.methodOverride());
app.use('/rest/user', loginApp());
app.use(app.router);
app.use(express.static(__dirname + '/public'));
}
app.get("/rest/something/to/authenticate",loginApp.authenticate,function(req,res,next){})
var express = require("express")
, mongoose = require("mongoose")
, _ = require('underscore')
, security = require('./security')()
var User;
function register(req,res,next) {
var options = req.body;
if (!req.body.userInfo) return res.send(400, "UserInfo is required");
var entity = User.buildSecure(req.body.userInfo);
delete options.userInfo;
User.findOne({ "email": entity.email }, function (err, dbEntity) {
if (err) {
next(err);
}
if (dbEntity === null || dbEntity == undefined || dbEntity.length == 0) {
savedEntity.password = undefined;
login(req,res,savedEntity);
} else {
res.send(409, "Entity already exists");
}
});
}
function loginByUserPass(req, res, email, password,onSuccess) {
User.findByPassword(email, password, function (user) {
if (_.isString(user)) {
res.send(403, user);
} else {
if (req.body.rememberMe) {
var token = security.getRandomSalt()
, expires = new Date(Date.now() + 900000);
res.cookie("rmToken", { id: user._id,
token: token
}
, {
expires: expires,
httpOnly: true,
signed: true
});
User.findByIdAndUpdate(user, { $addToSet: { tokens: { token: token, expires: expires}} },
function (err, dbUser) {
if (err) throw err;
login(req, res, dbUser);
});
} else {
login(req, res, user);
}
}
});
}
function login(req,res,user) {
req.session.user = user;
res.json({ success: true,
userInfo: user,
rememberMe: req.body.rememberMe ? true : false
});
}
function loginByToken(req, res, userId, token,onSuccess) {
User.findById(userId).exec(function (err, user) {
if (!user) {
res.send(403, "user unknown");
} else {
var dbToken = _.find(user.tokens, function (item) { return item.token == token });
if (dbToken) {
dbToken.token = security.getRandomSalt();
dbToken.expires = new Date(Date.now() + 90000000)
res.cookie("rmToken", { id: user._id,
token: dbToken.token
}
, {
expires: dbToken.expires,
httpOnly: true,
signed: true
});
User.update({ _id: user._id, 'tokens._id': dbToken._id }, { $set: { 'tokens.$.token': dbToken.token, 'tokens.$.expires': dbToken.expires} }, function (err, dbUser) {
if (err) throw err;
req.body.rememberMe = true;
login(req, res, dbUser)
});
} else {
res.send(403, "bad token");
}
}
})
}
function logout(req,res) {
var async = false;
if (req.signedCookies.rmToken) {
var user = req.user;
var tokenUser = req.signedCookies.rmToken.id;
var token = req.signedCookies.rmToken.token;
if (user._id ==tokenUser) {
for (var i=0;i< user.tokens.length;i++) {
if (user.tokens[i].token == token) {
async = true;
User.findByIdAndUpdate(user._id, { $pull: { tokens: { _id: user.tokens[i]._id}} }, function (err, dbUser) {
if (err) throw err;
res.send(200, { result: "logged out" });
})
break;
}
}
}
res.clearCookie("rmToken");
}
if (req.session) req.session.destroy();
if (!async) res.send(200,{result:"logged out"});
}
module.exports = function () {
var app = express();
User = mongoose.models["User"],
app.post("/login", function (req, res, next) {
if (req.body.password) {
loginByUserPass(req, res, req.body.email, req.body.password);
} else if (req.signedCookies.rmToken) {
loginByToken(req, res, req.signedCookies.rmToken.id, req.signedCookies.rmToken.token);
} else {
res.send(400, "malformed url");
}
})
app.post("/logout", module.exports.authenticate, logout);
app.post("/", register);
return app;
}
module.exports.authenticate = function (req, res, next) {
if (!req.session || !req.session.user) return res.send(403, "not logged in");
req.user = req.session.user;
next();
}
var crypto = require('crypto');
var _ = require('underscore');
module.exports = function (options) {
var defaultOptions = {
saltSize: 20,
saltLength: 64,
secretKey: "",
algorithm: "sha512"
}
options = _.extend(defaultOptions, options);
var saltpool = [];
var last_salt = Math.random().toString('36').replace('0.', '')
+ Math.random().toString('36').replace('0.', '')
+ Math.random().toString('36').replace('0.', '');
function addSaltToPool() {
if (saltpool.length <= options.saltSize) {
crypto.randomBytes(options.saltLength, function (ex, buf) {
var result = buf.toString('base64').replace(/\//g, '_').replace(/\+/g, '-');
saltpool.push(result);
last_salt = result;
addSaltToPool();
});
}
}
addSaltToPool();
var m = {};
m.hash = function hash(raw) {
'use strict';
var h = crypto.createHash(options.algorithm);
h.update(raw);
return h.digest("base64");
};
m.getRandomSalt =
function getRandomSalt() {
var result = saltpool.pop();
if (result === undefined || result === null) {
result = last_salt;
}
addSaltToPool();
return result;
}
m.hashPassword = function (password, salt) {
if (salt === undefined || salt === null) salt = m.getRandomSalt();
var hashed = m.hash(salt + password + options.secretKey);
return salt + "::" + hashed;
}
m.samePassword = function (toCompare, goodPassword) {
if (!goodPassword) throw new Error("hashedValue has bad format: " + goodPassword);
var parts = goodPassword.split("::");
if (parts === undefined || parts === null || parts.length != 2) {
throw new Error("hashedValue has bad format: " + goodPassword);
}
return goodPassword == m.hashPassword(toCompare, parts[0]);
}
return m;
};
var mongoose = require('mongoose')
var security = require('./security')();
var Schema = mongoose.Schema;
var schema = new Schema({
email: { type: String, unique: true },
password: {type: String, select: false},
firstName: String,
lastName: String,
tokens: { type: Array, cast: {
token: String,
expires: Date
}
}
});
schema.statics.buildSecure = function (raw) {
delete raw.tokens;
delete raw.accounts;
var entity = new this(raw)
entity.password = security.hashPassword(entity.password);
return entity;
};
schema.statics.findByPassword = function (email, password, callback) {
var result = this.findOne({ email: email }).select("+password").exec(function (err, result) {
if (result === undefined || result === null || result.length == 0) {
result = "User does not exists";
} else if (!security.samePassword(password, result.password)) {
result = "Bad password";
} else {
result.password = undefined;
result = result;
}
callback(result);
})
}
var User = mongoose.model('User', schema);
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment