[#9354] Unlock keycard account without a card
To make it work `encryption-public-key` and `whisper-private-key` are stored on the devices when a user chooses this option. The former key is used for multiaccount's database encryption, the latter is needed for a messaging. In case if a user wants to sign a transaction the card is still needed, we don't store wallet's keys on the device. Other things were fixed/added: - A user can enable biometric auth for a regular account when chooses to save the password on the device (if biometric auth is available). This is done for feature parity between keycard and "on device" accounts. - The option to create/restore an account on a keycard is not shown on the devices which do not support NFC. Currently, the app just crashes if the user continues a flow which is not supported by the device.
This commit is contained in:
parent
cd0486227d
commit
5bb6997d1d
|
@ -686,7 +686,7 @@
|
||||||
(handlers/register-handler-fx
|
(handlers/register-handler-fx
|
||||||
:hardwallet.callback/check-nfc-support-success
|
:hardwallet.callback/check-nfc-support-success
|
||||||
(fn [cofx [_ supported?]]
|
(fn [cofx [_ supported?]]
|
||||||
(hardwallet/set-nfc-support cofx supported?)))
|
(hardwallet/set-nfc-supported cofx supported?)))
|
||||||
|
|
||||||
(handlers/register-handler-fx
|
(handlers/register-handler-fx
|
||||||
:hardwallet.callback/on-card-connected
|
:hardwallet.callback/on-card-connected
|
||||||
|
@ -1592,4 +1592,4 @@
|
||||||
(commands.sending/send public-key
|
(commands.sending/send public-key
|
||||||
request-command
|
request-command
|
||||||
{:asset (name symbol)
|
{:asset (name symbol)
|
||||||
:amount (str (money/internal->formatted amount symbol decimals))})))))
|
:amount (str (money/internal->formatted amount symbol decimals))})))))
|
||||||
|
|
|
@ -2,20 +2,16 @@
|
||||||
(:require [clojure.string :as string]
|
(:require [clojure.string :as string]
|
||||||
[re-frame.core :as re-frame]
|
[re-frame.core :as re-frame]
|
||||||
[status-im.multiaccounts.create.core :as multiaccounts.create]
|
[status-im.multiaccounts.create.core :as multiaccounts.create]
|
||||||
[status-im.multiaccounts.login.core :as multiaccounts.login]
|
|
||||||
[status-im.multiaccounts.logout.core :as multiaccounts.logout]
|
[status-im.multiaccounts.logout.core :as multiaccounts.logout]
|
||||||
[status-im.multiaccounts.recover.core :as multiaccounts.recover]
|
|
||||||
[status-im.ethereum.core :as ethereum]
|
[status-im.ethereum.core :as ethereum]
|
||||||
[status-im.ethereum.mnemonic :as mnemonic]
|
[status-im.ethereum.mnemonic :as mnemonic]
|
||||||
[status-im.i18n :as i18n]
|
[status-im.i18n :as i18n]
|
||||||
[status-im.node.core :as node]
|
|
||||||
[status-im.ui.screens.navigation :as navigation]
|
[status-im.ui.screens.navigation :as navigation]
|
||||||
[status-im.utils.config :as config]
|
[status-im.utils.config :as config]
|
||||||
[status-im.utils.datetime :as utils.datetime]
|
[status-im.utils.datetime :as utils.datetime]
|
||||||
[status-im.utils.fx :as fx]
|
[status-im.utils.fx :as fx]
|
||||||
[status-im.utils.platform :as platform]
|
[status-im.utils.platform :as platform]
|
||||||
[status-im.utils.types :as types]
|
[status-im.utils.types :as types]
|
||||||
[status-im.wallet.core :as wallet]
|
|
||||||
[taoensso.timbre :as log]
|
[taoensso.timbre :as log]
|
||||||
status-im.hardwallet.fx
|
status-im.hardwallet.fx
|
||||||
[status-im.ui.components.react :as react]
|
[status-im.ui.components.react :as react]
|
||||||
|
@ -23,7 +19,9 @@
|
||||||
[status-im.multiaccounts.update.core :as multiaccounts.update]
|
[status-im.multiaccounts.update.core :as multiaccounts.update]
|
||||||
[status-im.ui.components.bottom-sheet.core :as bottom-sheet]
|
[status-im.ui.components.bottom-sheet.core :as bottom-sheet]
|
||||||
[status-im.multiaccounts.recover.core :as recover]
|
[status-im.multiaccounts.recover.core :as recover]
|
||||||
[status-im.ethereum.eip55 :as eip55]))
|
[status-im.ethereum.eip55 :as eip55]
|
||||||
|
[status-im.utils.keychain.core :as keychain]
|
||||||
|
[status-im.hardwallet.nfc :as nfc]))
|
||||||
|
|
||||||
(def default-pin "000000")
|
(def default-pin "000000")
|
||||||
|
|
||||||
|
@ -59,6 +57,11 @@
|
||||||
(:keycard-pairing
|
(:keycard-pairing
|
||||||
(find-multiaccount-by-key-uid db key-uid))))))
|
(find-multiaccount-by-key-uid db key-uid))))))
|
||||||
|
|
||||||
|
(re-frame/reg-fx
|
||||||
|
:hardwallet/set-nfc-supported
|
||||||
|
(fn [supported?]
|
||||||
|
(nfc/set-nfc-supported? supported?)))
|
||||||
|
|
||||||
(fx/defn listen-to-hardware-back-button
|
(fx/defn listen-to-hardware-back-button
|
||||||
[{:keys [db]}]
|
[{:keys [db]}]
|
||||||
(when-not (get-in db [:hardwallet :back-button-listener])
|
(when-not (get-in db [:hardwallet :back-button-listener])
|
||||||
|
@ -164,10 +167,10 @@
|
||||||
remove-instance-uid? (assoc :keycard-instance-uid nil))
|
remove-instance-uid? (assoc :keycard-instance-uid nil))
|
||||||
{}))
|
{}))
|
||||||
|
|
||||||
(defn hardwallet-supported? [{:keys [db]}]
|
(defn hardwallet-supported? []
|
||||||
(and config/hardwallet-enabled?
|
(and config/hardwallet-enabled?
|
||||||
platform/android?
|
platform/android?
|
||||||
(get-in db [:hardwallet :nfc-supported?])))
|
(nfc/nfc-supported?)))
|
||||||
|
|
||||||
(fx/defn unauthorized-operation
|
(fx/defn unauthorized-operation
|
||||||
[{:keys [db] :as cofx}]
|
[{:keys [db] :as cofx}]
|
||||||
|
@ -628,7 +631,7 @@
|
||||||
pairing (get-in db [:multiaccounts/multiaccounts multiaccount-address :keycard-pairing])
|
pairing (get-in db [:multiaccounts/multiaccounts multiaccount-address :keycard-pairing])
|
||||||
pin (string/join (get-in db [:hardwallet :pin :login]))]
|
pin (string/join (get-in db [:hardwallet :pin :login]))]
|
||||||
(when (and pairing
|
(when (and pairing
|
||||||
(not (empty? pin)))
|
(seq pin))
|
||||||
{:db (-> db
|
{:db (-> db
|
||||||
(assoc-in [:hardwallet :pin :status] :verifying))
|
(assoc-in [:hardwallet :pin :status] :verifying))
|
||||||
:hardwallet/get-keys {:pairing pairing
|
:hardwallet/get-keys {:pairing pairing
|
||||||
|
@ -723,9 +726,9 @@
|
||||||
(when on-card-read
|
(when on-card-read
|
||||||
(dispatch-event on-card-read)))))))
|
(dispatch-event on-card-read)))))))
|
||||||
|
|
||||||
(fx/defn set-nfc-support
|
(fx/defn set-nfc-supported
|
||||||
[{:keys [db]} supported?]
|
[_ supported?]
|
||||||
{:db (assoc-in db [:hardwallet :nfc-supported?] supported?)})
|
{:hardwallet/set-nfc-supported supported?})
|
||||||
|
|
||||||
(fx/defn keycard-option-pressed
|
(fx/defn keycard-option-pressed
|
||||||
{:events [:onboarding.ui/keycard-option-pressed]}
|
{:events [:onboarding.ui/keycard-option-pressed]}
|
||||||
|
@ -1774,14 +1777,15 @@
|
||||||
(let [account-data (js->clj data :keywordize-keys true)]
|
(let [account-data (js->clj data :keywordize-keys true)]
|
||||||
(fx/merge cofx
|
(fx/merge cofx
|
||||||
{:db (-> db
|
{:db (-> db
|
||||||
(assoc-in [:hardwallet :multiaccount] (-> account-data
|
(assoc-in [:hardwallet :multiaccount]
|
||||||
(update :address #(str "0x" %))
|
(-> account-data
|
||||||
(update :whisper-address #(str "0x" %))
|
(update :address ethereum/normalized-hex)
|
||||||
(update :wallet-address #(str "0x" %))
|
(update :whisper-address ethereum/normalized-hex)
|
||||||
(update :public-key #(str "0x" %))
|
(update :wallet-address ethereum/normalized-hex)
|
||||||
(update :whisper-public-key #(str "0x" %))
|
(update :public-key ethereum/normalized-hex)
|
||||||
(update :wallet-public-key #(str "0x" %))
|
(update :whisper-public-key ethereum/normalized-hex)
|
||||||
(update :instance-uid #(get-in db [:hardwallet :multiaccount :instance-uid] %))))
|
(update :wallet-public-key ethereum/normalized-hex)
|
||||||
|
(update :instance-uid #(get-in db [:hardwallet :multiaccount :instance-uid] %))))
|
||||||
(assoc-in [:hardwallet :multiaccount-wallet-address] (:wallet-address account-data))
|
(assoc-in [:hardwallet :multiaccount-wallet-address] (:wallet-address account-data))
|
||||||
(assoc-in [:hardwallet :multiaccount-whisper-public-key] (:whisper-public-key account-data))
|
(assoc-in [:hardwallet :multiaccount-whisper-public-key] (:whisper-public-key account-data))
|
||||||
(assoc-in [:hardwallet :application-info :key-uid] (:key-uid account-data))
|
(assoc-in [:hardwallet :application-info :key-uid] (:key-uid account-data))
|
||||||
|
@ -1808,28 +1812,65 @@
|
||||||
|
|
||||||
(fx/defn on-get-keys-success
|
(fx/defn on-get-keys-success
|
||||||
[{:keys [db] :as cofx} data]
|
[{:keys [db] :as cofx} data]
|
||||||
(let [{:keys [address whisper-address encryption-public-key whisper-private-key] :as account-data} (js->clj data :keywordize-keys true)
|
(let [{:keys [address encryption-public-key whisper-private-key] :as account-data} (js->clj data :keywordize-keys true)
|
||||||
address (str "0x" address)
|
address (ethereum/normalized-hex address)
|
||||||
{:keys [photo-path name]} (get-in db [:multiaccounts/multiaccounts address])
|
{:keys [photo-path name]} (get-in db [:multiaccounts/multiaccounts address])
|
||||||
key-uid (get-in db [:hardwallet :application-info :key-uid])
|
key-uid (get-in db [:hardwallet :application-info :key-uid])
|
||||||
multiaccount-data (types/clj->json {:name name :address address :photo-path photo-path})]
|
multiaccount-data (types/clj->json {:name name :address address :photo-path photo-path})
|
||||||
(fx/merge cofx
|
save-keys? (get-in db [:multiaccounts/login :save-password?])]
|
||||||
{:db (-> db
|
(fx/merge
|
||||||
(assoc-in [:hardwallet :pin :status] nil)
|
cofx
|
||||||
(assoc-in [:hardwallet :pin :login] [])
|
{:db
|
||||||
(assoc-in [:hardwallet :multiaccount] (update account-data :whisper-public-key #(str "0x" %)))
|
(-> db
|
||||||
(assoc-in [:hardwallet :flow] nil)
|
(assoc-in [:hardwallet :pin :status] nil)
|
||||||
(update :multiaccounts/login assoc
|
(assoc-in [:hardwallet :pin :login] [])
|
||||||
:password encryption-public-key
|
(assoc-in [:hardwallet :multiaccount]
|
||||||
:address address
|
(update account-data :whisper-public-key ethereum/normalized-hex))
|
||||||
:photo-path photo-path
|
(assoc-in [:hardwallet :flow] nil)
|
||||||
:name name))
|
(update :multiaccounts/login assoc
|
||||||
:hardwallet/get-application-info {:pairing (get-pairing db key-uid)}
|
:password encryption-public-key
|
||||||
:hardwallet/login-with-keycard {:multiaccount-data multiaccount-data
|
:address address
|
||||||
:password encryption-public-key
|
:photo-path photo-path
|
||||||
:chat-key whisper-private-key}}
|
:name name))
|
||||||
(clear-on-card-connected)
|
|
||||||
(clear-on-card-read))))
|
:hardwallet/get-application-info {:pairing (get-pairing db key-uid)}
|
||||||
|
:hardwallet/login-with-keycard {:multiaccount-data multiaccount-data
|
||||||
|
:password encryption-public-key
|
||||||
|
:chat-key whisper-private-key}}
|
||||||
|
(when save-keys?
|
||||||
|
(keychain/save-hardwallet-keys address encryption-public-key whisper-private-key))
|
||||||
|
(clear-on-card-connected)
|
||||||
|
(clear-on-card-read))))
|
||||||
|
|
||||||
|
(fx/defn on-hardwallet-keychain-keys
|
||||||
|
{:events [:multiaccounts.login.callback/get-hardwallet-keys-success]}
|
||||||
|
[{:keys [db] :as cofx} address [encryption-public-key whisper-private-key :as creds]]
|
||||||
|
(if (nil? creds)
|
||||||
|
(navigation/navigate-to-cofx cofx :keycard-login-pin nil)
|
||||||
|
(let [{:keys [photo-path name]} (get-in db [:multiaccounts/multiaccounts address])
|
||||||
|
multiaccount-data (types/clj->json {:name name
|
||||||
|
:address address
|
||||||
|
:photo-path photo-path})
|
||||||
|
account-data {:address address
|
||||||
|
:encryption-public-key encryption-public-key
|
||||||
|
:whisper-private-key whisper-private-key}]
|
||||||
|
{:db
|
||||||
|
(-> db
|
||||||
|
(assoc-in [:hardwallet :pin :status] nil)
|
||||||
|
(assoc-in [:hardwallet :pin :login] [])
|
||||||
|
(assoc-in [:hardwallet :multiaccount]
|
||||||
|
(update account-data :whisper-public-key ethereum/normalized-hex))
|
||||||
|
(assoc-in [:hardwallet :flow] nil)
|
||||||
|
(update :multiaccounts/login assoc
|
||||||
|
:password encryption-public-key
|
||||||
|
:address address
|
||||||
|
:photo-path photo-path
|
||||||
|
:name name
|
||||||
|
:save-password? true))
|
||||||
|
:hardwallet/login-with-keycard
|
||||||
|
{:multiaccount-data multiaccount-data
|
||||||
|
:password encryption-public-key
|
||||||
|
:chat-key whisper-private-key}})))
|
||||||
|
|
||||||
(fx/defn on-get-keys-error
|
(fx/defn on-get-keys-error
|
||||||
[{:keys [db] :as cofx} error]
|
[{:keys [db] :as cofx} error]
|
||||||
|
|
|
@ -0,0 +1,9 @@
|
||||||
|
(ns status-im.hardwallet.nfc)
|
||||||
|
|
||||||
|
(def is-nfc-supported? (atom nil))
|
||||||
|
|
||||||
|
(defn set-nfc-supported? [supported?]
|
||||||
|
(reset! is-nfc-supported? supported?))
|
||||||
|
|
||||||
|
(defn nfc-supported? []
|
||||||
|
@is-nfc-supported?)
|
|
@ -11,7 +11,8 @@
|
||||||
[status-im.react-native.js-dependencies :as js-dependencies]
|
[status-im.react-native.js-dependencies :as js-dependencies]
|
||||||
[re-frame.core :as re-frame]
|
[re-frame.core :as re-frame]
|
||||||
[status-im.ethereum.json-rpc :as json-rpc]
|
[status-im.ethereum.json-rpc :as json-rpc]
|
||||||
[status-im.utils.keychain.core :as keychain]))
|
[status-im.utils.keychain.core :as keychain]
|
||||||
|
[taoensso.timbre :as log]))
|
||||||
|
|
||||||
;; currently, for android, react-native-touch-id
|
;; currently, for android, react-native-touch-id
|
||||||
;; is not returning supported biometric type
|
;; is not returning supported biometric type
|
||||||
|
@ -81,6 +82,7 @@
|
||||||
(.catch #(callback nil))))
|
(.catch #(callback nil))))
|
||||||
|
|
||||||
(defn get-supported [callback]
|
(defn get-supported [callback]
|
||||||
|
(log/debug "[biometric] get-supported")
|
||||||
(cond platform/ios? (do-get-supported callback)
|
(cond platform/ios? (do-get-supported callback)
|
||||||
platform/android? (if android-device-blacklisted?
|
platform/android? (if android-device-blacklisted?
|
||||||
(callback nil)
|
(callback nil)
|
||||||
|
@ -91,6 +93,7 @@
|
||||||
([cb]
|
([cb]
|
||||||
(authenticate-fx cb nil))
|
(authenticate-fx cb nil))
|
||||||
([cb {:keys [reason ios-fallback-label]}]
|
([cb {:keys [reason ios-fallback-label]}]
|
||||||
|
(log/debug "[biometric] authenticate-fx")
|
||||||
(-> (.authenticate js-dependencies/touchid reason (authenticate-options ios-fallback-label))
|
(-> (.authenticate js-dependencies/touchid reason (authenticate-options ios-fallback-label))
|
||||||
(.then #(cb success-result))
|
(.then #(cb success-result))
|
||||||
(.catch #(cb (generate-error-result %))))))
|
(.catch #(cb (generate-error-result %))))))
|
||||||
|
@ -115,9 +118,14 @@
|
||||||
(authenticate-fx #(cb %) options)))
|
(authenticate-fx #(cb %) options)))
|
||||||
|
|
||||||
(fx/defn update-biometric [{db :db :as cofx} biometric-auth?]
|
(fx/defn update-biometric [{db :db :as cofx} biometric-auth?]
|
||||||
(let [address (get-in db [:multiaccount :address])]
|
(let [address (or (get-in db [:multiaccount :address])
|
||||||
|
(get-in db [:multiaccounts/login :address]))]
|
||||||
(fx/merge cofx
|
(fx/merge cofx
|
||||||
(keychain/save-auth-method address (if biometric-auth? "biometric" "none"))
|
(keychain/save-auth-method
|
||||||
|
address
|
||||||
|
(if biometric-auth?
|
||||||
|
keychain/auth-method-biometric
|
||||||
|
keychain/auth-method-none))
|
||||||
#(when-not biometric-auth?
|
#(when-not biometric-auth?
|
||||||
{:keychain/clear-user-password address}))))
|
{:keychain/clear-user-password address}))))
|
||||||
|
|
||||||
|
@ -143,9 +151,10 @@
|
||||||
|
|
||||||
(fx/defn biometric-init-done
|
(fx/defn biometric-init-done
|
||||||
{:events [:biometric-init-done]}
|
{:events [:biometric-init-done]}
|
||||||
[{:keys [db] :as cofx} {:keys [bioauth-success bioauth-message bioauth-code]}]
|
[cofx {:keys [bioauth-success bioauth-message bioauth-code]}]
|
||||||
(if bioauth-success
|
(if bioauth-success
|
||||||
(if (= "password" (get-in cofx [:db :auth-method]))
|
(if (= keychain/auth-method-password
|
||||||
|
(get-in cofx [:db :auth-method]))
|
||||||
(update-biometric cofx true)
|
(update-biometric cofx true)
|
||||||
(popover/show-popover cofx {:view :enable-biometric}))
|
(popover/show-popover cofx {:view :enable-biometric}))
|
||||||
(show-message cofx bioauth-message bioauth-code)))
|
(show-message cofx bioauth-message bioauth-code)))
|
||||||
|
@ -157,4 +166,33 @@
|
||||||
cofx
|
cofx
|
||||||
#(re-frame/dispatch [:biometric-auth-done %])
|
#(re-frame/dispatch [:biometric-auth-done %])
|
||||||
{:reason (i18n/label :t/biometric-auth-reason-login)
|
{:reason (i18n/label :t/biometric-auth-reason-login)
|
||||||
:ios-fallback-label (i18n/label :t/biometric-auth-login-ios-fallback-label)}))
|
:ios-fallback-label (i18n/label :t/biometric-auth-login-ios-fallback-label)}))
|
||||||
|
|
||||||
|
(fx/defn enable
|
||||||
|
{:events [:biometric/enable]}
|
||||||
|
[cofx]
|
||||||
|
(fx/merge
|
||||||
|
cofx
|
||||||
|
(popover/hide-popover)
|
||||||
|
(authenticate #(re-frame/dispatch [:biometric/setup-done %]) {})))
|
||||||
|
|
||||||
|
(fx/defn disable
|
||||||
|
{:events [:biometric/disable]}
|
||||||
|
[{:keys [db] :as cofx}]
|
||||||
|
(fx/merge
|
||||||
|
cofx
|
||||||
|
{:db (-> db
|
||||||
|
(assoc :auth-method keychain/auth-method-none)
|
||||||
|
(assoc-in [:multiaccounts/login :save-password?] false))}
|
||||||
|
(popover/hide-popover)))
|
||||||
|
|
||||||
|
(fx/defn setup-done
|
||||||
|
{:events [:biometric/setup-done]}
|
||||||
|
[{:keys [db] :as cofx} {:keys [bioauth-success bioauth-message bioauth-code]}]
|
||||||
|
(log/debug "[biometric] setup-done"
|
||||||
|
"bioauth-success" bioauth-success
|
||||||
|
"bioauth-message" bioauth-message
|
||||||
|
"bioauth-code" bioauth-code)
|
||||||
|
(if bioauth-success
|
||||||
|
{:db (assoc db :auth-method keychain/auth-method-biometric-prepare)}
|
||||||
|
(show-message cofx bioauth-message bioauth-code)))
|
||||||
|
|
|
@ -5,6 +5,7 @@
|
||||||
[status-im.ethereum.core :as ethereum]
|
[status-im.ethereum.core :as ethereum]
|
||||||
[taoensso.timbre :as log]
|
[taoensso.timbre :as log]
|
||||||
[status-im.i18n :as i18n]
|
[status-im.i18n :as i18n]
|
||||||
|
[status-im.hardwallet.nfc :as nfc]
|
||||||
[status-im.multiaccounts.db :as db]
|
[status-im.multiaccounts.db :as db]
|
||||||
[status-im.native-module.core :as status]
|
[status-im.native-module.core :as status]
|
||||||
[status-im.node.core :as node]
|
[status-im.node.core :as node]
|
||||||
|
@ -30,14 +31,16 @@
|
||||||
(defn decrement-step [step]
|
(defn decrement-step [step]
|
||||||
(let [inverted (map-invert step-kw-to-num)]
|
(let [inverted (map-invert step-kw-to-num)]
|
||||||
(if (and (= step :create-code)
|
(if (and (= step :create-code)
|
||||||
(not platform/android?))
|
(or (not platform/android?)
|
||||||
|
(not (nfc/nfc-supported?))))
|
||||||
:choose-key
|
:choose-key
|
||||||
(inverted (dec (step-kw-to-num step))))))
|
(inverted (dec (step-kw-to-num step))))))
|
||||||
|
|
||||||
(defn inc-step [step]
|
(defn inc-step [step]
|
||||||
(let [inverted (map-invert step-kw-to-num)]
|
(let [inverted (map-invert step-kw-to-num)]
|
||||||
(if (and (= step :choose-key)
|
(if (and (= step :choose-key)
|
||||||
(not platform/android?))
|
(or (not platform/android?)
|
||||||
|
(not (nfc/nfc-supported?))))
|
||||||
:create-code
|
:create-code
|
||||||
(inverted (inc (step-kw-to-num step))))))
|
(inverted (inc (step-kw-to-num step))))))
|
||||||
|
|
||||||
|
@ -138,7 +141,9 @@
|
||||||
(fx/defn intro-step-forward
|
(fx/defn intro-step-forward
|
||||||
{:events [:intro-wizard/step-forward-pressed]}
|
{:events [:intro-wizard/step-forward-pressed]}
|
||||||
[{:keys [db] :as cofx} {:keys [skip?] :as opts}]
|
[{:keys [db] :as cofx} {:keys [skip?] :as opts}]
|
||||||
(let [{:keys [step selected-storage-type processing? weak-password?]} (:intro-wizard db)]
|
(let [{:keys [step selected-storage-type processing? weak-password?]} (:intro-wizard db)]
|
||||||
|
(log/debug "[multiaccount.create] intro-step-forward"
|
||||||
|
"step" step)
|
||||||
(cond (confirm-failure? db)
|
(cond (confirm-failure? db)
|
||||||
(on-confirm-failure cofx)
|
(on-confirm-failure cofx)
|
||||||
|
|
||||||
|
|
|
@ -31,7 +31,9 @@
|
||||||
[status-im.ui.screens.db :refer [app-db]]
|
[status-im.ui.screens.db :refer [app-db]]
|
||||||
[status-im.multiaccounts.biometric.core :as biometric]
|
[status-im.multiaccounts.biometric.core :as biometric]
|
||||||
[status-im.utils.identicon :as identicon]
|
[status-im.utils.identicon :as identicon]
|
||||||
[status-im.ethereum.eip55 :as eip55]))
|
[status-im.ethereum.eip55 :as eip55]
|
||||||
|
[status-im.popover.core :as popover]
|
||||||
|
[status-im.hardwallet.nfc :as nfc]))
|
||||||
|
|
||||||
(def rpc-endpoint "https://goerli.infura.io/v3/f315575765b14720b32382a61a89341a")
|
(def rpc-endpoint "https://goerli.infura.io/v3/f315575765b14720b32382a61a89341a")
|
||||||
(def contract-address "0xfbf4c8e2B41fAfF8c616a0E49Fb4365a5355Ffaf")
|
(def contract-address "0xfbf4c8e2B41fAfF8c616a0E49Fb4365a5355Ffaf")
|
||||||
|
@ -171,19 +173,24 @@
|
||||||
(when-not platform/desktop?
|
(when-not platform/desktop?
|
||||||
(initialize-wallet)))))
|
(initialize-wallet)))))
|
||||||
|
|
||||||
|
(defn get-new-auth-method [auth-method save-password?]
|
||||||
|
(if save-password?
|
||||||
|
(when-not (or (= keychain/auth-method-biometric auth-method)
|
||||||
|
(= keychain/auth-method-password auth-method))
|
||||||
|
(if (= auth-method keychain/auth-method-biometric-prepare)
|
||||||
|
keychain/auth-method-biometric
|
||||||
|
keychain/auth-method-password))
|
||||||
|
(when (and auth-method
|
||||||
|
(not= auth-method keychain/auth-method-none))
|
||||||
|
keychain/auth-method-none)))
|
||||||
|
|
||||||
(fx/defn login-only-events
|
(fx/defn login-only-events
|
||||||
[{:keys [db] :as cofx} address password save-password?]
|
[{:keys [db] :as cofx} address password save-password?]
|
||||||
(let [auth-method (:auth-method db)
|
(let [auth-method (:auth-method db)
|
||||||
new-auth-method (if save-password?
|
new-auth-method (get-new-auth-method auth-method save-password?)]
|
||||||
(when-not (or (= "biometric" auth-method)
|
(log/debug "[login] login-only-events"
|
||||||
(= "password" auth-method))
|
"auth-method" auth-method
|
||||||
(if (= auth-method "biometric-prepare")
|
"new-auth-method" new-auth-method)
|
||||||
"biometric"
|
|
||||||
"password"))
|
|
||||||
(when (and auth-method
|
|
||||||
(not= auth-method
|
|
||||||
"none"))
|
|
||||||
"none"))]
|
|
||||||
(fx/merge cofx
|
(fx/merge cofx
|
||||||
{:db (assoc db :chats/loading? true)
|
{:db (assoc db :chats/loading? true)
|
||||||
::json-rpc/call
|
::json-rpc/call
|
||||||
|
@ -202,8 +209,7 @@
|
||||||
:on-success #(re-frame/dispatch [::get-config-callback %])}]}
|
:on-success #(re-frame/dispatch [::get-config-callback %])}]}
|
||||||
(when save-password?
|
(when save-password?
|
||||||
(keychain/save-user-password address password))
|
(keychain/save-user-password address password))
|
||||||
(when new-auth-method
|
(keychain/save-auth-method address (or new-auth-method auth-method))
|
||||||
(keychain/save-auth-method address new-auth-method))
|
|
||||||
(navigation/navigate-to-cofx :home nil)
|
(navigation/navigate-to-cofx :home nil)
|
||||||
(when platform/desktop?
|
(when platform/desktop?
|
||||||
(chat-model/update-dock-badge-label)))))
|
(chat-model/update-dock-badge-label)))))
|
||||||
|
@ -291,55 +297,106 @@
|
||||||
|
|
||||||
(fx/defn open-login
|
(fx/defn open-login
|
||||||
[{:keys [db] :as cofx} address photo-path name public-key]
|
[{:keys [db] :as cofx} address photo-path name public-key]
|
||||||
(let [keycard-multiaccount? (boolean (get-in db [:multiaccounts/multiaccounts address :keycard-pairing]))]
|
(fx/merge cofx
|
||||||
(fx/merge cofx
|
{:db (-> db
|
||||||
{:db (-> db
|
(update :multiaccounts/login assoc
|
||||||
(update :multiaccounts/login assoc
|
:public-key public-key
|
||||||
:public-key public-key
|
:address address
|
||||||
:address address
|
:photo-path photo-path
|
||||||
:photo-path photo-path
|
:name name)
|
||||||
:name name)
|
(assoc :profile/photo-added? (= (identicon/identicon public-key) photo-path))
|
||||||
(assoc :profile/photo-added? (= (identicon/identicon public-key) photo-path))
|
(update :multiaccounts/login dissoc
|
||||||
(update :multiaccounts/login dissoc
|
:error
|
||||||
:error
|
:password))}
|
||||||
:password))}
|
(keychain/get-auth-method address)))
|
||||||
(if keycard-multiaccount?
|
|
||||||
(open-keycard-login)
|
|
||||||
(keychain/get-auth-method address)))))
|
|
||||||
|
|
||||||
(fx/defn open-login-callback
|
(fx/defn open-login-callback
|
||||||
{:events [:multiaccounts.login.callback/get-user-password-success]}
|
{:events [:multiaccounts.login.callback/get-user-password-success]}
|
||||||
[{:keys [db] :as cofx} password]
|
[{:keys [db] :as cofx} password]
|
||||||
(if password
|
(let [address (get-in db [:multiaccounts/login :address])
|
||||||
(fx/merge cofx
|
keycard-account? (boolean (get-in db [:multiaccounts/multiaccounts
|
||||||
{:db (update-in db [:multiaccounts/login] assoc :password password :save-password? true)}
|
address
|
||||||
(navigation/navigate-to-cofx :progress nil)
|
:keycard-pairing]))]
|
||||||
login)
|
(if password
|
||||||
(navigation/navigate-to-cofx cofx :login nil)))
|
(fx/merge
|
||||||
|
cofx
|
||||||
|
{:db (update-in db [:multiaccounts/login] assoc
|
||||||
|
:password password
|
||||||
|
:save-password? true)}
|
||||||
|
(navigation/navigate-to-cofx :progress nil)
|
||||||
|
login)
|
||||||
|
(navigation/navigate-to-cofx
|
||||||
|
cofx
|
||||||
|
(if keycard-account? :keycard-login-pin :login)
|
||||||
|
nil))))
|
||||||
|
|
||||||
|
(fx/defn get-credentials
|
||||||
|
[{:keys [db] :as cofx} address]
|
||||||
|
(let [keycard-multiaccount? (boolean (get-in db [:multiaccounts/multiaccounts address :keycard-pairing]))]
|
||||||
|
(log/debug "[login] get-credentials"
|
||||||
|
"keycard-multiacc?" keycard-multiaccount?)
|
||||||
|
(if keycard-multiaccount?
|
||||||
|
(keychain/get-hardwallet-keys cofx address)
|
||||||
|
(keychain/get-user-password cofx address))))
|
||||||
|
|
||||||
(fx/defn get-auth-method-success
|
(fx/defn get-auth-method-success
|
||||||
"Auth method: nil - not supported, \"none\" - not selected, \"password\", \"biometric\", \"biometric-prepare\""
|
"Auth method: nil - not supported, \"none\" - not selected, \"password\", \"biometric\", \"biometric-prepare\""
|
||||||
{:events [:multiaccounts.login/get-auth-method-success]}
|
{:events [:multiaccounts.login/get-auth-method-success]}
|
||||||
[{:keys [db] :as cofx} auth-method]
|
[{:keys [db] :as cofx} auth-method]
|
||||||
(let [address (get-in db [:multiaccounts/login :address])]
|
(let [address (get-in db [:multiaccounts/login :address])
|
||||||
|
keycard-multiaccount? (boolean (get-in db [:multiaccounts/multiaccounts address :keycard-pairing]))]
|
||||||
|
(log/debug "[login] get-auth-method-success"
|
||||||
|
"auth-method" auth-method
|
||||||
|
"keycard-multiacc?" keycard-multiaccount?)
|
||||||
(fx/merge cofx
|
(fx/merge cofx
|
||||||
{:db (assoc db :auth-method auth-method)}
|
{:db (assoc db :auth-method auth-method)}
|
||||||
#(case auth-method
|
#(case auth-method
|
||||||
"biometric"
|
keychain/auth-method-biometric
|
||||||
(biometric/biometric-auth %)
|
(biometric/biometric-auth %)
|
||||||
"password"
|
keychain/auth-method-password
|
||||||
(keychain/get-user-password % address)
|
(get-credentials % address)
|
||||||
|
|
||||||
;;nil or "none" or "biometric-prepare"
|
;;nil or "none" or "biometric-prepare"
|
||||||
(open-login-callback % nil)))))
|
(if keycard-multiaccount?
|
||||||
|
(open-keycard-login %)
|
||||||
|
(open-login-callback % nil))))))
|
||||||
|
|
||||||
(fx/defn biometric-auth-done
|
(fx/defn biometric-auth-done
|
||||||
{:events [:biometric-auth-done]}
|
{:events [:biometric-auth-done]}
|
||||||
[{:keys [db] :as cofx} {:keys [bioauth-success bioauth-message bioauth-code]}]
|
[{:keys [db] :as cofx} {:keys [bioauth-success bioauth-message bioauth-code]}]
|
||||||
(let [address (get-in db [:multiaccounts/login :address])]
|
(let [address (get-in db [:multiaccounts/login :address])]
|
||||||
|
(log/debug "[biometric] biometric-auth-done"
|
||||||
|
"bioauth-success" bioauth-success
|
||||||
|
"bioauth-message" bioauth-message
|
||||||
|
"bioauth-code" bioauth-code)
|
||||||
(if bioauth-success
|
(if bioauth-success
|
||||||
(keychain/get-user-password cofx address)
|
(get-credentials cofx address)
|
||||||
(fx/merge cofx
|
(fx/merge cofx
|
||||||
{:db (assoc-in db [:multiaccounts/login :save-password?] true)}
|
{:db (assoc-in db [:multiaccounts/login :save-password?] true)}
|
||||||
(biometric/show-message bioauth-message bioauth-code)
|
(biometric/show-message bioauth-message bioauth-code)
|
||||||
(open-login-callback nil)))))
|
(open-login-callback nil)))))
|
||||||
|
|
||||||
|
(fx/defn save-password
|
||||||
|
{:events [:multiaccounts/save-password]}
|
||||||
|
[{:keys [db] :as cofx} save-password?]
|
||||||
|
(let [bioauth-supported? (boolean (get db :supported-biometric-auth))
|
||||||
|
previous-auth-method (get db :auth-method)]
|
||||||
|
(log/debug "[login] save-password"
|
||||||
|
"save-password?" save-password?
|
||||||
|
"bioauth-supported?" bioauth-supported?
|
||||||
|
"previous-auth-method" previous-auth-method)
|
||||||
|
(fx/merge
|
||||||
|
cofx
|
||||||
|
{:db (cond-> (assoc db :auth-method keychain/auth-method-none)
|
||||||
|
(or save-password?
|
||||||
|
(not bioauth-supported?)
|
||||||
|
(and (not save-password?)
|
||||||
|
bioauth-supported?
|
||||||
|
(= previous-auth-method keychain/auth-method-none)))
|
||||||
|
(assoc-in [:multiaccounts/login :save-password?] save-password?))}
|
||||||
|
(when bioauth-supported?
|
||||||
|
(if save-password?
|
||||||
|
(popover/show-popover {:view :secure-with-biometric})
|
||||||
|
(when-not (= previous-auth-method keychain/auth-method-none)
|
||||||
|
(popover/show-popover {:view :disable-password-saving})))))))
|
||||||
|
|
||||||
|
|
|
@ -22,7 +22,7 @@
|
||||||
(fx/defn logout
|
(fx/defn logout
|
||||||
{:events [:logout]}
|
{:events [:logout]}
|
||||||
[cofx]
|
[cofx]
|
||||||
(logout-method cofx "none"))
|
(logout-method cofx keychain/auth-method-none))
|
||||||
|
|
||||||
(fx/defn show-logout-confirmation [_]
|
(fx/defn show-logout-confirmation [_]
|
||||||
{:ui/show-confirmation
|
{:ui/show-confirmation
|
||||||
|
@ -34,9 +34,9 @@
|
||||||
|
|
||||||
(fx/defn biometric-logout
|
(fx/defn biometric-logout
|
||||||
{:events [:biometric-logout]}
|
{:events [:biometric-logout]}
|
||||||
[{:keys [db] :as cofx}]
|
[cofx]
|
||||||
(fx/merge cofx
|
(fx/merge cofx
|
||||||
(logout-method "biometric-prepare")
|
(logout-method keychain/auth-method-biometric-prepare)
|
||||||
(fn [{:keys [db]}]
|
(fn [{:keys [db]}]
|
||||||
{:db (assoc-in db [:multiaccounts/login :save-password?] true)})))
|
{:db (assoc-in db [:multiaccounts/login :save-password?] true)})))
|
||||||
|
|
||||||
|
|
|
@ -4,9 +4,9 @@
|
||||||
[status-im.constants :as constants]
|
[status-im.constants :as constants]
|
||||||
[status-im.ethereum.core :as ethereum]
|
[status-im.ethereum.core :as ethereum]
|
||||||
[status-im.ethereum.mnemonic :as mnemonic]
|
[status-im.ethereum.mnemonic :as mnemonic]
|
||||||
|
[status-im.hardwallet.nfc :as nfc]
|
||||||
[status-im.i18n :as i18n]
|
[status-im.i18n :as i18n]
|
||||||
[status-im.multiaccounts.create.core :as multiaccounts.create]
|
[status-im.multiaccounts.create.core :as multiaccounts.create]
|
||||||
[status-im.multiaccounts.db :as db]
|
|
||||||
[status-im.native-module.core :as status]
|
[status-im.native-module.core :as status]
|
||||||
[status-im.popover.core :as popover]
|
[status-im.popover.core :as popover]
|
||||||
[status-im.ui.screens.navigation :as navigation]
|
[status-im.ui.screens.navigation :as navigation]
|
||||||
|
@ -14,8 +14,7 @@
|
||||||
[status-im.utils.security :as security]
|
[status-im.utils.security :as security]
|
||||||
[status-im.utils.types :as types]
|
[status-im.utils.types :as types]
|
||||||
[status-im.utils.platform :as platform]
|
[status-im.utils.platform :as platform]
|
||||||
[status-im.utils.utils :as utils]
|
[status-im.utils.utils :as utils]))
|
||||||
[status-im.ethereum.eip55 :as eip55]))
|
|
||||||
|
|
||||||
(defn existing-account?
|
(defn existing-account?
|
||||||
[root-key multiaccounts]
|
[root-key multiaccounts]
|
||||||
|
@ -225,7 +224,8 @@
|
||||||
assoc :step :select-key-storage
|
assoc :step :select-key-storage
|
||||||
:forward-action :multiaccounts.recover/select-storage-next-pressed
|
:forward-action :multiaccounts.recover/select-storage-next-pressed
|
||||||
:selected-storage-type :default)}
|
:selected-storage-type :default)}
|
||||||
(if platform/android?
|
(if (and platform/android?
|
||||||
|
(nfc/nfc-supported?))
|
||||||
(navigation/navigate-to-cofx :recover-multiaccount-select-storage nil)
|
(navigation/navigate-to-cofx :recover-multiaccount-select-storage nil)
|
||||||
(select-storage-next-pressed))))
|
(select-storage-next-pressed))))
|
||||||
|
|
||||||
|
|
|
@ -165,8 +165,7 @@
|
||||||
|
|
||||||
(defn login-with-keycard
|
(defn login-with-keycard
|
||||||
[{:keys [multiaccount-data password chat-key]}]
|
[{:keys [multiaccount-data password chat-key]}]
|
||||||
(log/debug "[native-module] login-with-keycard"
|
(log/debug "[native-module] login-with-keycard")
|
||||||
"password" password)
|
|
||||||
(clear-web-data)
|
(clear-web-data)
|
||||||
(.loginWithKeycard (status) multiaccount-data password chat-key))
|
(.loginWithKeycard (status) multiaccount-data password chat-key))
|
||||||
|
|
||||||
|
|
|
@ -14,6 +14,8 @@
|
||||||
|
|
||||||
(fx/defn status-node-started
|
(fx/defn status-node-started
|
||||||
[{db :db :as cofx} {:keys [error]}]
|
[{db :db :as cofx} {:keys [error]}]
|
||||||
|
(log/debug "[signals] status-node-started"
|
||||||
|
"error" error)
|
||||||
(if error
|
(if error
|
||||||
{:db (-> db
|
{:db (-> db
|
||||||
(update :multiaccounts/login dissoc :processing)
|
(update :multiaccounts/login dissoc :processing)
|
||||||
|
|
|
@ -8,17 +8,73 @@
|
||||||
[status-im.ui.components.icons.vector-icons :as icons]
|
[status-im.ui.components.icons.vector-icons :as icons]
|
||||||
[status-im.i18n :as i18n]))
|
[status-im.i18n :as i18n]))
|
||||||
|
|
||||||
(views/defview enable-biometric-popover []
|
(defn get-supported-biometric-auth []
|
||||||
(views/letsubs [supported-biometric-auth [:supported-biometric-auth]]
|
@(re-frame/subscribe [:supported-biometric-auth]))
|
||||||
(let [bio-type-label (biometric/get-label supported-biometric-auth)]
|
|
||||||
[react/view {:padding 24 :align-items :center}
|
(defn get-bio-type-label []
|
||||||
[react/view {:margin-bottom 16 :width 32 :height 32 :background-color colors/blue-light
|
(biometric/get-label (get-supported-biometric-auth)))
|
||||||
:border-radius 16 :align-items :center :justify-content :center}
|
|
||||||
[icons/icon (if (= supported-biometric-auth :FaceID) :faceid :print)]]
|
(defn biometric-popover
|
||||||
[react/text {:style {:typography :title-bold}} (str (i18n/label :t/enable) " " bio-type-label)]
|
[{:keys [title-label description-label description-text
|
||||||
[react/text {:style {:margin-bottom 25 :margin-top 10 :text-align :center}}
|
ok-button-label cancel-button-label on-cancel on-confirm]}]
|
||||||
(i18n/label :t/to-enable-biometric {:bio-type-label bio-type-label})]
|
(let [supported-biometric-auth (get-supported-biometric-auth)
|
||||||
[button/button {:label (i18n/label :t/ok-save-pass) :style {:margin-bottom 16}
|
bio-type-label (get-bio-type-label)]
|
||||||
:on-press #(re-frame/dispatch [:biometric-logout])}]
|
[react/view {:padding 24
|
||||||
[button/button {:label :t/cancel :type :secondary
|
:align-items :center}
|
||||||
:on-press #(re-frame/dispatch [:hide-popover])}]])))
|
[react/view {:margin-bottom 16
|
||||||
|
:width 32
|
||||||
|
:height 32
|
||||||
|
:background-color colors/blue-light
|
||||||
|
:border-radius 16
|
||||||
|
:align-items :center
|
||||||
|
:justify-content :center}
|
||||||
|
[icons/icon (if (= supported-biometric-auth :FaceID) :faceid :print)]]
|
||||||
|
|
||||||
|
[react/text {:style {:typography :title-bold}}
|
||||||
|
(str (i18n/label title-label {:bio-type-label bio-type-label}))]
|
||||||
|
(vec
|
||||||
|
(concat
|
||||||
|
[react/nested-text {:style {:margin-bottom 25
|
||||||
|
:margin-top 10
|
||||||
|
:text-align :center}}]
|
||||||
|
(if description-label
|
||||||
|
[(i18n/label description-label {:bio-type-label bio-type-label})]
|
||||||
|
description-text)))
|
||||||
|
[button/button {:label (i18n/label ok-button-label
|
||||||
|
{:bio-type-label bio-type-label})
|
||||||
|
:style {:margin-bottom 16}
|
||||||
|
:on-press #(re-frame/dispatch [on-confirm])}]
|
||||||
|
[button/button {:label (or cancel-button-label :t/cancel)
|
||||||
|
:type :secondary
|
||||||
|
:on-press #(re-frame/dispatch [(or on-cancel :hide-popover)])}]]))
|
||||||
|
|
||||||
|
(defn disable-password-saving-popover []
|
||||||
|
(let [bio-label-type (get-bio-type-label)]
|
||||||
|
[biometric-popover
|
||||||
|
{:title-label :t/biometric-disable-password-title
|
||||||
|
:ok-button-label :t/continue
|
||||||
|
:on-confirm :biometric/disable
|
||||||
|
|
||||||
|
:description-text
|
||||||
|
[[{:style {:color colors/gray}}
|
||||||
|
(i18n/label :t/biometric-disable-password-description)]
|
||||||
|
[{} (i18n/label :t/biometric-disable-bioauth
|
||||||
|
{:bio-type-label bio-label-type})]]}]))
|
||||||
|
|
||||||
|
(defn enable-biometric-popover []
|
||||||
|
[biometric-popover
|
||||||
|
{:title-label :t/enable
|
||||||
|
:description-label :t/to-enable-biometric
|
||||||
|
:ok-button-label :t/biometric-enable-button
|
||||||
|
:on-confirm :biometric-logout}])
|
||||||
|
|
||||||
|
(defn secure-with-biometric-popover []
|
||||||
|
(let [bio-label-type (get-bio-type-label)]
|
||||||
|
[biometric-popover
|
||||||
|
{:title-label :t/biometric-secure-with
|
||||||
|
:ok-button-label :t/biometric-enable-button
|
||||||
|
:on-confirm :biometric/enable
|
||||||
|
|
||||||
|
:description-text
|
||||||
|
[[{:style {:color colors/gray}} (i18n/label :t/biometric-enable)]
|
||||||
|
[{} (i18n/label :t/biometric-sign-in {:bio-type-label bio-label-type})]]}]))
|
||||||
|
|
|
@ -54,8 +54,7 @@
|
||||||
:registry {}
|
:registry {}
|
||||||
:stickers/packs-owned #{}
|
:stickers/packs-owned #{}
|
||||||
:stickers/packs-pending #{}
|
:stickers/packs-pending #{}
|
||||||
:hardwallet {:nfc-supported? false
|
:hardwallet {:nfc-enabled? false
|
||||||
:nfc-enabled? false
|
|
||||||
:pin {:original []
|
:pin {:original []
|
||||||
:confirmation []
|
:confirmation []
|
||||||
:current []
|
:current []
|
||||||
|
|
|
@ -7,8 +7,9 @@
|
||||||
[status-im.ui.components.react :as react]
|
[status-im.ui.components.react :as react]
|
||||||
[status-im.ui.screens.hardwallet.pin.styles :as styles]
|
[status-im.ui.screens.hardwallet.pin.styles :as styles]
|
||||||
[status-im.ui.components.toolbar.view :as toolbar]
|
[status-im.ui.components.toolbar.view :as toolbar]
|
||||||
[status-im.ui.components.toolbar.actions :as actions]
|
[status-im.ui.components.toolbar.actions :as toolbar.actions]
|
||||||
[status-im.ui.components.toolbar.actions :as toolbar.actions]))
|
[status-im.ui.components.checkbox.view :as checkbox]
|
||||||
|
[status-im.utils.platform :as platform]))
|
||||||
|
|
||||||
(defn numpad-button [n step enabled? small-screen?]
|
(defn numpad-button [n step enabled? small-screen?]
|
||||||
[react/touchable-highlight
|
[react/touchable-highlight
|
||||||
|
@ -68,8 +69,21 @@
|
||||||
(repeat (- 12 (count puk))
|
(repeat (- 12 (count puk))
|
||||||
nil))))])
|
nil))))])
|
||||||
|
|
||||||
(defn pin-view [{:keys [pin title-label description-label step status error-label
|
(defn save-password []
|
||||||
retry-counter small-screen?]}]
|
(let [{:keys [save-password?]} @(re-frame/subscribe [:multiaccounts/login])
|
||||||
|
auth-method @(re-frame/subscribe [:auth-method])]
|
||||||
|
(when-not (and platform/android? (not auth-method))
|
||||||
|
[react/view
|
||||||
|
{:style {:flex-direction :row}}
|
||||||
|
[checkbox/checkbox
|
||||||
|
{:checked? save-password?
|
||||||
|
:style {:margin-right 10}
|
||||||
|
:on-value-change #(re-frame/dispatch [:multiaccounts/save-password %])}]
|
||||||
|
[react/text (i18n/label :t/hardwallet-dont-ask-card)]])))
|
||||||
|
|
||||||
|
(defn pin-view
|
||||||
|
[{:keys [pin title-label description-label step status error-label
|
||||||
|
retry-counter small-screen? save-password-checkbox?]}]
|
||||||
(let [enabled? (not= status :verifying)]
|
(let [enabled? (not= status :verifying)]
|
||||||
[react/scroll-view
|
[react/scroll-view
|
||||||
[react/view styles/pin-container
|
[react/view styles/pin-container
|
||||||
|
@ -81,8 +95,7 @@
|
||||||
[react/text {:style styles/create-pin-text
|
[react/text {:style styles/create-pin-text
|
||||||
:number-of-lines 2}
|
:number-of-lines 2}
|
||||||
(i18n/label description-label)])
|
(i18n/label description-label)])
|
||||||
[react/view {:margin-top 40
|
[react/view {:flex 1}
|
||||||
:height (if small-screen? 18 22)}
|
|
||||||
(case status
|
(case status
|
||||||
:verifying [react/view styles/waiting-indicator-container
|
:verifying [react/view styles/waiting-indicator-container
|
||||||
[react/activity-indicator {:animating true
|
[react/activity-indicator {:animating true
|
||||||
|
@ -94,6 +107,8 @@
|
||||||
[react/view {:margin-top (if (= step :puk) 24 8)}
|
[react/view {:margin-top (if (= step :puk) 24 8)}
|
||||||
[react/text {:style {:text-align :center}}
|
[react/text {:style {:text-align :center}}
|
||||||
(i18n/label :t/pin-retries-left {:number retry-counter})]]))]
|
(i18n/label :t/pin-retries-left {:number retry-counter})]]))]
|
||||||
|
(when save-password-checkbox?
|
||||||
|
[save-password])
|
||||||
(if (= step :puk)
|
(if (= step :puk)
|
||||||
[puk-indicators pin status]
|
[puk-indicators pin status]
|
||||||
[pin-indicators pin status nil])
|
[pin-indicators pin status nil])
|
||||||
|
|
|
@ -4,4 +4,4 @@
|
||||||
(def container
|
(def container
|
||||||
{:flex 1
|
{:flex 1
|
||||||
:justify-content :space-between
|
:justify-content :space-between
|
||||||
:background-color colors/white})
|
:background-color colors/white})
|
||||||
|
|
|
@ -2,6 +2,7 @@
|
||||||
(:require-macros [status-im.utils.views :refer [defview letsubs]])
|
(:require-macros [status-im.utils.views :refer [defview letsubs]])
|
||||||
(:require [status-im.multiaccounts.core :as multiaccounts]
|
(:require [status-im.multiaccounts.core :as multiaccounts]
|
||||||
[status-im.ui.components.react :as react]
|
[status-im.ui.components.react :as react]
|
||||||
|
[status-im.ui.components.checkbox.view :as checkbox]
|
||||||
[status-im.ui.components.toolbar.view :as toolbar]
|
[status-im.ui.components.toolbar.view :as toolbar]
|
||||||
[status-im.ui.components.toolbar.actions :as actions]
|
[status-im.ui.components.toolbar.actions :as actions]
|
||||||
[status-im.ui.screens.keycard.styles :as styles]
|
[status-im.ui.screens.keycard.styles :as styles]
|
||||||
|
@ -370,8 +371,7 @@
|
||||||
retry-counter [:hardwallet/retry-counter]]
|
retry-counter [:hardwallet/retry-counter]]
|
||||||
[react/view styles/container
|
[react/view styles/container
|
||||||
[toolbar/toolbar
|
[toolbar/toolbar
|
||||||
{:transparent? true
|
{:transparent? true}
|
||||||
:style {:margin-top 32}}
|
|
||||||
(when multiple-multiaccounts?
|
(when multiple-multiaccounts?
|
||||||
[toolbar/nav-button
|
[toolbar/nav-button
|
||||||
(actions/back
|
(actions/back
|
||||||
|
@ -385,12 +385,11 @@
|
||||||
[react/view {:flex 1
|
[react/view {:flex 1
|
||||||
:flex-direction :column
|
:flex-direction :column
|
||||||
:justify-content :space-between
|
:justify-content :space-between
|
||||||
:align-items :center
|
:align-items :center}
|
||||||
:margin-top (if small-screen? 28 46)}
|
|
||||||
[react/view {:flex-direction :column
|
[react/view {:flex-direction :column
|
||||||
:flex 1
|
|
||||||
:justify-content :center
|
:justify-content :center
|
||||||
:align-items :center}
|
:align-items :center
|
||||||
|
:height 140}
|
||||||
[react/view {:margin-horizontal 16
|
[react/view {:margin-horizontal 16
|
||||||
:flex-direction :column}
|
:flex-direction :column}
|
||||||
[react/view {:justify-content :center
|
[react/view {:justify-content :center
|
||||||
|
@ -431,12 +430,13 @@
|
||||||
:ellipsize-mode :middle}
|
:ellipsize-mode :middle}
|
||||||
(utils.core/truncate-str address 14 true)]]]
|
(utils.core/truncate-str address 14 true)]]]
|
||||||
[pin.views/pin-view
|
[pin.views/pin-view
|
||||||
{:pin pin
|
{:pin pin
|
||||||
:retry-counter retry-counter
|
:retry-counter retry-counter
|
||||||
:small-screen? small-screen?
|
:small-screen? small-screen?
|
||||||
:status status
|
:status status
|
||||||
:error-label error-label
|
:error-label error-label
|
||||||
:step enter-step}]
|
:step enter-step
|
||||||
|
:save-password-checkbox? true}]
|
||||||
[react/view {:margin-bottom (if small-screen? 25 32)}
|
[react/view {:margin-bottom (if small-screen? 25 32)}
|
||||||
[react/touchable-highlight
|
[react/touchable-highlight
|
||||||
{:on-press #(re-frame/dispatch [:multiaccounts.recover.ui/recover-multiaccount-button-pressed])}
|
{:on-press #(re-frame/dispatch [:multiaccounts.recover.ui/recover-multiaccount-button-pressed])}
|
||||||
|
|
|
@ -92,7 +92,7 @@
|
||||||
:margin-top 19}}
|
:margin-top 19}}
|
||||||
[checkbox/checkbox {:checked? save-password?
|
[checkbox/checkbox {:checked? save-password?
|
||||||
:style {:margin-left 3 :margin-right 10}
|
:style {:margin-left 3 :margin-right 10}
|
||||||
:on-value-change #(re-frame/dispatch [:set-in [:multiaccounts/login :save-password?] %])}]
|
:on-value-change #(re-frame/dispatch [:multiaccounts/save-password %])}]
|
||||||
[react/text (i18n/label :t/save-password)]]))]]
|
[react/text (i18n/label :t/save-password)]]))]]
|
||||||
(when processing
|
(when processing
|
||||||
[react/view styles/processing-view
|
[react/view styles/processing-view
|
||||||
|
|
|
@ -1,23 +1,13 @@
|
||||||
(ns status-im.ui.screens.multiaccounts.recover.views
|
(ns status-im.ui.screens.multiaccounts.recover.views
|
||||||
(:require-macros [status-im.utils.views :refer [defview letsubs]])
|
(:require-macros [status-im.utils.views :refer [defview]])
|
||||||
(:require [re-frame.core :as re-frame]
|
(:require [re-frame.core :as re-frame]
|
||||||
[status-im.ui.components.text-input.view :as text-input]
|
|
||||||
[status-im.ui.components.react :as react]
|
[status-im.ui.components.react :as react]
|
||||||
[status-im.ui.components.toolbar.view :as toolbar]
|
|
||||||
[status-im.multiaccounts.recover.core :as multiaccounts.recover]
|
[status-im.multiaccounts.recover.core :as multiaccounts.recover]
|
||||||
[status-im.hardwallet.core :as hardwallet]
|
[status-im.hardwallet.core :as hardwallet]
|
||||||
|
[status-im.hardwallet.nfc :as nfc]
|
||||||
[status-im.i18n :as i18n]
|
[status-im.i18n :as i18n]
|
||||||
[status-im.ui.components.styles :as components.styles]
|
|
||||||
[status-im.utils.config :as config]
|
[status-im.utils.config :as config]
|
||||||
[status-im.ui.components.common.common :as components.common]
|
|
||||||
[status-im.utils.security :as security]
|
|
||||||
[status-im.ui.components.colors :as colors]
|
[status-im.ui.components.colors :as colors]
|
||||||
[status-im.utils.gfycat.core :as gfy]
|
|
||||||
[status-im.utils.identicon :as identicon]
|
|
||||||
[status-im.ui.components.icons.vector-icons :as vector-icons]
|
|
||||||
[status-im.ui.screens.intro.views :as intro.views]
|
|
||||||
[status-im.utils.utils :as utils]
|
|
||||||
[status-im.constants :as constants]
|
|
||||||
[status-im.ui.components.list-item.views :as list-item]
|
[status-im.ui.components.list-item.views :as list-item]
|
||||||
[status-im.utils.platform :as platform]
|
[status-im.utils.platform :as platform]
|
||||||
[status-im.react-native.resources :as resources]
|
[status-im.react-native.resources :as resources]
|
||||||
|
@ -68,7 +58,8 @@
|
||||||
:icon :main-icons/text
|
:icon :main-icons/text
|
||||||
:on-press #(re-frame/dispatch [::multiaccounts.recover/enter-phrase-pressed])}]
|
:on-press #(re-frame/dispatch [::multiaccounts.recover/enter-phrase-pressed])}]
|
||||||
(when (and config/hardwallet-enabled?
|
(when (and config/hardwallet-enabled?
|
||||||
platform/android?)
|
platform/android?
|
||||||
|
(nfc/nfc-supported?))
|
||||||
[list-item/list-item
|
[list-item/list-item
|
||||||
{:theme :action
|
{:theme :action
|
||||||
:title :t/recover-with-keycard
|
:title :t/recover-with-keycard
|
||||||
|
@ -86,6 +77,9 @@
|
||||||
:style {:width 24 :height 24}}]]
|
:style {:width 24 :height 24}}]]
|
||||||
:on-press #(re-frame/dispatch [::hardwallet/recover-with-keycard-pressed])}])]])
|
:on-press #(re-frame/dispatch [::hardwallet/recover-with-keycard-pressed])}])]])
|
||||||
|
|
||||||
(def bottom-sheet
|
(defn bottom-sheet []
|
||||||
{:content bottom-sheet-view
|
{:content bottom-sheet-view
|
||||||
:content-height (if platform/android? 130 65)})
|
:content-height (if (and platform/android?
|
||||||
|
(nfc/nfc-supported?))
|
||||||
|
130
|
||||||
|
65)})
|
||||||
|
|
|
@ -106,6 +106,12 @@
|
||||||
(= :enable-biometric view)
|
(= :enable-biometric view)
|
||||||
[biometric/enable-biometric-popover]
|
[biometric/enable-biometric-popover]
|
||||||
|
|
||||||
|
(= :secure-with-biometric view)
|
||||||
|
[biometric/secure-with-biometric-popover]
|
||||||
|
|
||||||
|
(= :disable-password-saving view)
|
||||||
|
[biometric/disable-password-saving-popover]
|
||||||
|
|
||||||
:else
|
:else
|
||||||
[view])]]]]])))})))
|
[view])]]]]])))})))
|
||||||
|
|
||||||
|
|
|
@ -31,12 +31,12 @@
|
||||||
:title (str (i18n/label :t/lock-app-with) " " (biometric/get-label supported-biometric-auth))
|
:title (str (i18n/label :t/lock-app-with) " " (biometric/get-label supported-biometric-auth))
|
||||||
:container-margin-bottom 8
|
:container-margin-bottom 8
|
||||||
:accessibility-label :biometric-auth-settings-switch
|
:accessibility-label :biometric-auth-settings-switch
|
||||||
:disabled? (or (not (some? supported-biometric-auth)) keycard?)
|
:disabled? (not (some? supported-biometric-auth))
|
||||||
:accessories [[react/switch
|
:accessories [[react/switch
|
||||||
{:track-color #js {:true colors/blue :false nil}
|
{:track-color #js {:true colors/blue :false nil}
|
||||||
:value (boolean biometric-auth?)
|
:value (boolean biometric-auth?)
|
||||||
:on-value-change #(re-frame/dispatch [:multiaccounts.ui/biometric-auth-switched %])
|
:on-value-change #(re-frame/dispatch [:multiaccounts.ui/biometric-auth-switched %])
|
||||||
:disabled (or (not (some? supported-biometric-auth)) keycard?)}]]
|
:disabled (not supported-biometric-auth)}]]
|
||||||
:on-press #(re-frame/dispatch [:multiaccounts.ui/biometric-auth-switched
|
:on-press #(re-frame/dispatch [:multiaccounts.ui/biometric-auth-switched
|
||||||
((complement boolean) biometric-auth?)])}
|
((complement boolean) biometric-auth?)])}
|
||||||
;; TODO - uncomment when implemented
|
;; TODO - uncomment when implemented
|
||||||
|
|
|
@ -78,7 +78,7 @@
|
||||||
(merge home.sheet/group-chat-actions)
|
(merge home.sheet/group-chat-actions)
|
||||||
|
|
||||||
(= view :recover-sheet)
|
(= view :recover-sheet)
|
||||||
(merge recover.views/bottom-sheet))
|
(merge (recover.views/bottom-sheet)))
|
||||||
height-atom (reagent/atom (if (:content-height opts) (:content-height opts) nil))]
|
height-atom (reagent/atom (if (:content-height opts) (:content-height opts) nil))]
|
||||||
[bottom-sheet-comp opts height-atom])))
|
[bottom-sheet-comp opts height-atom])))
|
||||||
|
|
||||||
|
|
|
@ -77,7 +77,11 @@
|
||||||
#js {:authenticationType (enum-val "ACCESS_CONTROL" "BIOMETRY_ANY_OR_DEVICE_PASSCODE")})
|
#js {:authenticationType (enum-val "ACCESS_CONTROL" "BIOMETRY_ANY_OR_DEVICE_PASSCODE")})
|
||||||
(.then callback)))
|
(.then callback)))
|
||||||
|
|
||||||
|
(defn- whisper-key-name [address]
|
||||||
|
(str address "-whisper"))
|
||||||
|
|
||||||
(defn can-save-user-password? [callback]
|
(defn can-save-user-password? [callback]
|
||||||
|
(log/debug "[keychain] can-save-user-password?")
|
||||||
(cond
|
(cond
|
||||||
platform/ios?
|
platform/ios?
|
||||||
(check-conditions callback device-encrypted?)
|
(check-conditions callback device-encrypted?)
|
||||||
|
@ -91,6 +95,7 @@
|
||||||
(defn save-credentials
|
(defn save-credentials
|
||||||
"Stores the credentials for the address to the Keychain"
|
"Stores the credentials for the address to the Keychain"
|
||||||
[server username password callback]
|
[server username password callback]
|
||||||
|
(log/debug "[keychain] save-credentials")
|
||||||
(-> (.setInternetCredentials rn/keychain (string/lower-case server) username password
|
(-> (.setInternetCredentials rn/keychain (string/lower-case server) username password
|
||||||
keychain-secure-hardware keychain-restricted-availability)
|
keychain-secure-hardware keychain-restricted-availability)
|
||||||
(.then callback)))
|
(.then callback)))
|
||||||
|
@ -98,18 +103,27 @@
|
||||||
(defn get-credentials
|
(defn get-credentials
|
||||||
"Gets the credentials for a specified server from the Keychain"
|
"Gets the credentials for a specified server from the Keychain"
|
||||||
[server callback]
|
[server callback]
|
||||||
|
(log/debug "[keychain] get-credentials")
|
||||||
(if platform/mobile?
|
(if platform/mobile?
|
||||||
(-> (.getInternetCredentials rn/keychain (string/lower-case server))
|
(-> (.getInternetCredentials rn/keychain (string/lower-case server))
|
||||||
(.then callback))
|
(.then callback))
|
||||||
(callback))) ;; no-op for Desktop
|
(callback))) ;; no-op for Desktop
|
||||||
|
|
||||||
|
(def ^:const auth-method-password "password")
|
||||||
|
(def ^:const auth-method-biometric "biometric")
|
||||||
|
(def ^:const auth-method-biometric-prepare "biometric-prepare")
|
||||||
|
(def ^:const auth-method-none "none")
|
||||||
|
|
||||||
(re-frame/reg-fx
|
(re-frame/reg-fx
|
||||||
:keychain/get-auth-method
|
:keychain/get-auth-method
|
||||||
(fn [[address callback]]
|
(fn [[address callback]]
|
||||||
(can-save-user-password?
|
(can-save-user-password?
|
||||||
(fn [can-save?]
|
(fn [can-save?]
|
||||||
(if can-save?
|
(if can-save?
|
||||||
(get-credentials (str address "-auth") #(callback (if % (.-password %) "none")))
|
(get-credentials (str address "-auth")
|
||||||
|
#(callback (if %
|
||||||
|
(.-password %)
|
||||||
|
auth-method-none)))
|
||||||
(callback nil))))))
|
(callback nil))))))
|
||||||
|
|
||||||
(re-frame/reg-fx
|
(re-frame/reg-fx
|
||||||
|
@ -117,6 +131,22 @@
|
||||||
(fn [[address callback]]
|
(fn [[address callback]]
|
||||||
(get-credentials address #(if % (callback (security/mask-data (.-password %))) (callback nil)))))
|
(get-credentials address #(if % (callback (security/mask-data (.-password %))) (callback nil)))))
|
||||||
|
|
||||||
|
(re-frame/reg-fx
|
||||||
|
:keychain/get-hardwallet-keys
|
||||||
|
(fn [[address callback]]
|
||||||
|
(get-credentials
|
||||||
|
address
|
||||||
|
(fn [encryption-key-data]
|
||||||
|
(if encryption-key-data
|
||||||
|
(get-credentials
|
||||||
|
(whisper-key-name address)
|
||||||
|
(fn [whisper-key-data]
|
||||||
|
(if whisper-key-data
|
||||||
|
(callback [(.-password encryption-key-data)
|
||||||
|
(.-password whisper-key-data)])
|
||||||
|
(callback nil))))
|
||||||
|
(callback nil))))))
|
||||||
|
|
||||||
(re-frame/reg-fx
|
(re-frame/reg-fx
|
||||||
:keychain/save-user-password
|
:keychain/save-user-password
|
||||||
(fn [[address password]]
|
(fn [[address password]]
|
||||||
|
@ -134,6 +164,8 @@
|
||||||
(re-frame/reg-fx
|
(re-frame/reg-fx
|
||||||
:keychain/save-auth-method
|
:keychain/save-auth-method
|
||||||
(fn [[address method]]
|
(fn [[address method]]
|
||||||
|
(log/debug "[keychain] :keychain/save-auth-method"
|
||||||
|
"method" method)
|
||||||
(save-credentials
|
(save-credentials
|
||||||
(str address "-auth")
|
(str address "-auth")
|
||||||
address
|
address
|
||||||
|
@ -145,6 +177,24 @@
|
||||||
"The app will continue to work normally, "
|
"The app will continue to work normally, "
|
||||||
"but you will have to login again next time you launch it."))))))
|
"but you will have to login again next time you launch it."))))))
|
||||||
|
|
||||||
|
(re-frame/reg-fx
|
||||||
|
:keychain/save-hardwallet-keys
|
||||||
|
(fn [[address encryption-public-key whisper-private-key]]
|
||||||
|
(save-credentials
|
||||||
|
address
|
||||||
|
address
|
||||||
|
encryption-public-key
|
||||||
|
#(when-not %
|
||||||
|
(log/error
|
||||||
|
(str "Error while saving encryption-public-key"))))
|
||||||
|
(save-credentials
|
||||||
|
(whisper-key-name address)
|
||||||
|
address
|
||||||
|
whisper-private-key
|
||||||
|
#(when-not %
|
||||||
|
(log/error
|
||||||
|
(str "Error while saving whisper-private-key"))))))
|
||||||
|
|
||||||
(re-frame/reg-fx
|
(re-frame/reg-fx
|
||||||
:keychain/clear-user-password
|
:keychain/clear-user-password
|
||||||
(fn [address]
|
(fn [address]
|
||||||
|
@ -160,13 +210,27 @@
|
||||||
(fx/defn get-user-password
|
(fx/defn get-user-password
|
||||||
[_ address]
|
[_ address]
|
||||||
{:keychain/get-user-password
|
{:keychain/get-user-password
|
||||||
[address #(re-frame/dispatch [:multiaccounts.login.callback/get-user-password-success % address])]})
|
[address
|
||||||
|
#(re-frame/dispatch
|
||||||
|
[:multiaccounts.login.callback/get-user-password-success % address])]})
|
||||||
|
|
||||||
|
(fx/defn get-hardwallet-keys
|
||||||
|
[_ address]
|
||||||
|
{:keychain/get-hardwallet-keys
|
||||||
|
[address
|
||||||
|
#(re-frame/dispatch
|
||||||
|
[:multiaccounts.login.callback/get-hardwallet-keys-success address %])]})
|
||||||
|
|
||||||
(fx/defn save-user-password
|
(fx/defn save-user-password
|
||||||
[cofx address password]
|
[_ address password]
|
||||||
{:keychain/save-user-password [address password]})
|
{:keychain/save-user-password [address password]})
|
||||||
|
|
||||||
|
(fx/defn save-hardwallet-keys
|
||||||
|
[_ address encryption-public-key whisper-private-key]
|
||||||
|
{:keychain/save-hardwallet-keys [address
|
||||||
|
encryption-public-key
|
||||||
|
whisper-private-key]})
|
||||||
(fx/defn save-auth-method
|
(fx/defn save-auth-method
|
||||||
[{:keys [db]} address method]
|
[{:keys [db]} address method]
|
||||||
{:db (assoc db :auth-method method)
|
{:db (assoc db :auth-method method)
|
||||||
:keychain/save-auth-method [address method]})
|
:keychain/save-auth-method [address method]})
|
||||||
|
|
|
@ -68,6 +68,13 @@
|
||||||
"biometric-auth-reason-login": "Login in Status",
|
"biometric-auth-reason-login": "Login in Status",
|
||||||
"biometric-auth-reason-verify": "Verify authentication",
|
"biometric-auth-reason-verify": "Verify authentication",
|
||||||
"biometric-auth-setting-label": "Use biometric authentication",
|
"biometric-auth-setting-label": "Use biometric authentication",
|
||||||
|
"biometric-secure-with": "Secure with {{bio-type-label}}",
|
||||||
|
"biometric-sign-in": "{{bio-type-label}} sign in",
|
||||||
|
"biometric-enable": "If you don't want to use your Keycard each time to access the app, enable ",
|
||||||
|
"biometric-disable-bioauth": "disable {{bio-type-label}}",
|
||||||
|
"biometric-disable-password-title": "Disable password saving",
|
||||||
|
"biometric-disable-password-description": "If you disable this, you will also ",
|
||||||
|
"biometric-enable-button": "Enable {{bio-type-label}}",
|
||||||
"blank-keycard-text": "You can proceed with your keycard once you've generated your keys and name",
|
"blank-keycard-text": "You can proceed with your keycard once you've generated your keys and name",
|
||||||
"blank-keycard-title": "Looks like you’ve tapped \na blank keycard",
|
"blank-keycard-title": "Looks like you’ve tapped \na blank keycard",
|
||||||
"block": "Block",
|
"block": "Block",
|
||||||
|
@ -497,6 +504,7 @@
|
||||||
"group-info": "Group info",
|
"group-info": "Group info",
|
||||||
"gwei": "Gwei",
|
"gwei": "Gwei",
|
||||||
"hash": "Hash",
|
"hash": "Hash",
|
||||||
|
"hardwallet-dont-ask-card": "Don't ask for card to sign in",
|
||||||
"help": "help",
|
"help": "help",
|
||||||
"help-capitalized": "Help",
|
"help-capitalized": "Help",
|
||||||
"help-center": "Help Center",
|
"help-center": "Help Center",
|
||||||
|
|
Loading…
Reference in New Issue