TOTP: improve login UI

This commit is contained in:
yflory 2023-06-06 12:34:24 +02:00
parent 36a1c604d8
commit 5b703c8db5
4 changed files with 69 additions and 24 deletions

View file

@ -135,7 +135,7 @@ define([
Exports.mergeAnonDrive = 1;
};
Exports.loginOrRegister = function (uname, passwd, isRegister, shouldImport, cb) {
Exports.loginOrRegister = function (uname, passwd, isRegister, shouldImport, onOTP, cb) {
if (typeof(cb) !== 'function') { return; }
// Usernames are all lowercase. No going back on this one
@ -173,23 +173,19 @@ define([
// determine where a block for your set of keys would be stored
blockUrl = Block.getBlockUrl(res.opt.blockKeys);
var TOTP_prompt = function (cb) {
// XXX This should use nice UI elements integrated into
// the loading screen. window.prompt is here for prototyping only
var code = window.prompt('Enter TOTP');
if (!code) {
return void cb("INVALID_TOTP");
}
ServerCommand(res.opt.blockKeys.sign, {
command: 'TOTP_VALIDATE',
code: code,
// TODO optionally allow the user to specify a lifetime for this session?
// this will require a little bit of server work
// and more UI/UX:
// ie. just a simple "remember me" checkbox?
// allow them to specify a lifetime for the session?
// "log me out after one day"?
}, cb);
var TOTP_prompt = function (err, cb) {
onOTP(err, function (code) {
ServerCommand(res.opt.blockKeys.sign, {
command: 'TOTP_VALIDATE',
code: code,
// TODO optionally allow the user to specify a lifetime for this session?
// this will require a little bit of server work
// and more UI/UX:
// ie. just a simple "remember me" checkbox?
// allow them to specify a lifetime for the session?
// "log me out after one day"?
}, cb);
});
};
var done = waitFor();
@ -248,7 +244,7 @@ define([
return void cb('TOTP_ATTEMPTS_EXHAUSTED');
}
tries--;
TOTP_prompt(function (err, response) {
TOTP_prompt(tries !== 2, function (err, response) {
// ask again until your number of tries are exhausted
if (err) {
console.error(err);
@ -558,7 +554,7 @@ define([
};
var hashing;
Exports.loginOrRegisterUI = function (uname, passwd, isRegister, shouldImport, testing, test) {
Exports.loginOrRegisterUI = function (uname, passwd, isRegister, shouldImport, onOTP, testing, test) {
if (hashing) { return void console.log("hashing is already in progress"); }
hashing = true;
@ -583,7 +579,7 @@ define([
// We need a setTimeout(cb, 0) otherwise the loading screen is only displayed
// after hashing the password
window.setTimeout(function () {
Exports.loginOrRegister(uname, passwd, isRegister, shouldImport, function (err, result) {
Exports.loginOrRegister(uname, passwd, isRegister, shouldImport, onOTP, function (err, result) {
var proxy;
if (result) { proxy = result.proxy; }

View file

@ -6,10 +6,12 @@ define([
'/common/common-realtime.js',
'/common/common-feedback.js',
'/common/outer/local-store.js',
'/common/hyperscript.js',
'/customize/messages.js',
//'/common/test.js',
'css!/bower_components/components-font-awesome/css/font-awesome.min.css',
], function ($, Cryptpad, Login, UI, Realtime, Feedback, LocalStore /*, Test */) {
], function ($, Cryptpad, Login, UI, Realtime, Feedback, LocalStore, h, Msg /*, Test */) {
if (window.top !== window) { return; }
$(function () {
var $checkImport = $('#import-recent');
@ -19,6 +21,11 @@ define([
return;
}
Msg.settings_totp_code = "OTP code"; // XXX KEY ALREADY ADDED IN www/settings/inner.js
Msg.login_enter_totp = "This account is protected with MFA. Please enter your OTP code."; // XXX
Msg.login_invalid_otp = "Invalid OTP code";
/* Log in UI */
// deferred execution to avoid unnecessary asset loading
var loginReady = function (cb) {
@ -44,12 +51,47 @@ define([
$('button.login').click();
});
var onOTP = function (err, cb) {
var btn, input;
var error;
if (err) {
console.error(err);
error = h('p.cp-password-error', Msg.login_invalid_otp);
}
var block = h('div#cp-loading-password-prompt', [
error,
h('p.cp-password-info', Msg.login_enter_totp),
h('p.cp-password-form', [
input = h('input', {
placeholder: Msg.settings_totp_code,
autocomplete: 'off',
autocorrect: 'off',
autocapitalize: 'off',
spellcheck: false,
}),
btn = h('button.btn.btn-primary', Msg.ui_confirm)
])
]);
var $input = $(input);
var $btn = $(btn).click(function () {
var val = $input.val();
if (!val) { return void onOTP('INVALID_CODE', cb); }
cb(val);
});
$(input).on('keydown', function (e) {
if (e.which !== 13) { return; } // enter
$btn.click();
});
UI.errorLoadingScreen(block, false, false);
};
//var test;
$('button.login').click(function () {
var shouldImport = $checkImport[0].checked;
var uname = $uname.val();
var passwd = $passwd.val();
Login.loginOrRegisterUI(uname, passwd, false, shouldImport, /*Test.testing */ false, function () {
Login.loginOrRegisterUI(uname, passwd, false, shouldImport, onOTP, /*Test.testing */ false, function () {
/*
if (test) {
localStorage.clear();

View file

@ -103,6 +103,13 @@
margin-right: 5px;
}
}
.cp-settings-totp {
.cp-settings-qr {
img {
border: 10px solid white;
}
}
}
}
}

View file

@ -951,7 +951,7 @@ define([
var uri = `otpauth://totp/${label}:${username}@${hostname}?secret=${secret}`;
var qr = h('div');
var qr = h('div.cp-settings-qr');
var uriInput = UI.dialog.selectable(uri);
updateQR(uri, qr);