diff --git a/src/status_im/events.cljs b/src/status_im/events.cljs index bdebd22c51..80a5f9a50f 100644 --- a/src/status_im/events.cljs +++ b/src/status_im/events.cljs @@ -686,7 +686,7 @@ (handlers/register-handler-fx :hardwallet.callback/check-nfc-support-success (fn [cofx [_ supported?]] - (hardwallet/set-nfc-support cofx supported?))) + (hardwallet/set-nfc-supported cofx supported?))) (handlers/register-handler-fx :hardwallet.callback/on-card-connected @@ -1592,4 +1592,4 @@ (commands.sending/send public-key request-command {:asset (name symbol) - :amount (str (money/internal->formatted amount symbol decimals))}))))) \ No newline at end of file + :amount (str (money/internal->formatted amount symbol decimals))}))))) diff --git a/src/status_im/hardwallet/core.cljs b/src/status_im/hardwallet/core.cljs index 454cdbe66d..2027697c92 100644 --- a/src/status_im/hardwallet/core.cljs +++ b/src/status_im/hardwallet/core.cljs @@ -2,20 +2,16 @@ (:require [clojure.string :as string] [re-frame.core :as re-frame] [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.recover.core :as multiaccounts.recover] [status-im.ethereum.core :as ethereum] [status-im.ethereum.mnemonic :as mnemonic] [status-im.i18n :as i18n] - [status-im.node.core :as node] [status-im.ui.screens.navigation :as navigation] [status-im.utils.config :as config] [status-im.utils.datetime :as utils.datetime] [status-im.utils.fx :as fx] [status-im.utils.platform :as platform] [status-im.utils.types :as types] - [status-im.wallet.core :as wallet] [taoensso.timbre :as log] status-im.hardwallet.fx [status-im.ui.components.react :as react] @@ -23,7 +19,9 @@ [status-im.multiaccounts.update.core :as multiaccounts.update] [status-im.ui.components.bottom-sheet.core :as bottom-sheet] [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") @@ -59,6 +57,11 @@ (:keycard-pairing (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 [{:keys [db]}] (when-not (get-in db [:hardwallet :back-button-listener]) @@ -164,10 +167,10 @@ remove-instance-uid? (assoc :keycard-instance-uid nil)) {})) -(defn hardwallet-supported? [{:keys [db]}] +(defn hardwallet-supported? [] (and config/hardwallet-enabled? platform/android? - (get-in db [:hardwallet :nfc-supported?]))) + (nfc/nfc-supported?))) (fx/defn unauthorized-operation [{:keys [db] :as cofx}] @@ -628,7 +631,7 @@ pairing (get-in db [:multiaccounts/multiaccounts multiaccount-address :keycard-pairing]) pin (string/join (get-in db [:hardwallet :pin :login]))] (when (and pairing - (not (empty? pin))) + (seq pin)) {:db (-> db (assoc-in [:hardwallet :pin :status] :verifying)) :hardwallet/get-keys {:pairing pairing @@ -723,9 +726,9 @@ (when on-card-read (dispatch-event on-card-read))))))) -(fx/defn set-nfc-support - [{:keys [db]} supported?] - {:db (assoc-in db [:hardwallet :nfc-supported?] supported?)}) +(fx/defn set-nfc-supported + [_ supported?] + {:hardwallet/set-nfc-supported supported?}) (fx/defn keycard-option-pressed {:events [:onboarding.ui/keycard-option-pressed]} @@ -1774,14 +1777,15 @@ (let [account-data (js->clj data :keywordize-keys true)] (fx/merge cofx {:db (-> db - (assoc-in [:hardwallet :multiaccount] (-> account-data - (update :address #(str "0x" %)) - (update :whisper-address #(str "0x" %)) - (update :wallet-address #(str "0x" %)) - (update :public-key #(str "0x" %)) - (update :whisper-public-key #(str "0x" %)) - (update :wallet-public-key #(str "0x" %)) - (update :instance-uid #(get-in db [:hardwallet :multiaccount :instance-uid] %)))) + (assoc-in [:hardwallet :multiaccount] + (-> account-data + (update :address ethereum/normalized-hex) + (update :whisper-address ethereum/normalized-hex) + (update :wallet-address ethereum/normalized-hex) + (update :public-key ethereum/normalized-hex) + (update :whisper-public-key ethereum/normalized-hex) + (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-whisper-public-key] (:whisper-public-key account-data)) (assoc-in [:hardwallet :application-info :key-uid] (:key-uid account-data)) @@ -1808,28 +1812,65 @@ (fx/defn on-get-keys-success [{: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) - address (str "0x" address) + (let [{:keys [address encryption-public-key whisper-private-key] :as account-data} (js->clj data :keywordize-keys true) + address (ethereum/normalized-hex address) {:keys [photo-path name]} (get-in db [:multiaccounts/multiaccounts address]) key-uid (get-in db [:hardwallet :application-info :key-uid]) - multiaccount-data (types/clj->json {:name name :address address :photo-path photo-path})] - (fx/merge cofx - {:db (-> db - (assoc-in [:hardwallet :pin :status] nil) - (assoc-in [:hardwallet :pin :login] []) - (assoc-in [:hardwallet :multiaccount] (update account-data :whisper-public-key #(str "0x" %))) - (assoc-in [:hardwallet :flow] nil) - (update :multiaccounts/login assoc - :password encryption-public-key - :address address - :photo-path photo-path - :name name)) - :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}} - (clear-on-card-connected) - (clear-on-card-read)))) + multiaccount-data (types/clj->json {:name name :address address :photo-path photo-path}) + save-keys? (get-in db [:multiaccounts/login :save-password?])] + (fx/merge + cofx + {: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)) + + :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 [{:keys [db] :as cofx} error] diff --git a/src/status_im/hardwallet/nfc.cljs b/src/status_im/hardwallet/nfc.cljs new file mode 100644 index 0000000000..2cc810a92d --- /dev/null +++ b/src/status_im/hardwallet/nfc.cljs @@ -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?) diff --git a/src/status_im/multiaccounts/biometric/core.cljs b/src/status_im/multiaccounts/biometric/core.cljs index bb08343306..edda3b5e5a 100644 --- a/src/status_im/multiaccounts/biometric/core.cljs +++ b/src/status_im/multiaccounts/biometric/core.cljs @@ -11,7 +11,8 @@ [status-im.react-native.js-dependencies :as js-dependencies] [re-frame.core :as re-frame] [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 ;; is not returning supported biometric type @@ -81,6 +82,7 @@ (.catch #(callback nil)))) (defn get-supported [callback] + (log/debug "[biometric] get-supported") (cond platform/ios? (do-get-supported callback) platform/android? (if android-device-blacklisted? (callback nil) @@ -91,6 +93,7 @@ ([cb] (authenticate-fx cb nil)) ([cb {:keys [reason ios-fallback-label]}] + (log/debug "[biometric] authenticate-fx") (-> (.authenticate js-dependencies/touchid reason (authenticate-options ios-fallback-label)) (.then #(cb success-result)) (.catch #(cb (generate-error-result %)))))) @@ -115,9 +118,14 @@ (authenticate-fx #(cb %) options))) (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 - (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? {:keychain/clear-user-password address})))) @@ -143,9 +151,10 @@ (fx/defn 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 (= "password" (get-in cofx [:db :auth-method])) + (if (= keychain/auth-method-password + (get-in cofx [:db :auth-method])) (update-biometric cofx true) (popover/show-popover cofx {:view :enable-biometric})) (show-message cofx bioauth-message bioauth-code))) @@ -157,4 +166,33 @@ cofx #(re-frame/dispatch [:biometric-auth-done %]) {:reason (i18n/label :t/biometric-auth-reason-login) - :ios-fallback-label (i18n/label :t/biometric-auth-login-ios-fallback-label)})) \ No newline at end of file + :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))) diff --git a/src/status_im/multiaccounts/create/core.cljs b/src/status_im/multiaccounts/create/core.cljs index 3aa66cdce0..583cb40990 100644 --- a/src/status_im/multiaccounts/create/core.cljs +++ b/src/status_im/multiaccounts/create/core.cljs @@ -5,6 +5,7 @@ [status-im.ethereum.core :as ethereum] [taoensso.timbre :as log] [status-im.i18n :as i18n] + [status-im.hardwallet.nfc :as nfc] [status-im.multiaccounts.db :as db] [status-im.native-module.core :as status] [status-im.node.core :as node] @@ -30,14 +31,16 @@ (defn decrement-step [step] (let [inverted (map-invert step-kw-to-num)] (if (and (= step :create-code) - (not platform/android?)) + (or (not platform/android?) + (not (nfc/nfc-supported?)))) :choose-key (inverted (dec (step-kw-to-num step)))))) (defn inc-step [step] (let [inverted (map-invert step-kw-to-num)] (if (and (= step :choose-key) - (not platform/android?)) + (or (not platform/android?) + (not (nfc/nfc-supported?)))) :create-code (inverted (inc (step-kw-to-num step)))))) @@ -138,7 +141,9 @@ (fx/defn intro-step-forward {:events [:intro-wizard/step-forward-pressed]} [{: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) (on-confirm-failure cofx) diff --git a/src/status_im/multiaccounts/login/core.cljs b/src/status_im/multiaccounts/login/core.cljs index 72cec23b68..0880eaac2a 100644 --- a/src/status_im/multiaccounts/login/core.cljs +++ b/src/status_im/multiaccounts/login/core.cljs @@ -31,7 +31,9 @@ [status-im.ui.screens.db :refer [app-db]] [status-im.multiaccounts.biometric.core :as biometric] [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 contract-address "0xfbf4c8e2B41fAfF8c616a0E49Fb4365a5355Ffaf") @@ -171,19 +173,24 @@ (when-not platform/desktop? (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 [{:keys [db] :as cofx} address password save-password?] (let [auth-method (:auth-method db) - new-auth-method (if save-password? - (when-not (or (= "biometric" auth-method) - (= "password" auth-method)) - (if (= auth-method "biometric-prepare") - "biometric" - "password")) - (when (and auth-method - (not= auth-method - "none")) - "none"))] + new-auth-method (get-new-auth-method auth-method save-password?)] + (log/debug "[login] login-only-events" + "auth-method" auth-method + "new-auth-method" new-auth-method) (fx/merge cofx {:db (assoc db :chats/loading? true) ::json-rpc/call @@ -202,8 +209,7 @@ :on-success #(re-frame/dispatch [::get-config-callback %])}]} (when save-password? (keychain/save-user-password address password)) - (when new-auth-method - (keychain/save-auth-method address new-auth-method)) + (keychain/save-auth-method address (or new-auth-method auth-method)) (navigation/navigate-to-cofx :home nil) (when platform/desktop? (chat-model/update-dock-badge-label))))) @@ -291,55 +297,106 @@ (fx/defn open-login [{: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 - {:db (-> db - (update :multiaccounts/login assoc - :public-key public-key - :address address - :photo-path photo-path - :name name) - (assoc :profile/photo-added? (= (identicon/identicon public-key) photo-path)) - (update :multiaccounts/login dissoc - :error - :password))} - (if keycard-multiaccount? - (open-keycard-login) - (keychain/get-auth-method address))))) + (fx/merge cofx + {:db (-> db + (update :multiaccounts/login assoc + :public-key public-key + :address address + :photo-path photo-path + :name name) + (assoc :profile/photo-added? (= (identicon/identicon public-key) photo-path)) + (update :multiaccounts/login dissoc + :error + :password))} + (keychain/get-auth-method address))) (fx/defn open-login-callback {:events [:multiaccounts.login.callback/get-user-password-success]} [{:keys [db] :as cofx} password] - (if password - (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 :login nil))) + (let [address (get-in db [:multiaccounts/login :address]) + keycard-account? (boolean (get-in db [:multiaccounts/multiaccounts + address + :keycard-pairing]))] + (if password + (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 "Auth method: nil - not supported, \"none\" - not selected, \"password\", \"biometric\", \"biometric-prepare\"" {:events [:multiaccounts.login/get-auth-method-success]} [{: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 {:db (assoc db :auth-method auth-method)} #(case auth-method - "biometric" + keychain/auth-method-biometric (biometric/biometric-auth %) - "password" - (keychain/get-user-password % address) + keychain/auth-method-password + (get-credentials % address) ;;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 {:events [:biometric-auth-done]} [{:keys [db] :as cofx} {:keys [bioauth-success bioauth-message bioauth-code]}] (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 - (keychain/get-user-password cofx address) + (get-credentials cofx address) (fx/merge cofx {:db (assoc-in db [:multiaccounts/login :save-password?] true)} (biometric/show-message bioauth-message bioauth-code) (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}))))))) + diff --git a/src/status_im/multiaccounts/logout/core.cljs b/src/status_im/multiaccounts/logout/core.cljs index 3406e91fc8..e611e960d7 100644 --- a/src/status_im/multiaccounts/logout/core.cljs +++ b/src/status_im/multiaccounts/logout/core.cljs @@ -22,7 +22,7 @@ (fx/defn logout {:events [:logout]} [cofx] - (logout-method cofx "none")) + (logout-method cofx keychain/auth-method-none)) (fx/defn show-logout-confirmation [_] {:ui/show-confirmation @@ -34,9 +34,9 @@ (fx/defn biometric-logout {:events [:biometric-logout]} - [{:keys [db] :as cofx}] + [cofx] (fx/merge cofx - (logout-method "biometric-prepare") + (logout-method keychain/auth-method-biometric-prepare) (fn [{:keys [db]}] {:db (assoc-in db [:multiaccounts/login :save-password?] true)}))) diff --git a/src/status_im/multiaccounts/recover/core.cljs b/src/status_im/multiaccounts/recover/core.cljs index fd0b6ee916..4af77024b8 100644 --- a/src/status_im/multiaccounts/recover/core.cljs +++ b/src/status_im/multiaccounts/recover/core.cljs @@ -4,9 +4,9 @@ [status-im.constants :as constants] [status-im.ethereum.core :as ethereum] [status-im.ethereum.mnemonic :as mnemonic] + [status-im.hardwallet.nfc :as nfc] [status-im.i18n :as i18n] [status-im.multiaccounts.create.core :as multiaccounts.create] - [status-im.multiaccounts.db :as db] [status-im.native-module.core :as status] [status-im.popover.core :as popover] [status-im.ui.screens.navigation :as navigation] @@ -14,8 +14,7 @@ [status-im.utils.security :as security] [status-im.utils.types :as types] [status-im.utils.platform :as platform] - [status-im.utils.utils :as utils] - [status-im.ethereum.eip55 :as eip55])) + [status-im.utils.utils :as utils])) (defn existing-account? [root-key multiaccounts] @@ -225,7 +224,8 @@ assoc :step :select-key-storage :forward-action :multiaccounts.recover/select-storage-next-pressed :selected-storage-type :default)} - (if platform/android? + (if (and platform/android? + (nfc/nfc-supported?)) (navigation/navigate-to-cofx :recover-multiaccount-select-storage nil) (select-storage-next-pressed)))) diff --git a/src/status_im/native_module/core.cljs b/src/status_im/native_module/core.cljs index 6d321d61ab..169ef6824b 100644 --- a/src/status_im/native_module/core.cljs +++ b/src/status_im/native_module/core.cljs @@ -165,8 +165,7 @@ (defn login-with-keycard [{:keys [multiaccount-data password chat-key]}] - (log/debug "[native-module] login-with-keycard" - "password" password) + (log/debug "[native-module] login-with-keycard") (clear-web-data) (.loginWithKeycard (status) multiaccount-data password chat-key)) diff --git a/src/status_im/signals/core.cljs b/src/status_im/signals/core.cljs index fec5cab564..35e73989e9 100644 --- a/src/status_im/signals/core.cljs +++ b/src/status_im/signals/core.cljs @@ -14,6 +14,8 @@ (fx/defn status-node-started [{db :db :as cofx} {:keys [error]}] + (log/debug "[signals] status-node-started" + "error" error) (if error {:db (-> db (update :multiaccounts/login dissoc :processing) diff --git a/src/status_im/ui/screens/biometric/views.cljs b/src/status_im/ui/screens/biometric/views.cljs index d820b218aa..29f43793ec 100644 --- a/src/status_im/ui/screens/biometric/views.cljs +++ b/src/status_im/ui/screens/biometric/views.cljs @@ -8,17 +8,73 @@ [status-im.ui.components.icons.vector-icons :as icons] [status-im.i18n :as i18n])) -(views/defview enable-biometric-popover [] - (views/letsubs [supported-biometric-auth [:supported-biometric-auth]] - (let [bio-type-label (biometric/get-label supported-biometric-auth)] - [react/view {:padding 24 :align-items :center} - [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 :t/enable) " " bio-type-label)] - [react/text {:style {:margin-bottom 25 :margin-top 10 :text-align :center}} - (i18n/label :t/to-enable-biometric {:bio-type-label bio-type-label})] - [button/button {:label (i18n/label :t/ok-save-pass) :style {:margin-bottom 16} - :on-press #(re-frame/dispatch [:biometric-logout])}] - [button/button {:label :t/cancel :type :secondary - :on-press #(re-frame/dispatch [:hide-popover])}]]))) \ No newline at end of file +(defn get-supported-biometric-auth [] + @(re-frame/subscribe [:supported-biometric-auth])) + +(defn get-bio-type-label [] + (biometric/get-label (get-supported-biometric-auth))) + +(defn biometric-popover + [{:keys [title-label description-label description-text + ok-button-label cancel-button-label on-cancel on-confirm]}] + (let [supported-biometric-auth (get-supported-biometric-auth) + bio-type-label (get-bio-type-label)] + [react/view {:padding 24 + :align-items :center} + [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})]]}])) diff --git a/src/status_im/ui/screens/db.cljs b/src/status_im/ui/screens/db.cljs index f3af96865b..5271534dc5 100644 --- a/src/status_im/ui/screens/db.cljs +++ b/src/status_im/ui/screens/db.cljs @@ -54,8 +54,7 @@ :registry {} :stickers/packs-owned #{} :stickers/packs-pending #{} - :hardwallet {:nfc-supported? false - :nfc-enabled? false + :hardwallet {:nfc-enabled? false :pin {:original [] :confirmation [] :current [] diff --git a/src/status_im/ui/screens/hardwallet/pin/views.cljs b/src/status_im/ui/screens/hardwallet/pin/views.cljs index 4e80cea09b..457f7419ff 100644 --- a/src/status_im/ui/screens/hardwallet/pin/views.cljs +++ b/src/status_im/ui/screens/hardwallet/pin/views.cljs @@ -7,8 +7,9 @@ [status-im.ui.components.react :as react] [status-im.ui.screens.hardwallet.pin.styles :as styles] [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?] [react/touchable-highlight @@ -68,8 +69,21 @@ (repeat (- 12 (count puk)) nil))))]) -(defn pin-view [{:keys [pin title-label description-label step status error-label - retry-counter small-screen?]}] +(defn save-password [] + (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)] [react/scroll-view [react/view styles/pin-container @@ -81,8 +95,7 @@ [react/text {:style styles/create-pin-text :number-of-lines 2} (i18n/label description-label)]) - [react/view {:margin-top 40 - :height (if small-screen? 18 22)} + [react/view {:flex 1} (case status :verifying [react/view styles/waiting-indicator-container [react/activity-indicator {:animating true @@ -94,6 +107,8 @@ [react/view {:margin-top (if (= step :puk) 24 8)} [react/text {:style {:text-align :center}} (i18n/label :t/pin-retries-left {:number retry-counter})]]))] + (when save-password-checkbox? + [save-password]) (if (= step :puk) [puk-indicators pin status] [pin-indicators pin status nil]) diff --git a/src/status_im/ui/screens/keycard/styles.cljs b/src/status_im/ui/screens/keycard/styles.cljs index 9147bdae34..946b948b7d 100644 --- a/src/status_im/ui/screens/keycard/styles.cljs +++ b/src/status_im/ui/screens/keycard/styles.cljs @@ -4,4 +4,4 @@ (def container {:flex 1 :justify-content :space-between - :background-color colors/white}) \ No newline at end of file + :background-color colors/white}) diff --git a/src/status_im/ui/screens/keycard/views.cljs b/src/status_im/ui/screens/keycard/views.cljs index d6d3f3a4f6..fea0bd492e 100644 --- a/src/status_im/ui/screens/keycard/views.cljs +++ b/src/status_im/ui/screens/keycard/views.cljs @@ -2,6 +2,7 @@ (:require-macros [status-im.utils.views :refer [defview letsubs]]) (:require [status-im.multiaccounts.core :as multiaccounts] [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.actions :as actions] [status-im.ui.screens.keycard.styles :as styles] @@ -370,8 +371,7 @@ retry-counter [:hardwallet/retry-counter]] [react/view styles/container [toolbar/toolbar - {:transparent? true - :style {:margin-top 32}} + {:transparent? true} (when multiple-multiaccounts? [toolbar/nav-button (actions/back @@ -385,12 +385,11 @@ [react/view {:flex 1 :flex-direction :column :justify-content :space-between - :align-items :center - :margin-top (if small-screen? 28 46)} + :align-items :center} [react/view {:flex-direction :column - :flex 1 :justify-content :center - :align-items :center} + :align-items :center + :height 140} [react/view {:margin-horizontal 16 :flex-direction :column} [react/view {:justify-content :center @@ -431,12 +430,13 @@ :ellipsize-mode :middle} (utils.core/truncate-str address 14 true)]]] [pin.views/pin-view - {:pin pin - :retry-counter retry-counter - :small-screen? small-screen? - :status status - :error-label error-label - :step enter-step}] + {:pin pin + :retry-counter retry-counter + :small-screen? small-screen? + :status status + :error-label error-label + :step enter-step + :save-password-checkbox? true}] [react/view {:margin-bottom (if small-screen? 25 32)} [react/touchable-highlight {:on-press #(re-frame/dispatch [:multiaccounts.recover.ui/recover-multiaccount-button-pressed])} diff --git a/src/status_im/ui/screens/multiaccounts/login/views.cljs b/src/status_im/ui/screens/multiaccounts/login/views.cljs index f62ff10379..3889358ee5 100644 --- a/src/status_im/ui/screens/multiaccounts/login/views.cljs +++ b/src/status_im/ui/screens/multiaccounts/login/views.cljs @@ -92,7 +92,7 @@ :margin-top 19}} [checkbox/checkbox {:checked? save-password? :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)]]))]] (when processing [react/view styles/processing-view diff --git a/src/status_im/ui/screens/multiaccounts/recover/views.cljs b/src/status_im/ui/screens/multiaccounts/recover/views.cljs index 0ec3a66a26..1b84d15c89 100644 --- a/src/status_im/ui/screens/multiaccounts/recover/views.cljs +++ b/src/status_im/ui/screens/multiaccounts/recover/views.cljs @@ -1,23 +1,13 @@ (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] - [status-im.ui.components.text-input.view :as text-input] [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.hardwallet.core :as hardwallet] + [status-im.hardwallet.nfc :as nfc] [status-im.i18n :as i18n] - [status-im.ui.components.styles :as components.styles] [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.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.utils.platform :as platform] [status-im.react-native.resources :as resources] @@ -68,7 +58,8 @@ :icon :main-icons/text :on-press #(re-frame/dispatch [::multiaccounts.recover/enter-phrase-pressed])}] (when (and config/hardwallet-enabled? - platform/android?) + platform/android? + (nfc/nfc-supported?)) [list-item/list-item {:theme :action :title :t/recover-with-keycard @@ -86,6 +77,9 @@ :style {:width 24 :height 24}}]] :on-press #(re-frame/dispatch [::hardwallet/recover-with-keycard-pressed])}])]]) -(def bottom-sheet +(defn bottom-sheet [] {:content bottom-sheet-view - :content-height (if platform/android? 130 65)}) + :content-height (if (and platform/android? + (nfc/nfc-supported?)) + 130 + 65)}) diff --git a/src/status_im/ui/screens/popover/views.cljs b/src/status_im/ui/screens/popover/views.cljs index bb7a074f53..5cb9a32c8c 100644 --- a/src/status_im/ui/screens/popover/views.cljs +++ b/src/status_im/ui/screens/popover/views.cljs @@ -106,6 +106,12 @@ (= :enable-biometric view) [biometric/enable-biometric-popover] + (= :secure-with-biometric view) + [biometric/secure-with-biometric-popover] + + (= :disable-password-saving view) + [biometric/disable-password-saving-popover] + :else [view])]]]]])))}))) diff --git a/src/status_im/ui/screens/privacy_and_security_settings/views.cljs b/src/status_im/ui/screens/privacy_and_security_settings/views.cljs index f01b0f5764..1ce14a843a 100644 --- a/src/status_im/ui/screens/privacy_and_security_settings/views.cljs +++ b/src/status_im/ui/screens/privacy_and_security_settings/views.cljs @@ -31,12 +31,12 @@ :title (str (i18n/label :t/lock-app-with) " " (biometric/get-label supported-biometric-auth)) :container-margin-bottom 8 :accessibility-label :biometric-auth-settings-switch - :disabled? (or (not (some? supported-biometric-auth)) keycard?) + :disabled? (not (some? supported-biometric-auth)) :accessories [[react/switch {:track-color #js {:true colors/blue :false nil} :value (boolean biometric-auth?) :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 ((complement boolean) biometric-auth?)])} ;; TODO - uncomment when implemented diff --git a/src/status_im/ui/screens/views.cljs b/src/status_im/ui/screens/views.cljs index ec76e4cb39..96638befe3 100644 --- a/src/status_im/ui/screens/views.cljs +++ b/src/status_im/ui/screens/views.cljs @@ -78,7 +78,7 @@ (merge home.sheet/group-chat-actions) (= 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))] [bottom-sheet-comp opts height-atom]))) diff --git a/src/status_im/utils/keychain/core.cljs b/src/status_im/utils/keychain/core.cljs index 36e431148f..295e35ac30 100644 --- a/src/status_im/utils/keychain/core.cljs +++ b/src/status_im/utils/keychain/core.cljs @@ -77,7 +77,11 @@ #js {:authenticationType (enum-val "ACCESS_CONTROL" "BIOMETRY_ANY_OR_DEVICE_PASSCODE")}) (.then callback))) +(defn- whisper-key-name [address] + (str address "-whisper")) + (defn can-save-user-password? [callback] + (log/debug "[keychain] can-save-user-password?") (cond platform/ios? (check-conditions callback device-encrypted?) @@ -91,6 +95,7 @@ (defn save-credentials "Stores the credentials for the address to the Keychain" [server username password callback] + (log/debug "[keychain] save-credentials") (-> (.setInternetCredentials rn/keychain (string/lower-case server) username password keychain-secure-hardware keychain-restricted-availability) (.then callback))) @@ -98,18 +103,27 @@ (defn get-credentials "Gets the credentials for a specified server from the Keychain" [server callback] + (log/debug "[keychain] get-credentials") (if platform/mobile? (-> (.getInternetCredentials rn/keychain (string/lower-case server)) (.then callback)) (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 :keychain/get-auth-method (fn [[address callback]] (can-save-user-password? (fn [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)))))) (re-frame/reg-fx @@ -117,6 +131,22 @@ (fn [[address callback]] (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 :keychain/save-user-password (fn [[address password]] @@ -134,6 +164,8 @@ (re-frame/reg-fx :keychain/save-auth-method (fn [[address method]] + (log/debug "[keychain] :keychain/save-auth-method" + "method" method) (save-credentials (str address "-auth") address @@ -145,6 +177,24 @@ "The app will continue to work normally, " "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 :keychain/clear-user-password (fn [address] @@ -160,13 +210,27 @@ (fx/defn get-user-password [_ address] {: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 - [cofx address 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 [{:keys [db]} address method] {:db (assoc db :auth-method method) - :keychain/save-auth-method [address method]}) \ No newline at end of file + :keychain/save-auth-method [address method]}) diff --git a/translations/en.json b/translations/en.json index b8d6e2c267..e867fdb71f 100644 --- a/translations/en.json +++ b/translations/en.json @@ -68,6 +68,13 @@ "biometric-auth-reason-login": "Login in Status", "biometric-auth-reason-verify": "Verify 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-title": "Looks like you’ve tapped \na blank keycard", "block": "Block", @@ -497,6 +504,7 @@ "group-info": "Group info", "gwei": "Gwei", "hash": "Hash", + "hardwallet-dont-ask-card": "Don't ask for card to sign in", "help": "help", "help-capitalized": "Help", "help-center": "Help Center",