Added 2FA timeout on login screen, default is 5 minutes.

This commit is contained in:
Ylian Saint-Hilaire 2021-07-24 15:14:21 -07:00
parent 552520cdc0
commit 21aabc676d
6 changed files with 27 additions and 3 deletions

View File

@ -384,7 +384,8 @@
"skip2factor": { "type": "string", "description": "IP addresses where 2FA login is skipped, for example: 127.0.0.1,192.168.2.0/24" },
"oldPasswordBan": { "type": "integer", "description": "Number of old passwords the server should remember and not allow the user to switch back to." },
"banCommonPasswords": { "type": "boolean", "default": false, "description": "Uses WildLeek to block use of the 10000 most commonly used passwords." },
"loginTokens": { "type": "boolean", "default": true, "description": "Allows users to create alternative username/passwords for their account." }
"loginTokens": { "type": "boolean", "default": true, "description": "Allows users to create alternative username/passwords for their account." },
"twoFactorTimeout": { "type": "integer", "default": 300, "description": "Maximum about of time the to wait for a 2FA token on the login page in seconds." }
}
},
"twoFactorCookieDurationDays": { "type": "integer", "default": 30, "description": "Number of days that a user is allowed to remember this device for when completing 2FA. Set this to 0 to remove this option." },

View File

@ -196,7 +196,8 @@
"force2factor": true,
"skip2factor": "127.0.0.1,192.168.2.0/24",
"oldPasswordBan": 5,
"banCommonPasswords": false
"banCommonPasswords": false,
"twoFactorTimeout": 300
},
"_twoFactorCookieDurationDays": 30,
"_agentInviteCodes": true,

View File

