Finished up with remember me middleware.
This commit is contained in:
parent
e00e5a608b
commit
61ec3ffc52
|
|
@ -62,10 +62,13 @@ module.exports.post = async function(req, res){
|
||||||
//Check config for protocol
|
//Check config for protocol
|
||||||
const secure = config.protocol.toLowerCase() == "https";
|
const secure = config.protocol.toLowerCase() == "https";
|
||||||
|
|
||||||
|
//Create expiration date for cookies (180 days)
|
||||||
|
const expires = new Date(Date.now() + (1000 * 60 * 60 * 24 * 180))
|
||||||
|
|
||||||
//Set remember me ID and token as browser-side cookies for safe-keeping
|
//Set remember me ID and token as browser-side cookies for safe-keeping
|
||||||
res.cookie("rememberme.id", authToken.id, {sameSite: 'strict', httpOnly: true, secure});
|
res.cookie("rememberme.id", authToken.id, {sameSite: 'strict', httpOnly: true, secure, expires});
|
||||||
//This should be the servers last interaction with the plaintext token before saving the hashed copy, and dropping it out of RAM
|
//This should be the servers last interaction with the plaintext token before saving the hashed copy, and dropping it out of RAM
|
||||||
res.cookie("rememberme.token", authToken.token, {sameSite: 'strict', httpOnly: true, secure});
|
res.cookie("rememberme.token", authToken.token, {sameSite: 'strict', httpOnly: true, secure, expires});
|
||||||
}
|
}
|
||||||
|
|
||||||
//Tell the browser everything is dandy
|
//Tell the browser everything is dandy
|
||||||
|
|
|
||||||
|
|
@ -81,6 +81,12 @@ rememberMeToken.pre('save', async function (next){
|
||||||
next();
|
next();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
//Methods
|
||||||
|
rememberMeToken.methods.checkToken = async function(token){
|
||||||
|
//Compare ingested token to saved hash
|
||||||
|
return await hashUtil.compareRememberMeToken(token, this.token);
|
||||||
|
}
|
||||||
|
|
||||||
//statics
|
//statics
|
||||||
rememberMeToken.statics.genToken = async function(user, pass){
|
rememberMeToken.statics.genToken = async function(user, pass){
|
||||||
//Authenticate user and pull document
|
//Authenticate user and pull document
|
||||||
|
|
@ -104,4 +110,45 @@ rememberMeToken.statics.genToken = async function(user, pass){
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Authenticates an id and token pair
|
||||||
|
* @param {String} id - id of token auth against
|
||||||
|
* @param {String} token - token string to auth against
|
||||||
|
* @param {String} failLine - Line to paste into custom error upon login failure
|
||||||
|
* @returns {Mongoose.Document} - User DB Document upon success
|
||||||
|
*/
|
||||||
|
rememberMeToken.statics.authenticate = async function(id, token, failLine = "Bad Username or Password."){
|
||||||
|
//check for missing pass
|
||||||
|
if(!id || !token){
|
||||||
|
throw loggerUtils.exceptionSmith("Missing id/token.", "validation");
|
||||||
|
}
|
||||||
|
|
||||||
|
//get the token if it exists
|
||||||
|
const tokenDB = await this.findOne({id});
|
||||||
|
|
||||||
|
//if not scream and shout
|
||||||
|
if(!tokenDB){
|
||||||
|
badLogin();
|
||||||
|
}
|
||||||
|
|
||||||
|
//Check our password is correct
|
||||||
|
if(await tokenDB.checkToken(token)){
|
||||||
|
//Populate the user field
|
||||||
|
await tokenDB.populate('user');
|
||||||
|
|
||||||
|
//Return the user doc
|
||||||
|
return tokenDB.user;
|
||||||
|
}else{
|
||||||
|
//Nuke the token for security
|
||||||
|
await tokenDB.deleteOne();
|
||||||
|
//if not scream and shout
|
||||||
|
badLogin();
|
||||||
|
}
|
||||||
|
|
||||||
|
//standardize bad login response so it's unknown which is bad for security reasons.
|
||||||
|
function badLogin(){
|
||||||
|
throw loggerUtils.exceptionSmith(failLine, "unauthorized");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
module.exports = mongoose.model("rememberMe", rememberMeToken);
|
module.exports = mongoose.model("rememberMe", rememberMeToken);
|
||||||
|
|
@ -39,6 +39,7 @@ const pmHandler = require('./app/pm/pmHandler');
|
||||||
const configCheck = require('./utils/configCheck');
|
const configCheck = require('./utils/configCheck');
|
||||||
const scheduler = require('./utils/scheduler');
|
const scheduler = require('./utils/scheduler');
|
||||||
const {errorMiddleware} = require('./utils/loggerUtils');
|
const {errorMiddleware} = require('./utils/loggerUtils');
|
||||||
|
const sessionUtils = require('./utils/sessionUtils');
|
||||||
//Validator
|
//Validator
|
||||||
const accountValidator = require('./validators/accountValidator');
|
const accountValidator = require('./validators/accountValidator');
|
||||||
//DB Model
|
//DB Model
|
||||||
|
|
@ -143,6 +144,14 @@ mongoose.set("sanitizeFilter", true).connect(dbUrl).then(() => {
|
||||||
process.exit();
|
process.exit();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
//Static File Server, set this up first to avoid middleware running on top of it
|
||||||
|
//Serve client-side libraries
|
||||||
|
app.use('/lib/bootstrap-icons',express.static(path.join(__dirname, '../node_modules/bootstrap-icons'))); //Icon set
|
||||||
|
app.use('/lib/altcha',express.static(path.join(__dirname, '../node_modules/altcha/dist_external'))); //Self-Hosted PoW-based Captcha
|
||||||
|
app.use('/lib/hls.js',express.static(path.join(__dirname, '../node_modules/hls.js/dist'))); //HLS Media Handler
|
||||||
|
//Server public 'www' folder
|
||||||
|
app.use(express.static(path.join(__dirname, '../www')));
|
||||||
|
|
||||||
//Set View Engine
|
//Set View Engine
|
||||||
app.set('view engine', 'ejs');
|
app.set('view engine', 'ejs');
|
||||||
app.set('views', __dirname + '/views');
|
app.set('views', __dirname + '/views');
|
||||||
|
|
@ -164,6 +173,9 @@ io.engine.use(sessionMiddleware);
|
||||||
app.use(accountValidator.rememberMeID());
|
app.use(accountValidator.rememberMeID());
|
||||||
app.use(accountValidator.rememberMeToken());
|
app.use(accountValidator.rememberMeToken());
|
||||||
|
|
||||||
|
//Use remember me middleware
|
||||||
|
app.use(sessionUtils.rememberMeMiddleware);
|
||||||
|
|
||||||
//Routes
|
//Routes
|
||||||
//Humie-Friendly
|
//Humie-Friendly
|
||||||
app.use('/', indexRouter);
|
app.use('/', indexRouter);
|
||||||
|
|
@ -183,14 +195,6 @@ app.use('/tooltip', tooltipRouter);
|
||||||
//Bot-Ready
|
//Bot-Ready
|
||||||
app.use('/api', apiRouter);
|
app.use('/api', apiRouter);
|
||||||
|
|
||||||
//Static File Server
|
|
||||||
//Serve client-side libraries
|
|
||||||
app.use('/lib/bootstrap-icons',express.static(path.join(__dirname, '../node_modules/bootstrap-icons'))); //Icon set
|
|
||||||
app.use('/lib/altcha',express.static(path.join(__dirname, '../node_modules/altcha/dist_external'))); //Self-Hosted PoW-based Captcha
|
|
||||||
app.use('/lib/hls.js',express.static(path.join(__dirname, '../node_modules/hls.js/dist'))); //HLS Media Handler
|
|
||||||
//Server public 'www' folder
|
|
||||||
app.use(express.static(path.join(__dirname, '../www')));
|
|
||||||
|
|
||||||
//Handle error checking
|
//Handle error checking
|
||||||
app.use(errorMiddleware);
|
app.use(errorMiddleware);
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -14,6 +14,9 @@ GNU Affero General Public License for more details.
|
||||||
You should have received a copy of the GNU Affero General Public License
|
You should have received a copy of the GNU Affero General Public License
|
||||||
along with this program. If not, see <https://www.gnu.org/licenses/>.*/
|
along with this program. If not, see <https://www.gnu.org/licenses/>.*/
|
||||||
|
|
||||||
|
//npm imports
|
||||||
|
const {validationResult, matchedData} = require('express-validator');
|
||||||
|
|
||||||
//Local Imports
|
//Local Imports
|
||||||
const config = require('../../config.json');
|
const config = require('../../config.json');
|
||||||
const {userModel} = require('../schemas/user/userSchema.js');
|
const {userModel} = require('../schemas/user/userSchema.js');
|
||||||
|
|
@ -101,7 +104,7 @@ module.exports.authenticateSession = async function(identifier, secret, req, use
|
||||||
|
|
||||||
//If we're using remember me tokens
|
//If we're using remember me tokens
|
||||||
if(useRememberMeToken){
|
if(useRememberMeToken){
|
||||||
|
userDB = await rememberMeModel.authenticate(identifier, secret);
|
||||||
//Otherwise
|
//Otherwise
|
||||||
}else{
|
}else{
|
||||||
//Fallback on to username/password authentication
|
//Fallback on to username/password authentication
|
||||||
|
|
@ -211,5 +214,44 @@ module.exports.processExpiredAttempts = function(){
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
module.exports.rememberMeMiddleware = function(req, res, next){
|
||||||
|
//if we have an un-authenticated user
|
||||||
|
if(req.session.user == null || req.session.user == ""){
|
||||||
|
//Check validation result
|
||||||
|
const validResult = validationResult(req);
|
||||||
|
|
||||||
|
//if we don't have errors
|
||||||
|
if(validResult.isEmpty()){
|
||||||
|
//Pull verified data from request
|
||||||
|
const data = matchedData(req);
|
||||||
|
|
||||||
|
//If we have a valid remember me id and token
|
||||||
|
if(data.rememberme != null && data.rememberme.id != null && data.rememberme.token != null){
|
||||||
|
//Authenticate against standard auth function in remember me mode
|
||||||
|
module.exports.authenticateSession(data.rememberme.id, data.rememberme.token, req, true).then((userDB)=>{
|
||||||
|
//Jump to next middleware
|
||||||
|
next();
|
||||||
|
}).catch((err)=>{
|
||||||
|
//Clear out remember me fields
|
||||||
|
res.clearCookie('rememberme.id');
|
||||||
|
res.clearCookie('rememberme.token');
|
||||||
|
|
||||||
|
//Bitch, Moan, and guess what? That's fuckin' right! COMPLAIN!!!!
|
||||||
|
return loggerUtils.exceptionHandler(res, err);
|
||||||
|
});
|
||||||
|
}else{
|
||||||
|
//Jump to next middleware, this looks gross but it's only because they made me use .then like a bunch of fucking dicks
|
||||||
|
next();
|
||||||
|
}
|
||||||
|
}else{
|
||||||
|
//Jump to next middleware
|
||||||
|
next();
|
||||||
|
}
|
||||||
|
}else{
|
||||||
|
//Jump to next middleware
|
||||||
|
next();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
module.exports.throttleAttempts = throttleAttempts;
|
module.exports.throttleAttempts = throttleAttempts;
|
||||||
module.exports.maxAttempts = maxAttempts;
|
module.exports.maxAttempts = maxAttempts;
|
||||||
Loading…
Reference in a new issue