diff --git a/views/login-mobile.handlebars b/views/login-mobile.handlebars
index 78e404f5..c5fe0ff4 100644
--- a/views/login-mobile.handlebars
+++ b/views/login-mobile.handlebars
@@ -158,7 +158,7 @@
-
+
|
@@ -302,6 +302,7 @@
var currentpanel = 0;
var otpemail = ('{{{otpemail}}}' === 'true');
var otpsms = ('{{{otpsms}}}' === 'true');
+ var twoFactorCookieDays = parseInt('{{{twoFactorCookieDays}}}');
// Display the right server message
var messageid = parseInt('{{{messageid}}}');
@@ -328,6 +329,14 @@
Q('resetpasswordformargs').value = urlargs;
}
+ // Setup two factor cookie time
+ if (twoFactorCookieDays > 0) {
+ QV('tokenInputRememberLabel', true);
+ QH('tokenInputRememberSpan', format("Remember this device for {0} days.", twoFactorCookieDays));
+ } else {
+ QV('tokenInputRememberLabel', false);
+ }
+
function startup() {
if ((features & 32) == 0) {
// Guard against other site's top frames (web bugs).
diff --git a/views/login.handlebars b/views/login.handlebars
index e947cd17..22b12ed1 100644
--- a/views/login.handlebars
+++ b/views/login.handlebars
@@ -154,7 +154,7 @@
-
+
|
@@ -301,6 +301,7 @@
var publicKeyCredentialRequestOptions = null;
var otpemail = ('{{{otpemail}}}' === 'true');
var otpsms = ('{{{otpsms}}}' === 'true');
+ var twoFactorCookieDays = parseInt('{{{twoFactorCookieDays}}}');
// Display the right server message
var messageid = parseInt('{{{messageid}}}');
@@ -322,6 +323,14 @@
Q('termsLinkFooter').href += '?key=' + urlargs.key;
}
+ // Setup two factor cookie time
+ if (twoFactorCookieDays > 0) {
+ QV('tokenInputRememberLabel', true);
+ QH('tokenInputRememberSpan', format("Remember this device for {0} days.", twoFactorCookieDays));
+ } else {
+ QV('tokenInputRememberLabel', false);
+ }
+
// If URL arguments are provided, add them to form posts
if (window.location.href.indexOf('?') > 0) {
var xurlargs = window.location.href.substring(window.location.href.indexOf('?'));
diff --git a/webserver.js b/webserver.js
index 0f4fe730..cce10f8f 100644
--- a/webserver.js
+++ b/webserver.js
@@ -595,7 +595,7 @@ module.exports.CreateWebServer = function (parent, db, args, certificates) {
for (var i in cookies) {
if (cookies[i].startsWith('twofactor=')) {
var twoFactorCookie = obj.parent.decodeCookie(decodeURIComponent(cookies[i].substring(10)), obj.parent.loginCookieEncryptionKey, (30 * 24 * 60)); // 30 day timeout
- if ((twoFactorCookie != null) && (obj.args.cookieipcheck !== false) && ((twoFactorCookie.ip == null) || (twoFactorCookie.ip === cleanRemoteAddr(req.ip))) && (twoFactorCookie.userid == user._id)) { return false; }
+ if ((twoFactorCookie != null) && ((obj.args.cookieipcheck === false) || (twoFactorCookie.ip == null) || (twoFactorCookie.ip === cleanRemoteAddr(req.ip))) && (twoFactorCookie.userid == user._id)) { return false; }
}
}
}
@@ -851,9 +851,12 @@ module.exports.CreateWebServer = function (parent, db, args, certificates) {
}, randomWaitTime);
} else {
// Check if we need to remember this device
- if (req.body.remembertoken === 'on') {
- const twoFactorCookie = obj.parent.encodeCookie({ userid: user._id /*, ip: cleanRemoteAddr(req.ip)*/ }, obj.parent.loginCookieEncryptionKey);
- res.cookie('twofactor', twoFactorCookie, { maxAge: (30 * 24 * 60 * 60 * 1000), httpOnly: true, sameSite: 'strict', secure: true });
+ if ((req.body.remembertoken === 'on') && ((domain.twofactorcookiedurationdays == null) || (domain.twofactorcookiedurationdays > 0))) {
+ var maxCookieAge = domain.twofactorcookiedurationdays;
+ if (typeof maxCookieAge != 'number') { maxCookieAge = 30; }
+ console.log('maxCookieAge', maxCookieAge);
+ const twoFactorCookie = obj.parent.encodeCookie({ userid: user._id, expire: maxCookieAge * 24 * 60 /*, ip: cleanRemoteAddr(req.ip)*/ }, obj.parent.loginCookieEncryptionKey);
+ res.cookie('twofactor', twoFactorCookie, { maxAge: (maxCookieAge * 24 * 60 * 60 * 1000), httpOnly: true, sameSite: 'strict', secure: true });
}
// Check if email address needs to be confirmed
@@ -1977,8 +1980,12 @@ module.exports.CreateWebServer = function (parent, db, args, certificates) {
var otpsms = (parent.smsserver != null) && (req.session != null) && (req.session.tokensms == true);
if ((typeof domain.passwordrequirements == 'object') && (domain.passwordrequirements.sms2factor == false)) { otpsms = false; }
+ // See if we support two-factor trusted cookies
+ var twoFactorCookieDays = 30;
+ if (typeof domain.twofactorcookiedurationdays == 'number') { twoFactorCookieDays = domain.twofactorcookiedurationdays; }
+
// Render the login page
- render(req, res, getRenderPage('login', req, domain), getRenderArgs({ loginmode: loginmode, rootCertLink: getRootCertLink(), newAccount: newAccountsAllowed, newAccountPass: (((domain.newaccountspass == null) || (domain.newaccountspass == '')) ? 0 : 1), serverDnsName: obj.getWebServerName(domain), serverPublicPort: httpsPort, emailcheck: emailcheck, features: features, sessiontime: args.sessiontime, passRequirements: passRequirements, footer: (domain.footer == null) ? '' : domain.footer, hkey: encodeURIComponent(hardwareKeyChallenge), messageid: msgid, passhint: passhint, welcometext: domain.welcometext ? encodeURIComponent(domain.welcometext).split('\'').join('\\\'') : null, hwstate: hwstate, otpemail: otpemail, otpsms: otpsms }, domain));
+ render(req, res, getRenderPage('login', req, domain), getRenderArgs({ loginmode: loginmode, rootCertLink: getRootCertLink(), newAccount: newAccountsAllowed, newAccountPass: (((domain.newaccountspass == null) || (domain.newaccountspass == '')) ? 0 : 1), serverDnsName: obj.getWebServerName(domain), serverPublicPort: httpsPort, emailcheck: emailcheck, features: features, sessiontime: args.sessiontime, passRequirements: passRequirements, footer: (domain.footer == null) ? '' : domain.footer, hkey: encodeURIComponent(hardwareKeyChallenge), messageid: msgid, passhint: passhint, welcometext: domain.welcometext ? encodeURIComponent(domain.welcometext).split('\'').join('\\\'') : null, hwstate: hwstate, otpemail: otpemail, otpsms: otpsms, twoFactorCookieDays: twoFactorCookieDays }, domain));
}
// Handle a post request on the root