Twofactor authentication and strong password

This commit is contained in:
2025-10-17 14:52:45 +02:00
parent 71a688f97f
commit dbd8c8aed2
17 changed files with 3055 additions and 14 deletions
+211
View File
@@ -15,6 +15,20 @@ $( function () {
email : ko.observable(),
showPasswordRequest: ko.observable( false ),
// 2FA properties
twoFactorMethod : ko.observable( '' ),
twoFactorCode : ko.observable( '' ),
twoFactorError : ko.observable( '' ),
isVerifying : ko.observable( false ),
isResending : ko.observable( false ),
// Password change properties
newPassword : ko.observable( '' ),
confirmPassword : ko.observable( '' ),
passwordRequirements : ko.observableArray( [] ),
passwordChangeError : ko.observable( '' ),
isChangingPassword : ko.observable( false ),
login: function () {
dbo.loginService.login();
},
@@ -26,10 +40,64 @@ $( function () {
togglePasswordrequest: function () {
var vm = dbo.loginModel
vm.showPasswordRequest( !vm.showPasswordRequest() );
},
verifyTwoFactor: function () {
dbo.loginService.verifyTwoFactor();
},
resendOtp: function () {
dbo.loginService.resendOtp();
return false;
},
cancelTwoFactor: function () {
$('#twofactor-modal').modal('hide');
var vm = dbo.loginModel;
vm.twoFactorCode( '' );
vm.twoFactorError( '' );
vm.twoFactorMethod( '' );
},
handleTwoFactorKeyup: function (data, event) {
var vm = dbo.loginModel;
// Clear error on input
vm.twoFactorError( '' );
// Only allow digits
var sanitized = vm.twoFactorCode().replace(/[^0-9]/g, '');
if (sanitized !== vm.twoFactorCode()) {
vm.twoFactorCode(sanitized);
}
// Auto-submit on Enter key when code is 6 digits
if (vm.twoFactorCode().length === 6 && event.keyCode === 13) {
vm.verifyTwoFactor();
}
return true;
},
changePassword: function () {
dbo.loginService.changePassword();
},
handlePasswordChangeKeyup: function (data, event) {
var vm = dbo.loginModel;
// Clear error on input
vm.passwordChangeError( '' );
// Auto-submit on Enter key
if (event.keyCode === 13 && vm.newPassword().length >= 8 && vm.confirmPassword().length >= 8) {
vm.changePassword();
}
return true;
}
}
ko.applyBindings( dbo.loginModel, document.getElementById( "login-form" ) );
ko.applyBindings( dbo.loginModel, document.getElementById( "twofactor-modal" ) );
} );
dbo.loginService = {
@@ -43,6 +111,35 @@ dbo.loginService = {
if ( ! submitDATA ) return self.notifyWrongPassword();
ds.login( submitDATA, function ( data ) {
// Check if password change is required
if ( data && typeof data === 'object' && data.result === 'PASSWORD-CHANGE-REQUIRED' ) {
// Show password change modal
vm.passwordRequirements( data.requirements || [] );
vm.newPassword( '' );
vm.confirmPassword( '' );
vm.passwordChangeError( '' );
$('#password-change-modal').modal('show');
// Focus on new password input after modal is shown
setTimeout(function() {
$('#newPassword').focus();
}, 500);
return;
}
// Check if 2FA is required
if ( data && typeof data === 'object' && data.result === '2FA-REQUIRED' ) {
// Show 2FA modal
vm.twoFactorMethod( data.method );
vm.twoFactorCode( '' );
vm.twoFactorError( '' );
$('#twofactor-modal').modal('show');
// Focus on code input after modal is shown
setTimeout(function() {
$('#twoFactorCode').focus();
}, 500);
return;
}
if ( data ) return location.reload();
self.notifyWrongPassword();
@@ -50,6 +147,102 @@ dbo.loginService = {
} );
},
verifyTwoFactor: function () {
var ds = dbo.loginDataservice;
var vm = dbo.loginModel;
if ( vm.twoFactorCode().length !== 6 ) {
return;
}
vm.isVerifying( true );
vm.twoFactorError( '' );
ds.verifyTwoFactor( vm.twoFactorCode(), function ( response ) {
vm.isVerifying( false );
if ( response.success ) {
// Close modal and reload page
$('#twofactor-modal').modal('hide');
location.reload();
} else {
vm.twoFactorError( response.message || 'Ongeldige verificatiecode' );
vm.twoFactorCode( '' );
}
} );
},
resendOtp: function () {
var ds = dbo.loginDataservice;
var vm = dbo.loginModel;
vm.isResending( true );
vm.twoFactorError( '' );
ds.resendOtp( function ( response ) {
vm.isResending( false );
if ( response.success ) {
tellMe( {
title : 'Code verzonden',
message : response.message || 'Nieuwe code verzonden',
modalClass: 'modal-success'
} );
} else {
vm.twoFactorError( response.message || 'Kan code niet opnieuw verzenden' );
}
} );
},
changePassword: function () {
var ds = dbo.loginDataservice;
var vm = dbo.loginModel;
// Validate passwords match
if ( vm.newPassword() !== vm.confirmPassword() ) {
vm.passwordChangeError( 'Wachtwoorden komen niet overeen' );
return;
}
// Validate password length
if ( vm.newPassword().length < 8 ) {
vm.passwordChangeError( 'Wachtwoord moet minimaal 8 karakters bevatten' );
return;
}
vm.isChangingPassword( true );
vm.passwordChangeError( '' );
var submitDATA = {
new_password: vm.newPassword(),
confirm_password: vm.confirmPassword()
};
ds.changePassword( submitDATA, function ( response ) {
vm.isChangingPassword( false );
if ( response.success ) {
// Close password change modal
$('#password-change-modal').modal('hide');
// Check if 2FA is required after password change
if ( response.result === '2FA-REQUIRED' ) {
vm.twoFactorMethod( response.method );
vm.twoFactorCode( '' );
vm.twoFactorError( '' );
$('#twofactor-modal').modal('show');
setTimeout(function() {
$('#twoFactorCode').focus();
}, 500);
} else {
location.reload();
}
} else {
vm.passwordChangeError( response.message || 'Wachtwoord wijzigen mislukt' );
}
} );
},
requestPassword: function () {
var ds = dbo.loginDataservice;
var self = dbo.loginService;
@@ -147,6 +340,24 @@ dbo.loginDataservice = {
$.post( 'index.php/login/requestPassword', submitDATA, function ( data ) {
callback( data );
} );
},
verifyTwoFactor: function ( code, callback ) {
$.post( 'index.php/login/verify2fa', { code: code }, function ( data ) {
callback( data );
} );
},
resendOtp: function ( callback ) {
$.post( 'index.php/login/resendOtp', {}, function ( data ) {
callback( data );
} );
},
changePassword: function ( submitDATA, callback ) {
$.post( 'index.php/login/changeWeakPassword', submitDATA, function ( data ) {
callback( data );
} );
}
};