diff --git a/index.html b/index.html index 46287c6..32ae0bc 100644 --- a/index.html +++ b/index.html @@ -788,6 +788,16 @@ Sign out +
+

Secure private key

+

+ You can set a password to secure your private key and use the password instead of private key. + This is applied to this browser only. +

+ +

My UPI IDs

@@ -1434,6 +1444,26 @@
+ + + + + + + + diff --git a/scripts/components.js b/scripts/components.js index af1646a..dae48a7 100644 --- a/scripts/components.js +++ b/scripts/components.js @@ -1117,13 +1117,13 @@ customElements.define('sm-notifications', class extends HTMLElement { createNotification(message, options = {}) { const { pinned = false, icon = '', action } = options; - const notification = document.createElement('output') + const notification = document.createElement('div') notification.id = this.randString(8) notification.classList.add('notification'); let composition = ``; composition += `
${icon}
-

${message}

+ ${message} `; if (action) { composition += ` @@ -1177,6 +1177,7 @@ customElements.define('sm-notifications', class extends HTMLElement { } removeNotification(notification, direction = 'left') { + if (!notification) return; const sign = direction === 'left' ? '-' : '+'; notification.animate([ { @@ -1218,8 +1219,10 @@ customElements.define('sm-notifications', class extends HTMLElement { this.mediaQuery.addEventListener('change', this.handleOrientationChange); this.notificationPanel.addEventListener('pointerdown', e => { - if (e.target.closest('.notification')) { - this.swipeThreshold = this.clientWidth / 2; + if (e.target.closest('.close')) { + this.removeNotification(e.target.closest('.notification')); + } else if (e.target.closest('.notification')) { + this.swipeThreshold = e.target.closest('.notification').getBoundingClientRect().width / 2; this.currentTarget = e.target.closest('.notification'); this.currentTarget.setPointerCapture(e.pointerId); this.startTime = Date.now(); @@ -1262,12 +1265,6 @@ customElements.define('sm-notifications', class extends HTMLElement { this.notificationPanel.releasePointerCapture(e.pointerId); this.currentX = 0; }); - this.notificationPanel.addEventListener('click', e => { - if (e.target.closest('.close')) { - this.removeNotification(e.target.closest('.notification')); - } - }); - const observer = new MutationObserver(mutationList => { mutationList.forEach(mutation => { if (mutation.type === 'childList') { diff --git a/scripts/fn_ui.js b/scripts/fn_ui.js index ba9181c..7d6745a 100644 --- a/scripts/fn_ui.js +++ b/scripts/fn_ui.js @@ -11,9 +11,16 @@ const relativeTime = new RelativeTime({ style: 'narrow' }); // use floDapps.storeContact() to store contacts that can be used by other apps on same device -function syncUserData(obsName, data) { - const dataToSend = Crypto.AES.encrypt(JSON.stringify(data), myPrivKey); - return floCloudAPI.sendApplicationData(dataToSend, obsName, { receiverID: floDapps.user.id }); +async function syncUserData(obsName, data) { + floDapps.user.private.then(privateKey => { + if (!privateKey) return; + const encryptedData = Crypto.AES.encrypt(JSON.stringify(data), privateKey); + return floCloudAPI.sendApplicationData(encryptedData, obsName, { receiverID: floDapps.user.id }); + }).catch(error => { + console.log(error); + notify('Invalid password', 'error'); + return false; + }) } // store user data in separate IDB async function organizeSyncedData(obsName) { @@ -21,13 +28,20 @@ async function organizeSyncedData(obsName) { if (fetchedData.length && await compactIDB.readData(obsName, 'lastSyncTime') !== fetchedData[0].time) { await compactIDB.clearData(obsName); const dataToDecrypt = floCloudAPI.util.decodeMessage(fetchedData[0].message); - const decryptedData = JSON.parse(Crypto.AES.decrypt(dataToDecrypt, myPrivKey)); - for (let key in decryptedData) { - floGlobals[obsName][key] = decryptedData[key]; - compactIDB.addData(obsName, decryptedData[key], key); - } - compactIDB.addData(obsName, fetchedData[0].time, 'lastSyncTime'); - return true; + floDapps.user.private.then(privateKey => { + if (!privateKey) return; + const decryptedData = JSON.parse(Crypto.AES.decrypt(dataToDecrypt, privateKey)); + for (let key in decryptedData) { + floGlobals[obsName][key] = decryptedData[key]; + compactIDB.addData(obsName, decryptedData[key], key); + } + compactIDB.addData(obsName, fetchedData[0].time, 'lastSyncTime'); + return true; + }).catch(error => { + console.log(error); + notify('Invalid password', 'error'); + return false; + }) } else { const idbData = await compactIDB.readAllData(obsName); for (const key in idbData) { @@ -121,22 +135,30 @@ function withdrawMoneyFromWallet() { function transferToExchange() { const amount = parseFloat(getRef('exchange_transfer__amount').value.trim()); buttonLoader('exchange_transfer__button', true); - floExchangeAPI.depositToken('rupee', amount, floDapps.user.id, 'FRJkPqdbbsug3TtQRAWviqvTL9Qr2EMnrm', myPrivKey).then(txid => { - console.log(txid); - showChildElement('exchange_transfer_process', 1); - getRef('exchange_transfer__success_message').textContent = `Transferred ${formatAmount(amount)} to exchange`; - }).catch(error => { + floDapps.user.private.then(privateKey => { + if (!privateKey) return; + floExchangeAPI.depositToken('rupee', amount, floDapps.user.id, 'FRJkPqdbbsug3TtQRAWviqvTL9Qr2EMnrm', privateKey).then(txid => { + console.log(txid); + showChildElement('exchange_transfer_process', 1); + getRef('exchange_transfer__success_message').textContent = `Transferred ${formatAmount(amount)} to exchange`; + }).catch(error => { + console.log(error); + if (error.code) { + error = error.message; + } + if (error === 'Insufficient rupee# balance') + error = 'Insufficient rupee token balance in your wallet, please top-up your wallet.'; + getRef('exchange_transfer__failed_reason').textContent = error; + showChildElement('exchange_transfer_process', 2); + }).finally(() => { + buttonLoader('exchange_transfer__button', false); + }); + }).catch(error => { console.log(error); - if (error.code) { - error = error.message; - } - if (error === 'Insufficient rupee# balance') - error = 'Insufficient rupee token balance in your wallet, please top-up your wallet.'; - getRef('exchange_transfer__failed_reason').textContent = error; - showChildElement('exchange_transfer_process', 2); - }).finally(() => { - buttonLoader('exchange_transfer__button', false); - }); + notify('Invalid password', 'error'); + closePopup(); + return false; + }) } async function renderSavedUpiIds() { @@ -470,12 +492,13 @@ function declineTopUp() { } -function completeTokenToCashRequest(request) { +async function completeTokenToCashRequest(request) { const { vectorClock, senderID, message: { token_txid, amount, upi_id } } = request; var upiID; if (upi_id instanceof Object && "secret" in upi_id) { try { - upiID = floCrypto.decryptData(upi_id, myPrivKey); + const privateKey = await floGlobals.user.private + upiID = floCrypto.decryptData(upi_id, privateKey); } catch (error) { console.error("UPI ID is not encrypted with a proper key", error); return notify("Invalid UPI ID", 'error'); @@ -1197,17 +1220,26 @@ function getSignedIn(passwordType) { return new Promise((resolve, reject) => { try { console.log(floDapps.user.id) - + getPromptInput('Enter password', '', { + isPassword: true, + }).then(password => { + if (password) { + resolve(password) + } + }) }catch(err) { if (passwordType === 'PIN/Password') { + floGlobals.isPrivKeySecured = true; getRef('private_key_field').removeAttribute('data-private-key'); getRef('private_key_field').setAttribute('placeholder', 'Password'); getRef('private_key_field').customValidation = null - + getRef('secure_pwd_button').closest('.card').classList.add('hidden'); } else { + floGlobals.isPrivKeySecured = false; getRef('private_key_field').dataset.privateKey = '' getRef('private_key_field').setAttribute('placeholder', 'FLO private key'); - getRef('private_key_field').customValidation = floCrypto.getPubKeyHex + getRef('private_key_field').customValidation = floCrypto.getPubKeyHex; + getRef('secure_pwd_button').closest('.card').classList.remove('hidden'); } if (window.location.hash.includes('sign_in') || window.location.hash.includes('sign_up')) { showPage(window.location.hash); @@ -1227,6 +1259,19 @@ function getSignedIn(passwordType) { } }); } +function setSecurePassword() { + if (!floGlobals.isPrivKeySecured) { + const password = getRef('secure_pwd_input').value.trim(); + floDapps.securePrivKey(password).then(() => { + floGlobals.isPrivKeySecured = true; + notify('Password set successfully', 'success'); + getRef('secure_pwd_button').closest('.card').classList.add('hidden'); + closePopup(); + }).catch(err => { + notify(err, 'error'); + }) + } +} function signOut() { getConfirmation('Sign out?', 'You are about to sign out of the app, continue?', 'Stay', 'Leave') .then(async (res) => { @@ -1372,31 +1417,38 @@ getRef('fees_selector').addEventListener('change', e => { }) -getRef('send_transaction').onclick = evt => { +getRef('send_transaction').onclick = evt => { buttonLoader('send_transaction', true) - const senders = btc_api.convert.legacy2bech(floDapps.user.id); - const privKeys = btc_api.convert.wif(myPrivKey); - const receivers = [...getRef('receiver_container').querySelectorAll('.receiver-input')].map(input => input.value.trim()); - const amounts = [...getRef('receiver_container').querySelectorAll('.amount-input')].map(input => { - return parseFloat(input.value.trim()) - }); - const fee = parseFloat(getRef('send_fee').value.trim()); - console.debug(senders, receivers, amounts, fee); - btc_api.sendTx(senders, privKeys, receivers, amounts, fee).then(result => { - console.log(result); - closePopup(); - getRef('txid').value = result.txid; - openPopup('txid_popup'); - getRef('send_tx').reset() - getExchangeRate().then(() => { - calculateBtcFees() - }).catch(e => { - console.error(e) + floGlobals.user.private.then(privateKey => { + const privKeys = btc_api.convert.wif(privateKey); + const senders = btc_api.convert.legacy2bech(floDapps.user.id); + const receivers = [...getRef('receiver_container').querySelectorAll('.receiver-input')].map(input => input.value.trim()); + const amounts = [...getRef('receiver_container').querySelectorAll('.amount-input')].map(input => { + return parseFloat(input.value.trim()) + }); + const fee = parseFloat(getRef('send_fee').value.trim()); + console.debug(senders, receivers, amounts, fee); + btc_api.sendTx(senders, privKeys, receivers, amounts, fee).then(result => { + console.log(result); + closePopup(); + getRef('txid').value = result.txid; + openPopup('txid_popup'); + getRef('send_tx').reset() + getExchangeRate().then(() => { + calculateBtcFees() + }).catch(e => { + console.error(e) + }) + }).catch(error => { + notify(`Error sending transaction \n ${error}`, 'error'); + }).finally(_ => { + buttonLoader('send_transaction', false) }) }).catch(error => { - notify(`Error sending transaction \n ${error}`, 'error'); - }).finally(_ => { - buttonLoader('send_transaction', false) + console.log(error); + notify('Invalid password', 'error'); + closePopup(); + return false; }) } diff --git a/scripts/std_ui.js b/scripts/std_ui.js index 64b1608..6cbb02f 100644 --- a/scripts/std_ui.js +++ b/scripts/std_ui.js @@ -134,6 +134,7 @@ document.addEventListener('popupclosed', e => { break; case 'transfer_to_exchange_popup': showChildElement('exchange_transfer_process', 0); + buttonLoader('exchange_transfer__button', false); break; case 'confirm_topup_popup': showChildElement('confirm_topup_wrapper', 0);