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');
|
||||
|
||||
//NPM Imports
|
||||
const argon2 = require('argon2');
|
||||
const bcrypt = require('bcrypt');
|
||||
|
||||
/**
|
||||
|
|
@ -70,3 +71,23 @@ module.exports.hashIP = function(ip){
|
|||
//return the IP hash as a string
|
||||
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