I had this Model user.js
import mongoose from "mongoose";
import crypto from "crypto";
const { ObjectId } = mongoose.Schema;
const userSchema = new mongoose.Schema(
{
//Basic Data
email: {
type: String,
trim: true,
required: true,
unique: true,
},
hashed_password: {
type: String,
required: true,
},
salt: {
type: String,
},
);
// virtual field
userSchema
.virtual("password")
.set(function (password) {
// create a temporarity variable called _password
this._password = password;
// generate salt
this.salt = this.makeSalt();
// encryptPassword
this.hashed_password = this.encryptPassword(password);
})
.get(function () {
return this._password;
});
// Methods
userSchema.methods = {
// Method Authenticate User
authenticate: function (plainText) {
const encryptP = this.encryptPassword(plainText);
console.log("Entro a Autenticate...");
console.log("Authenticate PlainText:", plainText);
console.log("Authenticate Hashed:", this.hashed_password);
console.log("Encript Pass:", encryptP);
return this.encryptPassword(plainText) === this.hashed_password; // Ture or False
},
// Method Encrypt Pass
encryptPassword: function (password) {
if (!password) return "";
try {
return crypto.createHmac("sha1", this.salt).update(password).digest("hex");
} catch (err) {
return "";
}
},
// Method Make Salt
makeSalt: function () {
return Math.round(new Date().valueOf() * Math.random()) + "";
},
};
// export default mongoose.model("User", userSchema);
module.exports = mongoose.models.User || mongoose.model("User", userSchema);
The result of all console.log is:
Entro a Autenticate...
Authenticate PlainText: 1234567890
Authenticate Hashed: b4358d67e879145916c10ba00d66d78a946f4b53
Encript Pass: b4358d67e879145916c10ba00d66d78a946f4b53
The .js file works fine...
I start a new project using Typescript using the same logic and code but with typescript.
This is my model user.ts:
import { Model, model, Schema, models } from "mongoose";
import crypto from "crypto";
// 1. Create an interface representing a document in MongoDB.
interface IUser {
email: string;
hashed_password?: string;
salt?: string;
//Virtual Field
_password?: string;
}
// Put all user instance methods in this interface:
interface IUserMethods {
authenticate(plainText: string): boolean;
encryptPassword(password: string): string;
makeSalt(): string;
}
// Create a new Model type that knows about IUserMethods...
type UserModel = Model<IUser, {}, IUserMethods>;
// 2. Create a Schema corresponding to the document interface.
const userSchema = new Schema<IUser, UserModel, IUserMethods>(
{
//Basic Data
email: {
type: String,
trim: true,
required: true,
unique: true,
},
hashed_password: {
type: String,
required: true,
},
salt: {
type: String,
},
);
//Virtuals
userSchema
.virtual("password")
.set(function (password:string) {
// create a temporarity variable called _password
this._password = password;
// generate salt
this.salt = this.makeSalt();
// encryptPassword
this.hashed_password = this.encryptPassword(password);
})
.get(function () {
return this._password;
});
// Methods
userSchema.methods = {
// Method Authenticate User
authenticate: function (plainText:string) {
const encryptP: any = this.encryptPassword(plainText);
console.log("Entro a Autenticate...");
console.log("Authenticate PlainText:", plainText);
console.log("Authenticate Hashed:", this.hashed_password);
console.log("Encript Pass:", encryptP);
return this.encryptPassword(plainText) === this.hashed_password; // Ture or False
},
// Method Encrypt Pass
encryptPassword: function (password:string) {
if (!password) return "";
try {
return crypto.createHmac("sha1", this.salt).update(password).digest("hex");
} catch (err) {
return "";
}
},
// Method Make Salt
makeSalt: function () {
return Math.round(new Date().valueOf() * Math.random()) + "";
},
};
export default models.User || model<IUser, UserModel>("User", userSchema);
the result of all the console logs whit this Typescript file is:
Entro a Autenticate...
Authenticate PlainText: 1234567890
Authenticate Hashed: undefined
Encript Pass:
The problem appears to be in the way it is handle the types...
But not a clue...
EDITED:
Based on the comments, I found that the issue is whit:
this.salt
this.hashed_password
in TS is shown as "undefined" in JS as a string (as should be)
SOLVED:
The problem was not Typescript, was the OBJECT "this" , this object is created when the method is called.
So the method is call like this
userDB.authenticate(passIn);
The userDB object on mongoose is the "this" object mounted. So, the this.salt and this.hashed_password must be contained on the userDB object to "Exists" on the Method called.
The userDB object mounted on "this" did not contains salt and hashed_pass so I add them and the code WORKS just fine!