Started work on Remember Me Tokens.
This commit is contained in:
parent
7f6abdf8e2
commit
895a8201a5
103
src/schemas/user/rememberMeSchema.js
Normal file
103
src/schemas/user/rememberMeSchema.js
Normal file
|
|
@ -0,0 +1,103 @@
|
||||||
|
/*Canopy - The next generation of stoner streaming software
|
||||||
|
Copyright (C) 2024-2025 Rainbownapkin and the TTN Community
|
||||||
|
|
||||||
|
This program is free software: you can redistribute it and/or modify
|
||||||
|
it under the terms of the GNU Affero General Public License as
|
||||||
|
published by the Free Software Foundation, either version 3 of the
|
||||||
|
License, or (at your option) any later version.
|
||||||
|
|
||||||
|
This program is distributed in the hope that it will be useful,
|
||||||
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
GNU Affero General Public License for more details.
|
||||||
|
|
||||||
|
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/>.*/
|
||||||
|
|
||||||
|
//You could make an argument for making this part of the userModel
|
||||||
|
//However, this is so rarely used the preformance benefits aren't worth the extra clutter
|
||||||
|
|
||||||
|
//Config
|
||||||
|
const config = require('../../../config.json');
|
||||||
|
|
||||||
|
//Node Imports
|
||||||
|
const crypto = require("node:crypto");
|
||||||
|
|
||||||
|
//NPM Imports
|
||||||
|
const {mongoose} = require('mongoose');
|
||||||
|
|
||||||
|
//Local Imports
|
||||||
|
const userSchema = require('./userSchema');
|
||||||
|
const hashUtil = require('../../utils/hashUtils');
|
||||||
|
const loggerUtils = require('../../utils/loggerUtils');
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Password reset token retention time
|
||||||
|
*
|
||||||
|
* Lasts about half a year
|
||||||
|
*/
|
||||||
|
const daysToExpire = 182;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* DB Schema for documents containing a single expiring password reset token
|
||||||
|
*/
|
||||||
|
const rememberMeToken = new mongoose.Schema({
|
||||||
|
id: {
|
||||||
|
type: mongoose.SchemaTypes.UUID,
|
||||||
|
required: true,
|
||||||
|
default: crypto.randomUUID()
|
||||||
|
},
|
||||||
|
user: {
|
||||||
|
type: mongoose.SchemaTypes.ObjectID,
|
||||||
|
ref: "user",
|
||||||
|
required: true
|
||||||
|
},
|
||||||
|
token: {
|
||||||
|
type: mongoose.SchemaTypes.String,
|
||||||
|
required: true
|
||||||
|
},
|
||||||
|
date: {
|
||||||
|
type: mongoose.SchemaTypes.Date,
|
||||||
|
required: true,
|
||||||
|
default: new Date()
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Pre-Save function for rememberMeSchema
|
||||||
|
*/
|
||||||
|
rememberMeToken.pre('save', async function (next){
|
||||||
|
//If the token was changed
|
||||||
|
if(this.isModified("token")){
|
||||||
|
//Hash that sunnovabitch, no questions asked.
|
||||||
|
this.token = hashUtil.hashRememberMeToken(this.token);
|
||||||
|
}
|
||||||
|
|
||||||
|
//All is good, continue on saving.
|
||||||
|
next();
|
||||||
|
});
|
||||||
|
|
||||||
|
//statics
|
||||||
|
rememberMeToken.statics.genToken = async function(user, pass){
|
||||||
|
try{
|
||||||
|
//Authenticate user and pull document
|
||||||
|
const userDB = await userSchema.authenticate(user, pass);
|
||||||
|
|
||||||
|
//Generate a cryptographically secure string of 32 bytes in hexidecimal
|
||||||
|
const token = crypto.randomBytes(32).toString('hex');
|
||||||
|
|
||||||
|
//Create token document off of user and token string
|
||||||
|
const tokenDB = await this.create({user: userDB._id, token});
|
||||||
|
|
||||||
|
//Return token document UUID w/ plaintext token for browser consumption
|
||||||
|
return {
|
||||||
|
id: tokenDB.id,
|
||||||
|
token
|
||||||
|
};
|
||||||
|
//If we failed (most likely for bad login)
|
||||||
|
}catch(err){
|
||||||
|
return loggerUtils.localExceptionHandler(err);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
module.exports = mongoose.model("rememberMe", rememberMeToken);
|
||||||
|
|
@ -21,6 +21,7 @@ const config = require('../../config.json');
|
||||||
const crypto = require('node:crypto');
|
const crypto = require('node:crypto');
|
||||||
|
|
||||||
//NPM Imports
|
//NPM Imports
|
||||||
|
const argon2 = require('argon2');
|
||||||
const bcrypt = require('bcrypt');
|
const bcrypt = require('bcrypt');
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
@ -69,4 +70,24 @@ module.exports.hashIP = function(ip){
|
||||||
|
|
||||||
//return the IP hash as a string
|
//return the IP hash as a string
|
||||||
return hashObj.digest('hex');
|
return hashObj.digest('hex');
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Site-wide remember-me token hashing function
|
||||||
|
* @param {String} token - Token to hash
|
||||||
|
* @returns {String} - Hashed token
|
||||||
|
*/
|
||||||
|
module.exports.hashRememberMeToken = async function(token){
|
||||||
|
return await argon2.hash(token);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Site-wide remember-me token hash comparison function
|
||||||
|
* @param {String} token - Token to compare
|
||||||
|
* @param {String} hash - Hash to compare
|
||||||
|
* @returns {String} - Comparison results
|
||||||
|
*/
|
||||||
|
module.exports.compareRememberMeToken = async function(token, hash){
|
||||||
|
//Compare hash and return result
|
||||||
|
return await argon2.verify(hash, token);
|
||||||
}
|
}
|
||||||
Loading…
Reference in a new issue