Add OTP feature to redesign
This commit is contained in:
parent
0b85c8a7f6
commit
d7c00945a7
@ -26,12 +26,14 @@
|
|||||||
<script src="vendor/js/fontawesome/fontawesome.min.js"></script>
|
<script src="vendor/js/fontawesome/fontawesome.min.js"></script>
|
||||||
<script src="vendor/js/fontawesome/solid.min.js"></script>
|
<script src="vendor/js/fontawesome/solid.min.js"></script>
|
||||||
<script src="vendor/js/fontawesome/regular.min.js"></script>
|
<script src="vendor/js/fontawesome/regular.min.js"></script>
|
||||||
|
<script src="vendor/js/jsOTP.min.js"></script>
|
||||||
<script src="js/polyfills.js"></script>
|
<script src="js/polyfills.js"></script>
|
||||||
<script src="js/material-palette.js"></script>
|
<script src="js/material-palette.js"></script>
|
||||||
|
|
||||||
<script src="js/notifications.js"></script>
|
<script src="js/notifications.js"></script>
|
||||||
<script src="js/accounts.js"></script>
|
<script src="js/accounts.js"></script>
|
||||||
<script src="js/home.js"></script>
|
<script src="js/home.js"></script>
|
||||||
|
<script src="js/otp.js"></script>
|
||||||
<script src="js/app.js"></script>
|
<script src="js/app.js"></script>
|
||||||
|
|
||||||
<script src="routes.js"></script>
|
<script src="routes.js"></script>
|
||||||
|
188
www/js/otp.js
Normal file
188
www/js/otp.js
Normal file
@ -0,0 +1,188 @@
|
|||||||
|
/*
|
||||||
|
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||||
|
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||||
|
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||||
|
*/
|
||||||
|
|
||||||
|
var totp = new jsOTP.totp();
|
||||||
|
|
||||||
|
function parseOTP(jsontext, reload) {
|
||||||
|
var keys = [];
|
||||||
|
if (jsontext !== null && jsontext != "") {
|
||||||
|
var keys = JSON.parse(jsontext || "[]");
|
||||||
|
if (keys.length > 0) {
|
||||||
|
//$("#nokeys").css("display", "none");
|
||||||
|
}
|
||||||
|
var tmpldata = [];
|
||||||
|
for (var i = 0; i < keys.length; i++) {
|
||||||
|
var code = totp.getOtp(keys[i]["secret"]);
|
||||||
|
// Escape HTML characters
|
||||||
|
var label = $('<div/>').html(keys[i]["label"]).html();
|
||||||
|
var issuer = $('<div/>').text(keys[i]["issuer"]).html();
|
||||||
|
tmpldata.push({
|
||||||
|
code: code,
|
||||||
|
secret: keys[i]["secret"],
|
||||||
|
label: label,
|
||||||
|
issuer: issuer,
|
||||||
|
index: i
|
||||||
|
});
|
||||||
|
}
|
||||||
|
router.navigate("/otp", {
|
||||||
|
context: {
|
||||||
|
otpcodes: tmpldata
|
||||||
|
},
|
||||||
|
reloadCurrent: (reload == true ? true : false)
|
||||||
|
});
|
||||||
|
setInterval(function () {
|
||||||
|
refreshCountdown();
|
||||||
|
refreshCodes();
|
||||||
|
}, 1000);
|
||||||
|
|
||||||
|
refreshCountdown();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function loadOTPPage(reload) {
|
||||||
|
var ls_text = localStorage.getItem("otp");
|
||||||
|
if (ls_text === null || ls_text == "") {
|
||||||
|
// Recover from NativeStorage
|
||||||
|
NativeStorage.getItem("otp", function (data) {
|
||||||
|
localStorage.setItem("otp");
|
||||||
|
parseOTP(data, reload);
|
||||||
|
}, function (error) {
|
||||||
|
parseOTP("[]", reload);
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
parseOTP(ls_text, reload);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function refreshCountdown() {
|
||||||
|
var percent = ((30 - ((new Date).getSeconds() % 30)) / 30) * 100;
|
||||||
|
$("#countdown").css("width", percent + "%");
|
||||||
|
}
|
||||||
|
|
||||||
|
function refreshCodes() {
|
||||||
|
$(".otpcard").each(function () {
|
||||||
|
var code = totp.getOtp($(this).data("secret"));
|
||||||
|
$(".otpcode", this).text(code);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
function deleteOTPCode(index) {
|
||||||
|
var label = $(".otpcard[data-index=" + index + "] .otplabel").text();
|
||||||
|
navigator.notification.confirm("Delete auth key? This cannot be undone, so make sure you don't need this key to login anymore!", function (result) {
|
||||||
|
if (result != 1) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
var keys = JSON.parse(localStorage.getItem("otp"));
|
||||||
|
keys.splice(index, 1);
|
||||||
|
localStorage.setItem("otp", JSON.stringify(keys));
|
||||||
|
NativeStorage.setItem("otp", JSON.stringify(keys));
|
||||||
|
loadOTPPage(true);
|
||||||
|
}, "Delete " + label + "?");
|
||||||
|
}
|
||||||
|
|
||||||
|
function addOTPCode(key, label, issuer) {
|
||||||
|
if (key == "") {
|
||||||
|
navigator.notification.alert("Missing secret key.", null, "Error", 'Dismiss');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
key = key.toUpperCase();
|
||||||
|
/* Thanks to https://stackoverflow.com/a/27362880 for the regex */
|
||||||
|
if (!key.match(/^(?:[A-Z2-7]{8})*(?:[A-Z2-7]{2}={6}|[A-Z2-7]{4}={4}|[A-Z2-7]{5}={3}|[A-Z2-7]{7}=)?$/)) {
|
||||||
|
navigator.notification.alert("Secret key is not valid base32.", null, "Error", 'Dismiss');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (label == "") {
|
||||||
|
navigator.notification.alert("Missing label.", null, "Error", 'Dismiss');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
var ls_text = localStorage.getItem("otp");
|
||||||
|
var keys = [];
|
||||||
|
if (ls_text != null && ls_text != "") {
|
||||||
|
keys = JSON.parse(ls_text || "[]");
|
||||||
|
}
|
||||||
|
|
||||||
|
keys.push({"secret": key, "label": label, "issuer": issuer});
|
||||||
|
localStorage.setItem("otp", JSON.stringify(keys));
|
||||||
|
NativeStorage.setItem("otp", JSON.stringify(keys));
|
||||||
|
|
||||||
|
app.toast.create({
|
||||||
|
text: '2-factor key saved.',
|
||||||
|
closeButton: true,
|
||||||
|
}).open();
|
||||||
|
loadOTPPage(true);
|
||||||
|
}
|
||||||
|
|
||||||
|
function scanOTPCode() {
|
||||||
|
try {
|
||||||
|
cordova.plugins.barcodeScanner.scan(
|
||||||
|
function (result) {
|
||||||
|
if (!result.cancelled) {
|
||||||
|
try {
|
||||||
|
var url = decodeURI(result.text);
|
||||||
|
} catch (e) {
|
||||||
|
navigator.notification.alert("Could not decode OTP URI.", null, "Error", 'Dismiss');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (!url.startsWith("otpauth://")) {
|
||||||
|
navigator.notification.alert("Invalid OTP code. Try again.", null, "Error", 'Dismiss');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (!url.startsWith("otpauth://totp/")) {
|
||||||
|
navigator.notification.alert("Unsupported key type.", null, "Error", 'Dismiss');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
var stripped = url.replace("otpauth://totp/", "");
|
||||||
|
var params = stripped.split("?")[1].split("&");
|
||||||
|
var label = stripped.split("?")[0];
|
||||||
|
var secret = "";
|
||||||
|
var issuer = "";
|
||||||
|
for (var i = 0; i < params.length; i++) {
|
||||||
|
var param = params[i].split("=");
|
||||||
|
if (param[0] == "secret") {
|
||||||
|
secret = param[1].toUpperCase();
|
||||||
|
} else if (param[0] == "issuer") {
|
||||||
|
issuer = param[1];
|
||||||
|
} else if (param[0] == "algorithm" && param[1].toLowerCase() != "sha1") {
|
||||||
|
navigator.notification.alert("Unsupported hash algorithm.", null, "Error", 'Dismiss');
|
||||||
|
return;
|
||||||
|
} else if (param[0] == "digits" && param[1] != "6") {
|
||||||
|
navigator.notification.alert("Unsupported digit count.", null, "Error", 'Dismiss');
|
||||||
|
return;
|
||||||
|
} else if (param[0] == "period" && param[1] != "30") {
|
||||||
|
navigator.notification.alert("Unsupported period.", null, "Error", 'Dismiss');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
secret = decodeURIComponent(secret);
|
||||||
|
issuer = decodeURIComponent(issuer);
|
||||||
|
label = decodeURIComponent(label);
|
||||||
|
} catch (e) {
|
||||||
|
navigator.notification.alert("Could not decode OTP URI.", null, "Error", 'Dismiss');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
addOTPCode(secret, label, issuer);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
function (error) {
|
||||||
|
navigator.notification.alert("Scanning failed: " + error, null, "Error", 'Dismiss');
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"showFlipCameraButton": false,
|
||||||
|
"prompt": "Scan OTP QR code."
|
||||||
|
}
|
||||||
|
);
|
||||||
|
} catch (ex) {
|
||||||
|
navigator.notification.alert(ex.message, null, "Error", 'Dismiss');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function addManualOTP() {
|
||||||
|
var label = $("#addotppopup #label").val();
|
||||||
|
var secret = $("#addotppopup #secret").val().replace(/\s+/g, '');
|
||||||
|
app.popup.close("#addotppopup");
|
||||||
|
addOTPCode(secret, label, "");
|
||||||
|
}
|
@ -1,149 +0,0 @@
|
|||||||
<!-- This Source Code Form is subject to the terms of the Mozilla Public
|
|
||||||
- License, v. 2.0. If a copy of the MPL was not distributed with this
|
|
||||||
- file, You can obtain one at http://mozilla.org/MPL/2.0/. -->
|
|
||||||
<div class="panel panel-blue">
|
|
||||||
<div class="panel-body">
|
|
||||||
<p></p>
|
|
||||||
<span class="btn btn-primary btn-lg" onclick="scanCode()" id="scancodebtn">
|
|
||||||
<i class="fa fa-qrcode"></i> Scan QR Code
|
|
||||||
</span>
|
|
||||||
<span class="btn btn-link" onclick="manualshow()" id="manualaddbtn">
|
|
||||||
Manual Entry
|
|
||||||
</span>
|
|
||||||
<div id="manual_add" class="well" style="display: none;">
|
|
||||||
<input type="text" id="key" class="form-control" placeholder="Secret key" autocomplete="off" autocorrect="off" autocapitalize="off" spellcheck="false" /> <br />
|
|
||||||
<input type="text" id="label" class="form-control" placeholder="Label" autocomplete="off" autocorrect="off" autocapitalize="off" spellcheck="false" /> <br />
|
|
||||||
<input type="text" id="issuer" class="form-control" placeholder="Issuer" autocomplete="off" autocorrect="off" autocapitalize="off" spellcheck="false" />
|
|
||||||
<br />
|
|
||||||
<div class="btn btn-primary" onclick="manualadd()">
|
|
||||||
Continue
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<script>
|
|
||||||
$("#key").on("keyup", function () {
|
|
||||||
if (window.getSelection().toString() !== '') {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
var text = $('#key').val().replace(/\s+/g, '');
|
|
||||||
var formatted = "";
|
|
||||||
for (var i = 1; i <= text.length; i++) {
|
|
||||||
formatted = formatted + text[i - 1];
|
|
||||||
if (i % 4 == 0 && i > 1 && i < text.length) {
|
|
||||||
// add a space every 5 characters,
|
|
||||||
// unless it's the first character
|
|
||||||
// or the last character
|
|
||||||
formatted = formatted + " ";
|
|
||||||
}
|
|
||||||
}
|
|
||||||
$('#key').val(formatted.toUpperCase());
|
|
||||||
});
|
|
||||||
|
|
||||||
function manualadd() {
|
|
||||||
var key = $('#key').val().replace(/\s+/g, '');
|
|
||||||
var label = $('#label').val();
|
|
||||||
var issuer = $('#issuer').val();
|
|
||||||
addOTP(key, label, issuer);
|
|
||||||
}
|
|
||||||
|
|
||||||
function manualshow() {
|
|
||||||
$('#manual_add').css('display', 'block');
|
|
||||||
}
|
|
||||||
|
|
||||||
function addOTP(key, label, issuer) {
|
|
||||||
if (key == "") {
|
|
||||||
navigator.notification.alert("Missing secret key.", null, "Error", 'Dismiss');
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
key = key.toUpperCase();
|
|
||||||
/* Thanks to https://stackoverflow.com/a/27362880 for the regex */
|
|
||||||
if (!key.match(/^(?:[A-Z2-7]{8})*(?:[A-Z2-7]{2}={6}|[A-Z2-7]{4}={4}|[A-Z2-7]{5}={3}|[A-Z2-7]{7}=)?$/)) {
|
|
||||||
navigator.notification.alert("Secret key is not valid base32.", null, "Error", 'Dismiss');
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
if (label == "") {
|
|
||||||
navigator.notification.alert("Missing label.", null, "Error", 'Dismiss');
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
var ls_text = localStorage.getItem("otp");
|
|
||||||
var keys = [];
|
|
||||||
if (ls_text != null && ls_text != "") {
|
|
||||||
keys = JSON.parse(ls_text || "[]");
|
|
||||||
}
|
|
||||||
|
|
||||||
keys.push({"secret": key, "label": label, "issuer": issuer});
|
|
||||||
localStorage.setItem("otp", JSON.stringify(keys));
|
|
||||||
NativeStorage.setItem("otp", JSON.stringify(keys));
|
|
||||||
navigator.notification.alert("2-factor key saved.", null, "Key added", 'Dismiss');
|
|
||||||
openscreen("otp");
|
|
||||||
}
|
|
||||||
|
|
||||||
function scanCode() {
|
|
||||||
try {
|
|
||||||
cordova.plugins.barcodeScanner.scan(
|
|
||||||
function (result) {
|
|
||||||
if (!result.cancelled) {
|
|
||||||
try {
|
|
||||||
var url = decodeURI(result.text);
|
|
||||||
} catch (e) {
|
|
||||||
navigator.notification.alert("Could not decode OTP URI.", null, "Error", 'Dismiss');
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
if (!url.startsWith("otpauth://")) {
|
|
||||||
navigator.notification.alert("Invalid OTP code. Try again.", null, "Error", 'Dismiss');
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
if (!url.startsWith("otpauth://totp/")) {
|
|
||||||
navigator.notification.alert("Unsupported key type.", null, "Error", 'Dismiss');
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
var stripped = url.replace("otpauth://totp/", "");
|
|
||||||
var params = stripped.split("?")[1].split("&");
|
|
||||||
var label = stripped.split("?")[0];
|
|
||||||
var secret = "";
|
|
||||||
var issuer = "";
|
|
||||||
for (var i = 0; i < params.length; i++) {
|
|
||||||
var param = params[i].split("=");
|
|
||||||
if (param[0] == "secret") {
|
|
||||||
secret = param[1].toUpperCase();
|
|
||||||
} else if (param[0] == "issuer") {
|
|
||||||
issuer = param[1];
|
|
||||||
} else if (param[0] == "algorithm" && param[1].toLowerCase() != "sha1") {
|
|
||||||
navigator.notification.alert("Unsupported hash algorithm.", null, "Error", 'Dismiss');
|
|
||||||
return;
|
|
||||||
} else if (param[0] == "digits" && param[1] != "6") {
|
|
||||||
navigator.notification.alert("Unsupported digit count.", null, "Error", 'Dismiss');
|
|
||||||
return;
|
|
||||||
} else if (param[0] == "period" && param[1] != "30") {
|
|
||||||
navigator.notification.alert("Unsupported period.", null, "Error", 'Dismiss');
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
try {
|
|
||||||
secret = decodeURIComponent(secret);
|
|
||||||
issuer = decodeURIComponent(issuer);
|
|
||||||
label = decodeURIComponent(label);
|
|
||||||
} catch (e) {
|
|
||||||
navigator.notification.alert("Could not decode OTP URI.", null, "Error", 'Dismiss');
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
addOTP(secret, label, issuer);
|
|
||||||
}
|
|
||||||
},
|
|
||||||
function (error) {
|
|
||||||
navigator.notification.alert("Scanning failed: " + error, null, "Error", 'Dismiss');
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"showFlipCameraButton": false,
|
|
||||||
"prompt": "Scan OTP QR code."
|
|
||||||
}
|
|
||||||
);
|
|
||||||
} catch (ex) {
|
|
||||||
navigator.notification.alert(ex.message, null, "Error", 'Dismiss');
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
setnavbar("app", "Add Auth Key", "otp");
|
|
||||||
</script>
|
|
@ -1,91 +0,0 @@
|
|||||||
<!-- This Source Code Form is subject to the terms of the Mozilla Public
|
|
||||||
- License, v. 2.0. If a copy of the MPL was not distributed with this
|
|
||||||
- file, You can obtain one at http://mozilla.org/MPL/2.0/. -->
|
|
||||||
<div class="progress">
|
|
||||||
<div class="progress-bar" id="countdown" style="width: 0%;"></div>
|
|
||||||
</div>
|
|
||||||
<div class="circle-btn btn btn-light-blue" onclick="openscreen('addotp', 'FADE');">
|
|
||||||
<img src="icons/ic_add.svg" />
|
|
||||||
</div>
|
|
||||||
<div id="nokeys">
|
|
||||||
<div class="app-dock-container">
|
|
||||||
<div class="app-dock" id="app-dock">
|
|
||||||
<div style="margin-top: 50px; text-align: center; font-size: 120%;">
|
|
||||||
<img src="img/nokeys.svg" alt="" style="max-width: 80%; max-height: 25%;" />
|
|
||||||
<br /><br />
|
|
||||||
<p style="max-width: 80%; margin: 0 auto;">You haven't added any authentication keys yet. Press <i class="fa fa-plus"></i> to add one.</p>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div class="list-group" id="codelist">
|
|
||||||
</div>
|
|
||||||
<script src="js/jsOTP.min.js"></script>
|
|
||||||
<script>
|
|
||||||
setnavbar("otp", "", "home");
|
|
||||||
|
|
||||||
var totp = new jsOTP.totp();
|
|
||||||
|
|
||||||
function load(jsontext) {
|
|
||||||
var keys = [];
|
|
||||||
if (jsontext !== null && jsontext != "") {
|
|
||||||
var keys = JSON.parse(jsontext || "[]");
|
|
||||||
if (keys.length > 0) {
|
|
||||||
$("#nokeys").css("display", "none");
|
|
||||||
}
|
|
||||||
for (var i = 0; i < keys.length; i++) {
|
|
||||||
var code = totp.getOtp(keys[i]["secret"]);
|
|
||||||
// Escape HTML characters
|
|
||||||
var label = $('<div/>').html(keys[i]["label"]).html();
|
|
||||||
var issuer = $('<div/>').text(keys[i]["issuer"]).html();
|
|
||||||
$("#codelist").append("<div class=\"list-group-item\" id=\"codeitem_" + i + "\">"
|
|
||||||
+ "<span class=\"pull-right\" style=\"color: red;\" onclick=\"deleteCode(" + i + ")\"><i class=\"fa fa-trash-o\"></i></span>"
|
|
||||||
+ "<p class=\"h6\">" + label + "</p>"
|
|
||||||
+ "<div class=\"h3 code\" style=\"font-weight: bold;\">" + code + "</div>"
|
|
||||||
+ "<p class=\"small\">" + issuer + "</p>"
|
|
||||||
+ "</div>");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
var ls_text = localStorage.getItem("otp");
|
|
||||||
if (ls_text === null || ls_text == "") {
|
|
||||||
// Recover from NativeStorage
|
|
||||||
NativeStorage.getItem("otp", function (data) {
|
|
||||||
localStorage.setItem("otp");
|
|
||||||
load(data);
|
|
||||||
});
|
|
||||||
} else {
|
|
||||||
load(ls_text);
|
|
||||||
}
|
|
||||||
|
|
||||||
function refreshCountdown() {
|
|
||||||
var percent = ((30 - ((new Date).getSeconds() % 30)) / 30) * 100;
|
|
||||||
$("#countdown").css("width", percent + "%");
|
|
||||||
}
|
|
||||||
|
|
||||||
function refreshCodes() {
|
|
||||||
for (var i = 0; i < keys.length; i++) {
|
|
||||||
var code = totp.getOtp(keys[i]["secret"]);
|
|
||||||
$("#codelist #codeitem_" + i + " .code").text(code);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function deleteCode(index) {
|
|
||||||
navigator.notification.confirm("Delete auth key? This cannot be undone, so make sure you don't need this key to login anymore!", function (result) {
|
|
||||||
if (result != 1) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
keys.splice(index, 1);
|
|
||||||
localStorage.setItem("otp", JSON.stringify(keys));
|
|
||||||
openscreen("otp");
|
|
||||||
}, "Delete " + keys[index]["label"] + "?");
|
|
||||||
}
|
|
||||||
|
|
||||||
setInterval(function () {
|
|
||||||
refreshCountdown();
|
|
||||||
refreshCodes();
|
|
||||||
}, 1000);
|
|
||||||
|
|
||||||
refreshCountdown();
|
|
||||||
</script>
|
|
@ -1,44 +0,0 @@
|
|||||||
<!-- This Source Code Form is subject to the terms of the Mozilla Public
|
|
||||||
- License, v. 2.0. If a copy of the MPL was not distributed with this
|
|
||||||
- file, You can obtain one at http://mozilla.org/MPL/2.0/. -->
|
|
||||||
<br />
|
|
||||||
<div class="panel panel-blue">
|
|
||||||
<div class="panel-heading">
|
|
||||||
<h3 class="panel-title">Setup</h3>
|
|
||||||
</div>
|
|
||||||
<div class="panel-body">
|
|
||||||
<p>Almost done!
|
|
||||||
<br /><br />
|
|
||||||
Please enter your password, then press Finish.
|
|
||||||
</p>
|
|
||||||
<input type="password" id="passbox" class="form-control" placeholder="Password" style="display: block;" />
|
|
||||||
<br />
|
|
||||||
<div class="btn btn-primary" onclick="savePassword()"><i class="fa fa-check"></i> Finish</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<script>
|
|
||||||
function savePassword() {
|
|
||||||
$.post(setupsyncurl, {
|
|
||||||
username: setupusername,
|
|
||||||
key: setupsynckey,
|
|
||||||
password: $('#passbox').val(),
|
|
||||||
action: "check_password"
|
|
||||||
}, function (data) {
|
|
||||||
if (data.status === 'OK') {
|
|
||||||
setuppassword = $('#passbox').val();
|
|
||||||
var accid = addaccount(setupusername, setuppassword, setupsyncurl, setupsynckey);
|
|
||||||
switchaccount(accid);
|
|
||||||
localStorage.setItem("firstrun", "1");
|
|
||||||
navigator.notification.alert("Account connected!", null, "Success", 'Continue');
|
|
||||||
restartApplication();
|
|
||||||
} else {
|
|
||||||
navigator.notification.alert(data.msg, null, "Error", 'Dismiss');
|
|
||||||
}
|
|
||||||
}, "json").fail(function () {
|
|
||||||
navigator.notification.alert("Could not connect to the server. Try again later.", null, "Error", 'Dismiss');
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
setnavbar("setup");
|
|
||||||
</script>
|
|
@ -6,6 +6,12 @@
|
|||||||
<div class="navbar">
|
<div class="navbar">
|
||||||
<div class="navbar-inner">
|
<div class="navbar-inner">
|
||||||
<div class="title">Business</div>
|
<div class="title">Business</div>
|
||||||
|
|
||||||
|
<div class="right">
|
||||||
|
<a href="#" onclick="loadOTPPage()" class="link icon-only">
|
||||||
|
<i class="fas fa-key"></i>
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
89
www/pages/otp.html
Normal file
89
www/pages/otp.html
Normal file
@ -0,0 +1,89 @@
|
|||||||
|
<!-- This Source Code Form is subject to the terms of the Mozilla Public
|
||||||
|
- License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||||
|
- file, You can obtain one at http://mozilla.org/MPL/2.0/. -->
|
||||||
|
<div class="page" data-name="otp">
|
||||||
|
|
||||||
|
<div class="navbar">
|
||||||
|
<div class="navbar-inner">
|
||||||
|
<div class="left">
|
||||||
|
<a href="#" class="link icon-only back">
|
||||||
|
<i class="icon icon-back"></i>
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
<div class="title">Auth Keys</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="popup" id="addotppopup">
|
||||||
|
<div class="view">
|
||||||
|
<div class="page">
|
||||||
|
<div class="page-content">
|
||||||
|
<div class="block">
|
||||||
|
<div class="list">
|
||||||
|
<ul>
|
||||||
|
<li class="item-content item-input">
|
||||||
|
<div class="item-inner">
|
||||||
|
<div class="item-input-wrap">
|
||||||
|
<input type="text" id="label" placeholder="Label" autocomplete="off" autocorrect="off" autocapitalize="off" spellcheck="false" />
|
||||||
|
<span class="input-clear-button"></span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</li>
|
||||||
|
|
||||||
|
<li class="item-content item-input">
|
||||||
|
<div class="item-inner">
|
||||||
|
<div class="item-input-wrap">
|
||||||
|
<input type="text" id="secret" placeholder="Secret" autocomplete="off" autocorrect="off" autocapitalize="off" spellcheck="false" />
|
||||||
|
<span class="input-clear-button"></span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="button button-fill" onclick="addManualOTP()">
|
||||||
|
Save
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="fab fab-right-bottom">
|
||||||
|
<a href="#">
|
||||||
|
<!-- First icon is visible when Speed Dial actions are closed -->
|
||||||
|
<i class="material-icons">add</i>
|
||||||
|
<!-- Second icon is visible when Speed Dial actions are opened -->
|
||||||
|
<i class="material-icons">close</i>
|
||||||
|
</a>
|
||||||
|
<!-- Speed Dial action buttons -->
|
||||||
|
<div class="fab-buttons fab-buttons-top">
|
||||||
|
<a href="#" data-popup="#addotppopup" class="fab-label-button popup-open">
|
||||||
|
<span><i class="fas fa-edit"></i></span>
|
||||||
|
<span class="fab-label">Manual</span>
|
||||||
|
</a>
|
||||||
|
<a href="#" onclick="scanOTPCode()" class="fab-label-button">
|
||||||
|
<span><i class="fas fa-qrcode"></i></span>
|
||||||
|
<span class="fab-label">QR code</span>
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="page-content">
|
||||||
|
{{#each otpcodes}}
|
||||||
|
<div class="card otpcard" data-secret="{{secret}}" data-index="{{index}}">
|
||||||
|
<div class="card-content card-content-padding">
|
||||||
|
<div>
|
||||||
|
<h1 class="otpcode">{{code}}</h1>
|
||||||
|
<a class="link float-right" onclick="deleteOTPCode('{{index}}')">
|
||||||
|
<i class="material-icons">delete</i>
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
<p class="otplabel">{{label}}</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
{{/each}}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
</div>
|
@ -38,7 +38,7 @@
|
|||||||
<li class="item-content item-input">
|
<li class="item-content item-input">
|
||||||
<div class="item-inner">
|
<div class="item-inner">
|
||||||
<div class="item-input-wrap">
|
<div class="item-input-wrap">
|
||||||
<input type="text" id="key" class="form-control" autocomplete="off" autocorrect="off" autocapitalize="off" spellcheck="false" />
|
<input type="text" id="key" placeholder="Key" autocomplete="off" autocorrect="off" autocapitalize="off" spellcheck="false" />
|
||||||
<span class="input-clear-button"></span>
|
<span class="input-clear-button"></span>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
@ -25,5 +25,10 @@ var routes = [
|
|||||||
path: '/app',
|
path: '/app',
|
||||||
url: './pages/app.html',
|
url: './pages/app.html',
|
||||||
name: 'app'
|
name: 'app'
|
||||||
}
|
},
|
||||||
|
{
|
||||||
|
path: '/otp',
|
||||||
|
templateUrl: './pages/otp.html',
|
||||||
|
name: 'otp'
|
||||||
|
},
|
||||||
];
|
];
|
Loading…
x
Reference in New Issue
Block a user