TOTP: improve login UI
This commit is contained in:
parent
36a1c604d8
commit
5b703c8db5
4 changed files with 69 additions and 24 deletions
|
@ -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; }
|
||||
|
||||
|
|
|
@ -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();
|
||||
|
|
|
@ -103,6 +103,13 @@
|
|||
margin-right: 5px;
|
||||
}
|
||||
}
|
||||
.cp-settings-totp {
|
||||
.cp-settings-qr {
|
||||
img {
|
||||
border: 10px solid white;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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);
|
||||
|
||||
|
|
Loading…
Reference in a new issue