Reputation: 13
If I manually input the Secret Key into the front end, it matches the token to the expected token. However, if I scan the QR code, it doesn't match the token.
/Controller
const setupTwoFactor = async (req, res) => {
try {
const patient = await Patient.findById(req.params.id);
if (!patient) {
return res.status(404).json({ message: 'Patient not found' });
}
let secret;
if (!patient.twoFactorSecret || req.body.regenerate) {
// Generate a new secret key if the patient does not have one or if regenerate flag is true
secret = speakeasy.generateSecret({ length: 30 });
patient.twoFactorSecret = secret.base32;
patient.twoFactorEnabled = true; // Enable 2FA for this patient
await patient.save(); // Save the changes
} else {
// Use the existing secret key
secret = { base32: patient.twoFactorSecret };
}
// Log the stored secret key for debugging
console.log('Stored Secret in DB:', patient.twoFactorSecret);
// Generate the QR code with the saved secret key using the method from the schema
const qrCode = await patient.generateQRCode();
console.log('Generated Secret:', secret.base32); // Log the secret
res.json({ qrCode, secret: secret.base32 });
} catch (error) {
console.error('Error generating 2FA secret:', error); // Log the error
res.status(500).json({ message: 'Error generating 2FA secret', error });
}
};
// Verify Two-Factor Function
const verifyTwoFactor = async (req, res) => {
const { userId, token } = req.body;
try {
const patient = await Patient.findById(userId);
if (!patient) {
return res.status(404).json({ message: 'Patient not found' });
}
if (!patient.twoFactorEnabled) {
return res.status(400).json({ message: '2FA not enabled for this patient' });
}
if (!token) {
return res.status(400).json({ message: '2FA token is required' });
}
console.log(`Verifying token: ${token} for user ${userId}`);
console.log(`Secret key: ${patient.twoFactorSecret}`);
const verified = speakeasy.totp.verify({
secret: patient.twoFactorSecret,
encoding: 'base32',
token,
window: 2 // Increase the window size if necessary
});
console.log(`Verified: ${verified}`);
if (verified) {
res.json({ verified: true });
} else {
res.status(400).json({ verified: false, message: 'Invalid 2FA token' });
}
} catch (error) {
console.error('Error verifying 2FA token:', error);
res.status(500).json({ message: 'Error verifying 2FA token', error });
}
};
//Model
const mongoose = require('mongoose');
const bcrypt = require('bcrypt');
const { Schema, model } = mongoose;
const PatientSchema = new Schema({
// Personal info
patient_ID: {
type: String,
unique: true
},
patient_firstName: {
type: String,
minlength: 3,
maxlength: 20
},
patient_middleInitial: {
type: String,
maxlength: 1
},
patient_lastName: {
type: String,
minlength: 2,
maxlength: 20
},
patient_email: {
type: String,
unique: true,
},
patient_password: {
type: String,
minlength: 6,
},
patient_dob: {
type: Date,
},
patient_age:{
type:String
},
patient_contactNumber: {
type: String,
unique: true,
validate: {
validator: function (v) {
return v.length === 11;
},
message: props => `${props.value} has to be 11 characters long.`
}
},
patient_gender: {
type: String,
enum: ['Male', 'Female', 'Other']
},
patient_appointments: [{
type: Schema.Types.ObjectId,
ref: 'Appointment'
}],
medicalHistory: {
type: Schema.Types.ObjectId,
ref: 'MedicalHistory'
},
prescriptions: [{
type: Schema.Types.ObjectId,
ref: 'Prescription'
}],
notifications: [{
type: Schema.Types.ObjectId,
ref: 'Notification'
}],
twoFactorSecret: { type: String },
twoFactorEnabled: { type: Boolean, default: false }
}, { timestamps: true });
// Pre-save hook for generating the patient ID
PatientSchema.pre('save', async function (next) {
if (!this.isNew) {
return next();
}
const currentYear = new Date().getFullYear();
try {
const highestPatient = await this.constructor.findOne({ patient_ID: new RegExp(`^P${currentYear}`, 'i') })
.sort({ patient_ID: -1 })
.limit(1);
let nextNumber = 1;
if (highestPatient) {
const lastNumber = parseInt(highestPatient.patient_ID.split('-')[1]);
nextNumber = lastNumber + 1;
}
const paddedNumber = nextNumber.toString().padStart(6, '0');
this.patient_ID = `P${currentYear}-${paddedNumber}`;
next();
} catch (error) {
next(error);
}
});
const QRCode = require('qrcode');
const speakeasy = require('speakeasy');
PatientSchema.methods.generateQRCode = async function() {
const otpAuthUrl = speakeasy.otpauthURL({
secret: this.twoFactorSecret,
label: `Landagan Kids Clinic:${this.patient_email}`,
issuer: 'Landagan Kids Clinic',
algorithm: 'SHA1' // Ensure the algorithm is specified
});
return await QRCode.toDataURL(otpAuthUrl);
};
const Patient = model('Patient', PatientSchema);
module.exports = Patient;
I am expecting that the generated key, has the same value of key also in the qr code, but it turns out different.
//Output Verifying token: 893236 for user 66533caad18ff8bf1f76fd11 Secret key: KB3HAOLLJVCDQ7JSO5JWE33JIFEW26TEO5JTCZZ2FY6DYVKB Verified: false
Upvotes: 0
Views: 212