@ -326,6 +326,7 @@
var otpsms = ('{{{otpsms}}}' === 'true');
var twoFactorCookieDays = parseInt('{{{twoFactorCookieDays}}}');
var authStrategies = '{{{authStrategies}}}'.split(',');
var tokenTimeout = parseInt('{{{tokenTimeout}}}');
// Display the right server message
var messageid = parseInt('{{{messageid}}}');
@ -402,6 +403,7 @@
QV('hrAccountDiv', (emailCheck == 'true') || (newAccountPass == 1));
if (loginMode == '4') {
if (tokenTimeout > 0) { setTimeout(function () { Q('hwtokenInput').value = '**timeout**'; QE('tokenOkButton', true); Q('tokenOkButton').click(); }, tokenTimeout); }
try { if (hardwareKeyChallenge.length > 0) { hardwareKeyChallenge = JSON.parse(hardwareKeyChallenge); } else { hardwareKeyChallenge = null; } } catch (ex) { hardwareKeyChallenge = null }
QV('securityKeyButton', (hardwareKeyChallenge != null) && (hardwareKeyChallenge.type == 'webAuthn'));
QV('emailKeyButton', otpemail && (messageid != 2) && (messageid != 4));
@ -409,6 +411,7 @@
}
if (loginMode == '5') {
if (tokenTimeout > 0) { setTimeout(function () { Q('hwtokenInput').value = '**timeout**'; QE('tokenOkButton', true); Q('tokenOkButton').click(); }, tokenTimeout); }
try { if (hardwareKeyChallenge.length > 0) { hardwareKeyChallenge = JSON.parse(hardwareKeyChallenge); } else { hardwareKeyChallenge = null; } } catch (ex) { hardwareKeyChallenge = null }
if ((hardwareKeyChallenge != null) && (hardwareKeyChallenge.type == 'webAuthn')) {
if (typeof hardwareKeyChallenge.challenge == 'string') { hardwareKeyChallenge.challenge = Uint8Array.from(atob(hardwareKeyChallenge.challenge), function (c) { return c.charCodeAt(0) }).buffer; }

View File

@ -325,6 +325,7 @@
var otpsms = (decodeURIComponent('{{{otpsms}}}') === 'true');
var twoFactorCookieDays = parseInt('{{{twoFactorCookieDays}}}');
var authStrategies = '{{{authStrategies}}}'.split(',');
var tokenTimeout = parseInt('{{{tokenTimeout}}}');
function startup() {
// Display the right server message
@ -432,6 +433,7 @@
QV('hrAccountDiv', (emailCheck == 'true') || (newAccountPass == 1));
if (loginMode == '4') {
if (tokenTimeout > 0) { setTimeout(function () { Q('hwtokenInput').value = '**timeout**'; QE('tokenOkButton', true); Q('tokenOkButton').click(); }, tokenTimeout); }
try { if (hardwareKeyChallenge.length > 0) { hardwareKeyChallenge = JSON.parse(hardwareKeyChallenge); } else { hardwareKeyChallenge = null; } } catch (ex) { hardwareKeyChallenge = null }
QV('securityKeyButton', (hardwareKeyChallenge != null) && (hardwareKeyChallenge.type == 'webAuthn'));
QV('emailKeyButton', otpemail && (messageid != 2) && (messageid != 4));
@ -439,6 +441,7 @@
}
if (loginMode == '5') {
if (tokenTimeout > 0) { setTimeout(function () { Q('hwtokenInput').value = '**timeout**'; QE('tokenOkButton', true); Q('tokenOkButton').click(); }, tokenTimeout); }
try { if (hardwareKeyChallenge.length > 0) { hardwareKeyChallenge = JSON.parse(hardwareKeyChallenge); } else { hardwareKeyChallenge = null; } } catch (ex) { hardwareKeyChallenge = null }
QV('securityKeyButton2', (hardwareKeyChallenge != null) && (hardwareKeyChallenge.type == 'webAuthn'));
QV('emailKeyButton2', otpemail && (messageid != 2) && (messageid != 4));

View File

@ -358,6 +358,7 @@
var otppush = (decodeURIComponent('{{{otppush}}}') === 'true');
var twoFactorCookieDays = parseInt('{{{twoFactorCookieDays}}}');
var authStrategies = '{{{authStrategies}}}'.split(',');
var tokenTimeout = parseInt('{{{tokenTimeout}}}');
var websocket = null;
function startup() {
@ -463,6 +464,7 @@
QV('hrAccountDiv', (emailCheck == 'true') || (newAccountPass == 1));
if (loginMode == '4') {
if (tokenTimeout > 0) { setTimeout(function () { Q('hwtokenInput').value = '**timeout**'; QE('tokenOkButton', true); Q('tokenOkButton').click(); }, tokenTimeout); }
try { if (hardwareKeyChallenge.length > 0) { hardwareKeyChallenge = JSON.parse(hardwareKeyChallenge); } else { hardwareKeyChallenge = null; } } catch (ex) { hardwareKeyChallenge = null }
var twofakey = (hardwareKeyChallenge != null) && (hardwareKeyChallenge.type == 'webAuthn');
var emailkey = otpemail && (messageid != 2) && (messageid != 4);
@ -476,6 +478,7 @@
}
if (loginMode == '5') {
if (tokenTimeout > 0) { setTimeout(function () { Q('hwtokenInput').value = '**timeout**'; QE('tokenOkButton', true); Q('tokenOkButton').click(); }, tokenTimeout); }
try { if (hardwareKeyChallenge.length > 0) { hardwareKeyChallenge = JSON.parse(hardwareKeyChallenge); } else { hardwareKeyChallenge = null; } } catch (ex) { hardwareKeyChallenge = null }
var twofakey = (hardwareKeyChallenge != null) && (hardwareKeyChallenge.type == 'webAuthn');
var emailkey = otpemail && (messageid != 2) && (messageid != 4);

View File

@ -1018,6 +1018,12 @@ module.exports.CreateWebServer = function (parent, db, args, certificates) {
// Check if this user has 2-step login active
if ((req.session.loginmode != 6) && checkUserOneTimePasswordRequired(domain, user, req, loginOptions)) {
if ((req.body.hwtoken == '**timeout**')) {
delete req.session; // Clear the session
res.redirect(domain.url + getQueryPortion(req));
return;
}
if ((req.body.hwtoken == '**email**') && email2fa) {
user.otpekey = { k: obj.common.zeroPad(getRandomEightDigitInteger(), 8), d: Date.now() };
obj.db.SetUser(user);
@ -2879,6 +2885,12 @@ module.exports.CreateWebServer = function (parent, db, args, certificates) {
var customui = '';
if (domain.customui != null) { customui = encodeURIComponent(JSON.stringify(domain.customui)); }
// Get two-factor screen timeout
var twoFactorTimeout = 300000; // Default is 5 minutes, 0 for no timeout.
if ((typeof domain.passwordrequirements == 'object') && (typeof domain.passwordrequirements.twofactortimeout == 'number')) {
twoFactorTimeout = domain.passwordrequirements.twofactortimeout * 1000;
}
// Render the login page
render(req, res,
getRenderPage((domain.sitestyle == 2) ? 'login2' : 'login', req, domain),
@ -2907,7 +2919,8 @@ module.exports.CreateWebServer = function (parent, db, args, certificates) {
otppush: otppush,
twoFactorCookieDays: twoFactorCookieDays,
authStrategies: authStrategies.join(','),
loginpicture: (typeof domain.loginpicture == 'string')
loginpicture: (typeof domain.loginpicture == 'string'),
tokenTimeout: twoFactorTimeout // Two-factor authentication screen timeout in milliseconds
}, req, domain, (domain.sitestyle == 2) ? 'login2' : 'login'));
}