Compare commits
13
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
4127fdae9e | ||
|
|
99074382bc | ||
|
|
84ad0d03fb | ||
|
|
c4b27cad05 | ||
|
|
7c231f1fc3 | ||
|
|
bd68f60524 | ||
|
|
b8b77d0d03 | ||
|
|
5d21482506 | ||
|
|
66d10bd116 | ||
|
|
1ab55787dd | ||
|
|
77b97199ff | ||
|
|
5059a4bea4 | ||
|
|
146c766892 |
@@ -42,7 +42,6 @@
|
||||
[react-native.core :as rn]
|
||||
[react-native.permissions :as permissions]
|
||||
[react-native.platform :as platform]
|
||||
[status-im.common.biometric.events :as biometric]
|
||||
status-im.common.serialization
|
||||
status-im.common.standard-authentication.events
|
||||
[status-im.common.theme.core :as theme]
|
||||
@@ -125,7 +124,7 @@
|
||||
:content (i18n/label :t/biometric-auth-confirm-message)
|
||||
:confirm-button-text (i18n/label :t/biometric-auth-confirm-try-again)
|
||||
:cancel-button-text (i18n/label :t/biometric-auth-confirm-logout)
|
||||
:on-accept #(biometric/authenticate nil {:on-fail on-biometric-auth-fail})
|
||||
:on-accept #(rf/dispatch [:biometric/authenticate {:on-fail on-biometric-auth-fail}])
|
||||
:on-cancel #(re-frame/dispatch [:multiaccounts.logout.ui/logout-confirmed])})))
|
||||
|
||||
(rf/defn on-return-from-background
|
||||
@@ -148,7 +147,7 @@
|
||||
#(when-let [chat-id (:current-chat-id db)]
|
||||
{:dispatch [:chat/mark-all-as-read chat-id]})
|
||||
#(when requires-bio-auth
|
||||
(biometric/authenticate % {:on-fail on-biometric-auth-fail})))))
|
||||
{:dispatch [:biometric/authenticate {:on-fail on-biometric-auth-fail}]}))))
|
||||
|
||||
(rf/defn on-going-in-background
|
||||
[{:keys [db now]}]
|
||||
|
||||
@@ -28,10 +28,15 @@
|
||||
[:on-success [:or fn? [:cat keyword? [:* :any]]]]
|
||||
[:on-error [:or fn? [:cat keyword? [:* :any]]]]]])
|
||||
|
||||
(def ^:private ?error
|
||||
[:fn {:error/message "schema.common/error should be of type js/Error"}
|
||||
(fn [v] (instance? js/Error v))])
|
||||
|
||||
(defn register-schemas
|
||||
[]
|
||||
(registry/register ::theme ?theme)
|
||||
(registry/register ::customization-color ?customization-color)
|
||||
(registry/register ::public-key ?public-key)
|
||||
(registry/register ::image-source ?image-source)
|
||||
(registry/register ::rpc-call ?rpc-call))
|
||||
(registry/register ::rpc-call ?rpc-call)
|
||||
(registry/register ::error ?error))
|
||||
|
||||
@@ -5,6 +5,16 @@
|
||||
(def ^:private ?cofx
|
||||
[:map])
|
||||
|
||||
(def ^:private ?event-fx
|
||||
[:maybe
|
||||
[:map {:closed true}
|
||||
[:db {:optional true} [:maybe map?]]
|
||||
[:fx {:optional true}
|
||||
[:maybe
|
||||
[:vector {:optional true}
|
||||
[:vector :any]]]]]])
|
||||
|
||||
(defn register-schemas
|
||||
[]
|
||||
(registry/register ::event-fx ?event-fx)
|
||||
(registry/register ::cofx ?cofx))
|
||||
|
||||
@@ -1,120 +1,124 @@
|
||||
(ns status-im.common.biometric.events
|
||||
(:require
|
||||
[native-module.core :as native-module]
|
||||
[re-frame.core :as re-frame]
|
||||
[react-native.biometrics :as biometrics]
|
||||
[react-native.platform :as platform]
|
||||
[schema.core :as schema]
|
||||
[status-im.common.biometric.events-schema :as events-schema]
|
||||
[status-im.common.biometric.utils :as utils]
|
||||
[status-im.common.keychain.events :as keychain]
|
||||
[status-im.constants :as constants]
|
||||
[taoensso.timbre :as log]
|
||||
[utils.i18n :as i18n]
|
||||
[utils.re-frame :as rf]))
|
||||
|
||||
(def android-device-blacklisted?
|
||||
(and platform/android? (= (:brand (native-module/get-device-model-info)) "bannedbrand")))
|
||||
|
||||
(defn get-label-by-type
|
||||
[biometric-type]
|
||||
(condp = biometric-type
|
||||
constants/biometrics-type-android (i18n/label :t/biometric-fingerprint)
|
||||
constants/biometrics-type-face-id (i18n/label :t/biometric-faceid)
|
||||
(i18n/label :t/biometric-touchid)))
|
||||
|
||||
(defn get-icon-by-type
|
||||
[biometric-type]
|
||||
(condp = biometric-type
|
||||
constants/biometrics-type-face-id :i/face-id
|
||||
:i/touch-id))
|
||||
|
||||
(re-frame/reg-fx
|
||||
:biometric/get-supported-biometric-type
|
||||
(rf/reg-fx
|
||||
:biometric/get-supported-type
|
||||
(fn []
|
||||
;;NOTE: if we can't save user password, we can't use biometric
|
||||
(keychain/can-save-user-password?
|
||||
(fn [can-save?]
|
||||
(when (and can-save? (not android-device-blacklisted?))
|
||||
(when (and can-save? (not utils/android-device-blacklisted?))
|
||||
(-> (biometrics/get-supported-type)
|
||||
(.then (fn [type]
|
||||
(rf/dispatch [:biometric/get-supported-biometric-type-success type])))))))))
|
||||
(rf/dispatch [:biometric/set-supported-type type])))))))))
|
||||
|
||||
(rf/defn get-supported-biometric-auth-success
|
||||
{:events [:biometric/get-supported-biometric-type-success]}
|
||||
[{:keys [db]} supported-type]
|
||||
(defn set-supported-type
|
||||
[{:keys [db]} [supported-type]]
|
||||
{:db (assoc db :biometric/supported-type supported-type)})
|
||||
|
||||
(rf/defn show-message
|
||||
{:events [:biometric/show-message]}
|
||||
[_ error]
|
||||
(let [code (ex-cause error)
|
||||
content (if (#{::biometrics/not-enrolled
|
||||
(schema/=> set-supported-type events-schema/?set-supported-type)
|
||||
(rf/reg-event-fx :biometric/set-supported-type set-supported-type)
|
||||
|
||||
(defn show-message
|
||||
[_ [code]]
|
||||
(let [content (if (#{::biometrics/not-enrolled
|
||||
::biometrics/not-available}
|
||||
code)
|
||||
(i18n/label :t/grant-face-id-permissions)
|
||||
(i18n/label :t/biometric-auth-error {:code code}))]
|
||||
{:effects.utils/show-popup
|
||||
{:title (i18n/label :t/biometric-auth-login-error-title)
|
||||
:content content}}))
|
||||
{:fx [[:effects.utils/show-popup
|
||||
{:title (i18n/label :t/biometric-auth-login-error-title)
|
||||
:content content}]]}))
|
||||
|
||||
(schema/=> show-message events-schema/?show-message)
|
||||
(rf/reg-event-fx :biometric/show-message show-message)
|
||||
|
||||
(re-frame/reg-fx
|
||||
(rf/reg-fx
|
||||
:biometric/authenticate
|
||||
(fn [{:keys [on-success on-fail prompt-message]}]
|
||||
(fn [{:keys [on-success on-fail on-cancel prompt-message on-done]}]
|
||||
(-> (biometrics/authenticate
|
||||
{:prompt-message (or prompt-message (i18n/label :t/biometric-auth-reason-login))
|
||||
:fallback-prompt-message (i18n/label
|
||||
:t/biometric-auth-login-ios-fallback-label)
|
||||
:cancel-button-text (i18n/label :t/cancel)})
|
||||
(.then (fn [not-canceled?]
|
||||
(when (and on-success not-canceled?)
|
||||
(on-success))))
|
||||
(utils/handle-cb on-done)
|
||||
(if not-canceled?
|
||||
(utils/handle-cb on-success)
|
||||
(utils/handle-cb on-cancel))))
|
||||
(.catch (fn [err]
|
||||
(when on-fail
|
||||
(on-fail err)))))))
|
||||
(utils/handle-cb on-done)
|
||||
(utils/handle-cb on-fail err))))))
|
||||
|
||||
(rf/defn authenticate
|
||||
{:events [:biometric/authenticate]}
|
||||
[_ opts]
|
||||
{:biometric/authenticate opts})
|
||||
(defn authenticate
|
||||
[{:keys [db]} [opts]]
|
||||
(let [pending? (get db :biometric/auth-pending?)]
|
||||
;;NOTE: prompting biometric check while another one is pending triggers error
|
||||
(when-not pending?
|
||||
{:db (assoc db :biometric/auth-pending? true)
|
||||
:fx [[:biometric/authenticate
|
||||
(assoc opts :on-done [:set :biometric/auth-pending? false])]]})))
|
||||
|
||||
(rf/reg-event-fx
|
||||
:biometric/on-enable-success
|
||||
(fn [{:keys [db]} [password]]
|
||||
(let [key-uid (get-in db [:profile/profile :key-uid])]
|
||||
{:db (assoc db :auth-method constants/auth-method-biometric)
|
||||
:dispatch [:keychain/save-password-and-auth-method
|
||||
{:key-uid key-uid
|
||||
:masked-password password}]})))
|
||||
(schema/=> authenticate events-schema/?authenticate)
|
||||
(rf/reg-event-fx :biometric/authenticate authenticate)
|
||||
|
||||
(rf/reg-event-fx
|
||||
:biometric/enable
|
||||
(fn [_ [password]]
|
||||
{:dispatch [:biometric/authenticate
|
||||
{:on-success #(rf/dispatch [:biometric/on-enable-success password])
|
||||
:on-fail #(rf/dispatch [:biometric/show-message %])}]}))
|
||||
(defn enable-biometrics
|
||||
[{:keys [db]} [password]]
|
||||
(let [key-uid (get-in db [:profile/profile :key-uid])]
|
||||
{:db (assoc db :auth-method constants/auth-method-biometric)
|
||||
:fx [[:dispatch
|
||||
[:keychain/save-password-and-auth-method
|
||||
{:key-uid key-uid
|
||||
:masked-password password}]]]}))
|
||||
|
||||
(rf/reg-event-fx
|
||||
:biometric/disable
|
||||
(fn [{:keys [db]}]
|
||||
(let [key-uid (get-in db [:profile/profile :key-uid])]
|
||||
{:db (assoc db :auth-method constants/auth-method-none)
|
||||
:keychain/clear-user-password key-uid})))
|
||||
(schema/=> enable-biometrics events-schema/?enable-biometrics)
|
||||
(rf/reg-event-fx :biometric/enable enable-biometrics)
|
||||
|
||||
(rf/reg-fx
|
||||
:biometric/check-if-available
|
||||
(fn [[key-uid callback]]
|
||||
(keychain/can-save-user-password?
|
||||
(fn [can-save?]
|
||||
(when can-save?
|
||||
(-> (biometrics/get-available)
|
||||
(.then (fn [available?]
|
||||
(when-not available?
|
||||
(throw (js/Error. "biometric-not-available")))))
|
||||
(.then #(keychain/get-auth-method! key-uid))
|
||||
(.then (fn [auth-method]
|
||||
(when auth-method (callback auth-method))))
|
||||
(.catch (fn [err]
|
||||
(when-not (= (.-message err) "biometric-not-available")
|
||||
(log/error "Failed to check if biometrics is available"
|
||||
{:error err
|
||||
:key-uid key-uid
|
||||
:event :profile.login/check-biometric}))))))))))
|
||||
(defn disable-biometrics
|
||||
[{:keys [db]}]
|
||||
(let [key-uid (get-in db [:profile/profile :key-uid])]
|
||||
{:db (assoc db :auth-method constants/auth-method-none)
|
||||
:fx [[:keychain/clear-user-password key-uid]]}))
|
||||
|
||||
(schema/=> disable-biometrics events-schema/?disable-biometrics)
|
||||
(rf/reg-event-fx :biometric/disable disable-biometrics)
|
||||
|
||||
(defn check-if-biometrics-available
|
||||
[{:keys [key-uid on-success on-fail]}]
|
||||
(keychain/can-save-user-password?
|
||||
(fn [can-save?]
|
||||
(if-not can-save?
|
||||
(utils/handle-cb on-fail
|
||||
(ex-info "cannot-save-user-password"
|
||||
{:fx :biometric/check-if-available}))
|
||||
(-> (biometrics/get-available)
|
||||
(.then (fn [available?]
|
||||
(when-not available?
|
||||
(throw (js/Error. "biometric-not-available")))))
|
||||
(.then #(keychain/get-auth-method! key-uid))
|
||||
(.then (fn [auth-method]
|
||||
(when auth-method
|
||||
(utils/handle-cb on-success auth-method))))
|
||||
(.catch (fn [err]
|
||||
(let [message (.-message err)]
|
||||
(utils/handle-cb on-fail
|
||||
(ex-info message
|
||||
{:err err
|
||||
:fx :biometric/check-if-available}))
|
||||
(when-not (= message "biometric-not-available")
|
||||
(log/error "Failed to check if biometrics is available"
|
||||
{:error err
|
||||
:key-uid key-uid
|
||||
:event :profile.login/check-biometric}))))))))))
|
||||
|
||||
(schema/=> check-if-biometrics-available events-schema/?check-if-biometrics-available)
|
||||
(rf/reg-fx :biometric/check-if-available check-if-biometrics-available)
|
||||
|
||||
@@ -0,0 +1,62 @@
|
||||
(ns status-im.common.biometric.events-schema
|
||||
(:require
|
||||
[status-im.constants :as constants]
|
||||
[utils.security.core :as security]))
|
||||
|
||||
(def ?set-supported-type
|
||||
[:=>
|
||||
[:catn
|
||||
[:cofx :schema.re-frame/cofx]
|
||||
[:args
|
||||
[:tuple
|
||||
[:enum
|
||||
constants/biometrics-type-android
|
||||
constants/biometrics-type-face-id
|
||||
constants/biometrics-type-touch-id]]]]
|
||||
:schema.re-frame/event-fx])
|
||||
|
||||
(def ?show-message
|
||||
[:=>
|
||||
[:catn
|
||||
[:cofx :schema.re-frame/cofx]
|
||||
[:args
|
||||
[:tuple keyword?]]]
|
||||
:schema.re-frame/event-fx])
|
||||
|
||||
(def ?authenticate
|
||||
[:=>
|
||||
[:catn
|
||||
[:cofx :schema.re-frame/cofx]
|
||||
[:args
|
||||
[:tuple
|
||||
[:map {:closed true}
|
||||
[:on-success {:optional true} fn?]
|
||||
[:on-fail {:optional true} fn?]
|
||||
[:on-cancel {:optional true} fn?]
|
||||
[:prompt-message {:optional true} :string]]]]]
|
||||
:schema.re-frame/event-fx])
|
||||
|
||||
(def ?enable-biometrics
|
||||
[:=>
|
||||
[:catn
|
||||
[:cofx :schema.re-frame/cofx]
|
||||
[:args
|
||||
[:tuple
|
||||
[:fn {:error/message "Should be an instance of security/MaskedData"}
|
||||
(fn [pw] (instance? security/MaskedData pw))]]]]
|
||||
:schema.re-frame/event-fx])
|
||||
|
||||
(def ?disable-biometrics
|
||||
[:=>
|
||||
[:catn
|
||||
[:cofx :schema.re-frame/cofx]]
|
||||
:schema.re-frame/event-fx])
|
||||
|
||||
(def ?check-if-biometrics-available
|
||||
[:=>
|
||||
[:cat
|
||||
[:map {:closed true}
|
||||
[:key-uid string?]
|
||||
[:on-success {:optional true} [:maybe fn?]]
|
||||
[:on-fail {:optional true} [:maybe fn?]]]]
|
||||
:any])
|
||||
@@ -0,0 +1,80 @@
|
||||
(ns status-im.common.biometric.events-test
|
||||
(:require [cljs.test :refer [deftest testing is]]
|
||||
matcher-combinators.test
|
||||
[react-native.biometrics :as biometrics]
|
||||
[status-im.common.biometric.events :as sut]
|
||||
[status-im.constants :as constants]
|
||||
[utils.i18n :as i18n]
|
||||
[utils.security.core :as security]))
|
||||
|
||||
(deftest set-supported-biometrics-type-test
|
||||
(testing "successfully setting supported biometrics type"
|
||||
(let [cofx {:db {}}
|
||||
supported-type constants/biometrics-type-face-id
|
||||
expected {:db (assoc (:db cofx) :biometric/supported-type supported-type)}]
|
||||
(is (match? expected (sut/set-supported-type cofx [supported-type])))))
|
||||
|
||||
(testing "throws error when setting unsupported biometrics type"
|
||||
(let [cofx {:db {}}
|
||||
supported-type :unsupported-type]
|
||||
(is (thrown? js/Error (sut/set-supported-type cofx [supported-type]))))))
|
||||
|
||||
(deftest show-message-test
|
||||
(testing "informs the user to enable biometrics from settings"
|
||||
(let [cofx {:db {}}
|
||||
expected {:fx [[:effects.utils/show-popup
|
||||
{:title (i18n/label :t/biometric-auth-login-error-title)
|
||||
:content (i18n/label :t/grant-face-id-permissions)}]]}]
|
||||
(is (match? expected
|
||||
(sut/show-message cofx
|
||||
[::biometrics/not-available])))))
|
||||
|
||||
(testing "shows a generic error message"
|
||||
(let [cofx {:db {}}
|
||||
error-cause :test-error
|
||||
expected {:fx [[:effects.utils/show-popup
|
||||
{:title (i18n/label :t/biometric-auth-login-error-title)
|
||||
:content (i18n/label :t/biometric-auth-error {:code error-cause})}]]}]
|
||||
(is (match? expected
|
||||
(sut/show-message cofx
|
||||
[error-cause]))))))
|
||||
|
||||
(deftest authenticate-biometrics-test
|
||||
(testing "passing the right args to authenticate"
|
||||
(let [cofx {:db {}}
|
||||
args {:on-success identity
|
||||
:on-fail identity
|
||||
:prompt-message "test"}
|
||||
result (sut/authenticate cofx [args])]
|
||||
(is (= (:prompt-message args) (get-in result [:fx 0 1 :prompt-message])))
|
||||
(is (not (nil? (get-in result [:fx 0 1 :on-success]))))
|
||||
(is (not (nil? (get-in result [:fx 0 1 :on-fail]))))))
|
||||
|
||||
(testing "skips biometric check if another one pending"
|
||||
(let [cofx {:db {:biometric/auth-pending? true}}
|
||||
result (sut/authenticate cofx [{}])]
|
||||
(is (nil? result)))))
|
||||
|
||||
(deftest enable-biometrics-test
|
||||
(testing "successfully enabling biometrics"
|
||||
(let [key-uid "test-uid"
|
||||
password (security/mask-data "test-pw")
|
||||
cofx {:db {:profile/profile {:key-uid key-uid}}}
|
||||
expected-db (assoc (:db cofx) :auth-method constants/auth-method-biometric)
|
||||
result (sut/enable-biometrics cofx [password])]
|
||||
(is (match? expected-db (:db result)))
|
||||
(is (= password (get-in result [:fx 0 1 1 :masked-password])))))
|
||||
|
||||
(testing "throws error if raw password is passed"
|
||||
(let [key-uid "test-uid"
|
||||
password "test-password"
|
||||
cofx {:db {:profile/profile {:key-uid key-uid}}}]
|
||||
(is (thrown? js/Error (sut/enable-biometrics cofx [password]))))))
|
||||
|
||||
(deftest disable-biometrics-test
|
||||
(testing "successfully disabling biometrics"
|
||||
(let [key-uid "test-uid"
|
||||
cofx {:db {:profile/profile {:key-uid key-uid}}}
|
||||
expected {:db (assoc (:db cofx) :auth-method constants/auth-method-none)
|
||||
:fx [[:keychain/clear-user-password key-uid]]}]
|
||||
(is (match? expected (sut/disable-biometrics cofx))))))
|
||||
@@ -0,0 +1,36 @@
|
||||
(ns status-im.common.biometric.utils
|
||||
(:require
|
||||
[native-module.core :as native-module]
|
||||
[react-native.platform :as platform]
|
||||
[status-im.constants :as constants]
|
||||
[utils.i18n :as i18n]
|
||||
[utils.re-frame :as rf]))
|
||||
|
||||
(def android-device-blacklisted?
|
||||
(and platform/android? (= (:brand (native-module/get-device-model-info)) "bannedbrand")))
|
||||
|
||||
(defn get-label-by-type
|
||||
[biometric-type]
|
||||
(condp = biometric-type
|
||||
constants/biometrics-type-android (i18n/label :t/biometric-fingerprint)
|
||||
constants/biometrics-type-face-id (i18n/label :t/biometric-faceid)
|
||||
(i18n/label :t/biometric-touchid)))
|
||||
|
||||
(defn get-icon-by-type
|
||||
[biometric-type]
|
||||
(condp = biometric-type
|
||||
constants/biometrics-type-face-id :i/face-id
|
||||
:i/touch-id))
|
||||
|
||||
;; NOTE: move to utils.re-frame?
|
||||
(defn handle-cb
|
||||
"Handles re-frame callbacks that are passed as anonymous fn or as dispatch vectors to effects.
|
||||
|
||||
e.g.:\n
|
||||
* with dispatch vector - `:on-success [:my-ns/my-event]`\n
|
||||
* with fn - `:on-success #(rf/dispatch [:my-ns/my-event])`"
|
||||
[cb & args]
|
||||
(when cb
|
||||
(cond
|
||||
(fn? cb) (apply cb args)
|
||||
(vector? cb) (rf/dispatch (into cb args)))))
|
||||
@@ -6,47 +6,59 @@
|
||||
[status-im.common.standard-authentication.password-input.view :as password-input]
|
||||
[status-im.contexts.profile.utils :as profile.utils]
|
||||
[utils.i18n :as i18n]
|
||||
[utils.re-frame :as rf]))
|
||||
[utils.re-frame :as rf]
|
||||
[reagent.core :as reagent]
|
||||
[utils.security.core :as security]))
|
||||
|
||||
(defn view
|
||||
[{:keys [on-enter-password on-press-biometrics button-label button-icon-left]}]
|
||||
(let [{:keys [key-uid customization-color] :as profile} (rf/sub [:profile/profile-with-image])
|
||||
{:keys [error processing password]} (rf/sub [:profile/login])
|
||||
sign-in-enabled? (rf/sub [:sign-in-enabled?])]
|
||||
[:<>
|
||||
[rn/view {:style style/enter-password-container}
|
||||
[rn/view
|
||||
[quo/text
|
||||
{:accessibility-label :sync-code-generated
|
||||
:weight :bold
|
||||
:size :heading-1
|
||||
:style {:margin-bottom 4}}
|
||||
(i18n/label :t/enter-password)]
|
||||
[rn/view
|
||||
{:style style/context-tag}
|
||||
[quo/context-tag
|
||||
{:type :default
|
||||
:blur? true
|
||||
:profile-picture (profile.utils/photo profile)
|
||||
:full-name (profile.utils/displayed-name profile)
|
||||
:customization-color customization-color
|
||||
:size 24}]]
|
||||
[password-input/view
|
||||
{:on-press-biometrics on-press-biometrics
|
||||
:processing processing
|
||||
:error error
|
||||
:default-password password
|
||||
:sign-in-enabled? sign-in-enabled?}]
|
||||
[quo/button
|
||||
{:size 40
|
||||
:container-style style/enter-password-button
|
||||
:type :primary
|
||||
:customization-color (or customization-color :primary)
|
||||
:accessibility-label :login-button
|
||||
:icon-left button-icon-left
|
||||
:disabled? (or (not sign-in-enabled?) processing)
|
||||
:on-press (fn []
|
||||
(rf/dispatch [:set-in [:profile/login :key-uid] key-uid])
|
||||
(rf/dispatch [:profile.login/verify-database-password password
|
||||
#(on-enter-password password)]))}
|
||||
button-label]]]]))
|
||||
[]
|
||||
(let [password-value (reagent/atom nil)]
|
||||
(fn [{:keys [on-enter-password on-press-biometrics button-label button-icon-left]}]
|
||||
(let [{:keys [key-uid customization-color]
|
||||
:as profile} (rf/sub [:profile/profile-with-image])
|
||||
{:keys [error processing password]} (rf/sub [:profile/login])
|
||||
sign-in-enabled? (rf/sub [:sign-in-enabled?])
|
||||
on-change-password (fn [value]
|
||||
(reset! password-value value)
|
||||
(when (not= value (security/safe-unmask-data password))
|
||||
(rf/dispatch [:set-in [:profile/login :password]
|
||||
(security/mask-data value)])
|
||||
(rf/dispatch [:set-in [:profile/login :error] ""])))]
|
||||
[:<>
|
||||
[rn/view {:style style/enter-password-container}
|
||||
[rn/view
|
||||
[quo/text
|
||||
{:accessibility-label :sync-code-generated
|
||||
:weight :bold
|
||||
:size :heading-1
|
||||
:style {:margin-bottom 4}}
|
||||
(i18n/label :t/enter-password)]
|
||||
[rn/view
|
||||
{:style style/context-tag}
|
||||
[quo/context-tag
|
||||
{:type :default
|
||||
:blur? true
|
||||
:profile-picture (profile.utils/photo profile)
|
||||
:full-name (profile.utils/displayed-name profile)
|
||||
:customization-color customization-color
|
||||
:size 24}]]
|
||||
[password-input/view
|
||||
{:on-press-biometrics on-press-biometrics
|
||||
:processing processing
|
||||
:error error
|
||||
:password @password-value
|
||||
:on-change-password on-change-password
|
||||
:sign-in-enabled? sign-in-enabled?}]
|
||||
[quo/button
|
||||
{:size 40
|
||||
:container-style style/enter-password-button
|
||||
:type :primary
|
||||
:customization-color (or customization-color :primary)
|
||||
:accessibility-label :login-button
|
||||
:icon-left button-icon-left
|
||||
:disabled? (or (not sign-in-enabled?) processing)
|
||||
:on-press (fn []
|
||||
(rf/dispatch [:set-in [:profile/login :key-uid] key-uid])
|
||||
(rf/dispatch [:profile.login/verify-database-password password
|
||||
#(on-enter-password password)]))}
|
||||
button-label]]]]))))
|
||||
|
||||
@@ -1,11 +1,86 @@
|
||||
(ns status-im.common.standard-authentication.events
|
||||
(:require
|
||||
[utils.re-frame :as rf]))
|
||||
[schema.core :as schema]
|
||||
[status-im.common.standard-authentication.enter-password.view :as enter-password]
|
||||
[status-im.common.standard-authentication.events-schema :as events-schema]
|
||||
[taoensso.timbre :as log]
|
||||
[utils.i18n :as i18n]
|
||||
[utils.re-frame :as rf]
|
||||
[utils.security.core :as security]))
|
||||
|
||||
(rf/reg-event-fx :standard-auth/on-biometric-success
|
||||
(fn [{:keys [db]} [callback]]
|
||||
(let [key-uid (get-in db [:profile/profile :key-uid])]
|
||||
{:fx [[:keychain/get-user-password [key-uid callback]]]})))
|
||||
(defn authorize
|
||||
[{:keys [db]} [args]]
|
||||
(let [key-uid (get-in db [:profile/profile :key-uid])]
|
||||
{:fx [[:biometric/check-if-available
|
||||
{:key-uid key-uid
|
||||
:on-success [:standard-auth/authorize-with-biometric args]
|
||||
:on-fail [:standard-auth/authorize-with-password args]}]]}))
|
||||
|
||||
(schema/=> authorize events-schema/?authorize)
|
||||
(rf/reg-event-fx :standard-auth/authorize authorize)
|
||||
|
||||
(defn authorize-with-biometric
|
||||
[_ [{:keys [on-auth-success on-auth-fail] :as args}]]
|
||||
(let [args-with-biometric-btn
|
||||
(assoc args
|
||||
:on-press-biometric
|
||||
#(rf/dispatch [:standard-auth/authorize-with-biometric args]))]
|
||||
{:fx [[:dispatch [:dismiss-keyboard]]
|
||||
[:dispatch
|
||||
[:biometric/authenticate
|
||||
{:prompt-message (i18n/label :t/biometric-auth-confirm-message)
|
||||
:on-cancel [:standard-auth/authorize-with-password args-with-biometric-btn]
|
||||
:on-success [:standard-auth/on-biometric-success on-auth-success]
|
||||
:on-fail [:standard-auth/on-biometric-fail on-auth-fail]}]]]}))
|
||||
|
||||
(schema/=> authorize-with-biometric events-schema/?authorize-with-biometric)
|
||||
(rf/reg-event-fx :standard-auth/authorize-with-biometric authorize-with-biometric)
|
||||
|
||||
(defn on-biometric-success
|
||||
[{:keys [db]} [on-auth-success]]
|
||||
(let [key-uid (get-in db [:profile/profile :key-uid])]
|
||||
{:fx [[:keychain/get-user-password [key-uid on-auth-success]]]}))
|
||||
|
||||
(schema/=> on-biometric-success events-schema/?on-biometric-success)
|
||||
(rf/reg-event-fx :standard-auth/on-biometric-success on-biometric-success)
|
||||
|
||||
(defn on-biometric-fail
|
||||
[_ [on-auth-fail error]]
|
||||
(when on-auth-fail
|
||||
(on-auth-fail error))
|
||||
(log/error (ex-message error)
|
||||
(-> error
|
||||
ex-data
|
||||
(assoc :code (ex-cause error)
|
||||
:event :standard-auth/on-biometric-fail)))
|
||||
{:fx [[:dispatch [:biometric/show-message (ex-cause error)]]]})
|
||||
|
||||
(schema/=> on-biometric-fail events-schema/?on-biometrics-fail)
|
||||
(rf/reg-event-fx :standard-auth/on-biometric-fail on-biometric-fail)
|
||||
|
||||
(defn- bottom-sheet-password-view
|
||||
[{:keys [on-press-biometric on-auth-success auth-button-icon-left auth-button-label]}]
|
||||
(fn []
|
||||
(let [handle-password-success (fn [password]
|
||||
(-> password security/hash-masked-password on-auth-success))]
|
||||
[enter-password/view
|
||||
{:on-enter-password handle-password-success
|
||||
:on-press-biometrics on-press-biometric
|
||||
:button-icon-left auth-button-icon-left
|
||||
:button-label auth-button-label}])))
|
||||
|
||||
(defn authorize-with-password
|
||||
[_ [{:keys [on-close theme blur?] :as args}]]
|
||||
{:fx [[:dispatch [:standard-auth/reset-login-password]]
|
||||
[:dispatch
|
||||
[:show-bottom-sheet
|
||||
{:on-close on-close
|
||||
:theme theme
|
||||
:shell? blur?
|
||||
:content #(bottom-sheet-password-view args)}]]]})
|
||||
|
||||
(schema/=> authorize-with-password events-schema/?authorize-with-password)
|
||||
(rf/reg-event-fx :standard-auth/authorize-with-password authorize-with-password)
|
||||
|
||||
(rf/reg-event-fx
|
||||
:standard-auth/reset-login-password
|
||||
|
||||
@@ -0,0 +1,53 @@
|
||||
(ns status-im.common.standard-authentication.events-schema)
|
||||
|
||||
(def ^:private ?authorize-map
|
||||
[:map {:closed true}
|
||||
[:on-auth-success fn?]
|
||||
[:on-auth-fail {:optional true} [:maybe fn?]]
|
||||
[:on-close {:optional true} [:maybe fn?]]
|
||||
[:auth-button-label {:optional true} [:maybe string?]]
|
||||
[:auth-button-icon-left {:optional true} [:maybe keyword?]]
|
||||
[:blur? {:optional true} [:maybe boolean?]]
|
||||
[:theme {:optional true} [:maybe :schema.common/theme]]])
|
||||
|
||||
(def ?authorize
|
||||
[:=>
|
||||
[:catn
|
||||
[:cofx :schema.re-frame/cofx]
|
||||
[:args
|
||||
[:tuple ?authorize-map]]]
|
||||
:schema.re-frame/event-fx])
|
||||
|
||||
(def ?authorize-with-biometric
|
||||
[:=>
|
||||
[:catn
|
||||
[:cofx :schema.re-frame/cofx]
|
||||
[:args
|
||||
[:tuple ?authorize-map]]]
|
||||
:schema.re-frame/event-fx])
|
||||
|
||||
(def ?on-biometric-success
|
||||
[:=>
|
||||
[:catn
|
||||
[:cofx :schema.re-frame/cofx]
|
||||
[:args
|
||||
[:tuple fn?]]]
|
||||
:schema.re-frame/event-fx])
|
||||
|
||||
(def ?on-biometrics-fail
|
||||
[:=>
|
||||
[:catn
|
||||
[:cofx :schema.re-frame/cofx]
|
||||
[:args
|
||||
[:tuple
|
||||
[:maybe fn?]
|
||||
[:maybe :schema.common/error]]]]
|
||||
:schema.re-frame/event-fx])
|
||||
|
||||
(def ?authorize-with-password
|
||||
[:=>
|
||||
[:catn
|
||||
[:cofx :schema.re-frame/cofx]
|
||||
[:args
|
||||
[:tuple ?authorize-map]]]
|
||||
:schema.re-frame/event-fx])
|
||||
@@ -9,7 +9,8 @@
|
||||
[status-im.common.standard-authentication.password-input.style :as style]
|
||||
[utils.i18n :as i18n]
|
||||
[utils.re-frame :as rf]
|
||||
[utils.security.core :as security]))
|
||||
[utils.security.core :as security]
|
||||
[reagent.core :as reagent]))
|
||||
|
||||
(defn get-error-message
|
||||
[error]
|
||||
@@ -20,14 +21,8 @@
|
||||
(i18n/label :t/oops-wrong-password)
|
||||
error))
|
||||
|
||||
(defn- on-change-password
|
||||
[entered-password]
|
||||
(rf/dispatch [:set-in [:profile/login :password]
|
||||
(security/mask-data entered-password)])
|
||||
(rf/dispatch [:set-in [:profile/login :error] ""]))
|
||||
|
||||
(defn- view-internal
|
||||
[{:keys [default-password theme shell? on-press-biometrics blur?]}]
|
||||
[{:keys [password on-change-password theme shell? on-press-biometrics blur?]}]
|
||||
(let [{:keys [error processing]} (rf/sub [:profile/login])
|
||||
error-message (get-error-message error)
|
||||
error? (boolean (seq error-message))]
|
||||
@@ -43,7 +38,7 @@
|
||||
:error? error?
|
||||
:label (i18n/label :t/profile-password)
|
||||
:on-change-text on-change-password
|
||||
:default-value (security/safe-unmask-data default-password)}]
|
||||
:value password}]
|
||||
(when on-press-biometrics
|
||||
[quo/button
|
||||
{:container-style style/auth-button
|
||||
|
||||
@@ -1,67 +0,0 @@
|
||||
(ns status-im.common.standard-authentication.standard-auth.authorize
|
||||
(:require
|
||||
[react-native.biometrics :as biometrics]
|
||||
[status-im.common.standard-authentication.enter-password.view :as enter-password]
|
||||
[taoensso.timbre :as log]
|
||||
[utils.i18n :as i18n]
|
||||
[utils.re-frame :as rf]
|
||||
[utils.security.core :as security]))
|
||||
|
||||
(defn reset-password
|
||||
[]
|
||||
(rf/dispatch [:set-in [:profile/login :password] nil])
|
||||
(rf/dispatch [:set-in [:profile/login :error] ""]))
|
||||
|
||||
(defn authorize
|
||||
[{:keys [biometric-auth? on-auth-success on-auth-fail on-close
|
||||
auth-button-label theme blur? auth-button-icon-left]}]
|
||||
(let [handle-auth-success (fn [biometric?]
|
||||
(fn [entered-password]
|
||||
(let [sha3-masked-password (if biometric?
|
||||
entered-password
|
||||
(security/hash-masked-password
|
||||
entered-password))]
|
||||
(on-auth-success sha3-masked-password))))
|
||||
password-login (fn [{:keys [on-press-biometrics]}]
|
||||
(rf/dispatch [:show-bottom-sheet
|
||||
{:on-close on-close
|
||||
:theme theme
|
||||
:shell? blur?
|
||||
:content (fn []
|
||||
[enter-password/view
|
||||
{:on-enter-password (handle-auth-success
|
||||
false)
|
||||
:on-press-biometrics on-press-biometrics
|
||||
:button-icon-left auth-button-icon-left
|
||||
:button-label auth-button-label}])}]))
|
||||
; biometrics-login recursively passes itself as a parameter because if the user
|
||||
; fails biometric auth they will be shown the password bottom sheet with an option
|
||||
; to retrigger biometric auth, so they can endlessly repeat this cycle.
|
||||
biometrics-login (fn [on-press-biometrics]
|
||||
(rf/dispatch [:dismiss-keyboard])
|
||||
(rf/dispatch
|
||||
[:biometric/authenticate
|
||||
{:prompt-message (i18n/label :t/biometric-auth-confirm-message)
|
||||
:on-success (fn []
|
||||
(on-close)
|
||||
(rf/dispatch [:standard-auth/on-biometric-success
|
||||
(handle-auth-success true)]))
|
||||
:on-fail (fn [error]
|
||||
(on-close)
|
||||
(log/error
|
||||
(ex-message error)
|
||||
(-> error ex-data (assoc :code (ex-cause error))))
|
||||
(when on-auth-fail (on-auth-fail error))
|
||||
(password-login {:on-press-biometrics
|
||||
#(on-press-biometrics
|
||||
on-press-biometrics)}))}]))]
|
||||
(if biometric-auth?
|
||||
(-> (biometrics/get-supported-type)
|
||||
(.then (fn [biometric-type]
|
||||
(if biometric-type
|
||||
(biometrics-login biometrics-login)
|
||||
(do
|
||||
(reset-password)
|
||||
(password-login {})))))
|
||||
(.catch #(password-login {})))
|
||||
(password-login {}))))
|
||||
@@ -4,7 +4,6 @@
|
||||
[quo.theme :as quo.theme]
|
||||
[react-native.core :as rn]
|
||||
[reagent.core :as reagent]
|
||||
[status-im.common.standard-authentication.standard-auth.authorize :as authorize]
|
||||
[status-im.constants :as constants]
|
||||
[utils.re-frame :as rf]))
|
||||
|
||||
@@ -33,14 +32,15 @@
|
||||
{:size size
|
||||
:customization-color customization-color
|
||||
:on-reset (when @reset-slider? #(reset! reset-slider? false))
|
||||
:on-complete #(authorize/authorize {:on-close on-close
|
||||
:auth-button-icon-left auth-button-icon-left
|
||||
:theme theme
|
||||
:blur? blur?
|
||||
:biometric-auth? biometric-auth?
|
||||
:on-auth-success on-auth-success
|
||||
:on-auth-fail on-auth-fail
|
||||
:auth-button-label auth-button-label})
|
||||
:on-complete #(rf/dispatch [:standard-auth/authorize
|
||||
{:on-close on-close
|
||||
:auth-button-icon-left auth-button-icon-left
|
||||
:theme theme
|
||||
:blur? blur?
|
||||
:biometric-auth? biometric-auth?
|
||||
:on-auth-success on-auth-success
|
||||
:on-auth-fail on-auth-fail
|
||||
:auth-button-label auth-button-label}])
|
||||
:track-icon (if biometric-auth? :i/face-id :password)
|
||||
:track-text track-text}]])))
|
||||
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
[quo.core :as quo]
|
||||
[react-native.core :as rn]
|
||||
[react-native.safe-area :as safe-area]
|
||||
[status-im.common.biometric.events :as biometric]
|
||||
[status-im.common.biometric.utils :as biometric]
|
||||
[status-im.common.parallax.view :as parallax]
|
||||
[status-im.common.parallax.whitelist :as whitelist]
|
||||
[status-im.common.resources :as resources]
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
(:require
|
||||
[native-module.core :as native-module]
|
||||
[re-frame.core :as re-frame]
|
||||
[status-im.common.biometric.events :as biometric]
|
||||
status-im.common.biometric.events
|
||||
[status-im.constants :as constants]
|
||||
[status-im.contexts.profile.create.events :as profile.create]
|
||||
[status-im.contexts.profile.recover.events :as profile.recover]
|
||||
@@ -32,8 +32,10 @@
|
||||
(rf/defn enable-biometrics
|
||||
{:events [:onboarding/enable-biometrics]}
|
||||
[_]
|
||||
{:biometric/authenticate {:on-success #(rf/dispatch [:onboarding/biometrics-done])
|
||||
:on-fail #(rf/dispatch [:onboarding/biometrics-fail %])}})
|
||||
{:fx [[:dispatch
|
||||
[:biometric/authenticate
|
||||
{:on-success #(rf/dispatch [:onboarding/biometrics-done])
|
||||
:on-fail #(rf/dispatch [:onboarding/biometrics-fail %])}]]]})
|
||||
|
||||
(rf/defn navigate-to-enable-notifications
|
||||
{:events [:onboarding/navigate-to-enable-notifications]}
|
||||
@@ -51,10 +53,10 @@
|
||||
[:onboarding/finalize-setup]
|
||||
[:onboarding/create-account-and-login])}))
|
||||
|
||||
(rf/defn biometrics-fail
|
||||
{:events [:onboarding/biometrics-fail]}
|
||||
[cofx code]
|
||||
(biometric/show-message cofx code))
|
||||
(rf/reg-event-fx
|
||||
:onboarding/biometrics-fail
|
||||
(fn [_ [error]]
|
||||
{:dispatch [:biometric/show-message (ex-cause error)]}))
|
||||
|
||||
(rf/defn create-account-and-login
|
||||
{:events [:onboarding/create-account-and-login]}
|
||||
|
||||
@@ -177,12 +177,14 @@
|
||||
(rf/defn login-with-biometric-if-available
|
||||
{:events [:profile.login/login-with-biometric-if-available]}
|
||||
[_ key-uid]
|
||||
{:biometric/check-if-available [key-uid
|
||||
#(rf/dispatch [:profile.login/check-biometric-success % key-uid])]})
|
||||
{:biometric/check-if-available {:key-uid key-uid
|
||||
:on-success (fn [auth-method]
|
||||
(rf/dispatch [:profile.login/check-biometric-success
|
||||
key-uid auth-method]))}})
|
||||
|
||||
(rf/defn check-biometric-success
|
||||
{:events [:profile.login/check-biometric-success]}
|
||||
[{:keys [db]} auth-method key-uid]
|
||||
[{:keys [db]} key-uid auth-method]
|
||||
(merge {:db (assoc db :auth-method auth-method)}
|
||||
(when (= auth-method keychain/auth-method-biometric)
|
||||
{:keychain/password-hash-migration
|
||||
@@ -218,7 +220,7 @@
|
||||
ex-data
|
||||
(assoc :code (ex-cause error)
|
||||
:event :profile.login/biometric-auth-fail)))
|
||||
{:dispatch [:biometric/show-message error]}))
|
||||
{:dispatch [:biometric/show-message (ex-cause error)]}))
|
||||
|
||||
(rf/defn verify-database-password
|
||||
{:events [:profile.login/verify-database-password]}
|
||||
|
||||
@@ -3,9 +3,8 @@
|
||||
[quo.theme :as quo.theme]
|
||||
[react-native.core :as rn]
|
||||
[react-native.safe-area :as safe-area]
|
||||
[status-im.common.biometric.events :as biometric]
|
||||
[status-im.common.biometric.utils :as biometric]
|
||||
[status-im.common.not-implemented :as not-implemented]
|
||||
[status-im.common.standard-authentication.standard-auth.authorize :as authorize]
|
||||
[status-im.constants :as constants]
|
||||
[status-im.contexts.profile.settings.screens.password.style :as style]
|
||||
[utils.i18n :as i18n]
|
||||
@@ -14,16 +13,19 @@
|
||||
(defn- on-press-biometric-enable
|
||||
[button-label theme]
|
||||
(fn []
|
||||
(authorize/authorize
|
||||
{:biometric-auth? false
|
||||
:blur? true
|
||||
:theme theme
|
||||
:auth-button-label (i18n/label :t/biometric-enable-button {:bio-type-label button-label})
|
||||
:on-close (fn [] (rf/dispatch [:standard-auth/reset-login-password]))
|
||||
:on-auth-success (fn [password]
|
||||
(rf/dispatch [:hide-bottom-sheet])
|
||||
(rf/dispatch [:standard-auth/reset-login-password])
|
||||
(rf/dispatch [:biometric/enable password]))})))
|
||||
(rf/dispatch
|
||||
[:standard-auth/authorize-with-password
|
||||
{:blur? true
|
||||
:theme theme
|
||||
:auth-button-label (i18n/label :t/biometric-enable-button {:bio-type-label button-label})
|
||||
:on-close (fn [] (rf/dispatch [:standard-auth/reset-login-password]))
|
||||
:on-auth-success (fn [password]
|
||||
(rf/dispatch [:hide-bottom-sheet])
|
||||
(rf/dispatch [:standard-auth/reset-login-password])
|
||||
(rf/dispatch
|
||||
[:biometric/authenticate
|
||||
{:on-success #(rf/dispatch [:biometric/enable password])
|
||||
:on-fail #(rf/dispatch [:biometric/show-message (ex-cause %)])}]))}])))
|
||||
|
||||
(defn- get-biometric-item
|
||||
[theme]
|
||||
|
||||
@@ -38,7 +38,7 @@
|
||||
{:db db/app-db
|
||||
:theme/init-theme nil
|
||||
:network/listen-to-network-info nil
|
||||
:biometric/get-supported-biometric-type nil
|
||||
:biometric/get-supported-type nil
|
||||
;;app starting flow continues in get-profiles-overview
|
||||
:profile/get-profiles-overview #(rf/dispatch [:profile/get-profiles-overview-success %])
|
||||
:effects.font/get-font-file-for-initials-avatar
|
||||
|
||||
@@ -0,0 +1,100 @@
|
||||
(ns status-im.integration-test.standard-auth-test
|
||||
(:require
|
||||
[cljs.test :refer [deftest testing is use-fixtures]]
|
||||
[day8.re-frame.test :as rf-test]
|
||||
re-frame.core
|
||||
[test-helpers.integration :as h]
|
||||
[utils.re-frame :as rf]))
|
||||
|
||||
(defn fixture-re-frame
|
||||
[]
|
||||
(let [restore-re-frame (atom nil)]
|
||||
{:before #(reset! restore-re-frame (re-frame.core/make-restore-fn))
|
||||
:after #(@restore-re-frame)}))
|
||||
|
||||
(use-fixtures :each (fixture-re-frame))
|
||||
|
||||
(def default-args
|
||||
{:on-auth-success identity
|
||||
:on-auth-fail identity
|
||||
:on-close identity
|
||||
:auth-button-label "test"
|
||||
:auth-button-icon-left :test-icon
|
||||
:blur? false
|
||||
:theme :light})
|
||||
|
||||
(deftest standard-auth-biometric-authorize-success
|
||||
(testing "calling success callback when completing biometric authentication"
|
||||
(h/log-headline :standard-auth-authorize-success)
|
||||
(rf-test/run-test-async
|
||||
(do
|
||||
(rf/reg-fx :biometric/check-if-available
|
||||
(fn [{:keys [on-success]}]
|
||||
(rf/dispatch on-success)))
|
||||
(rf/reg-event-fx :biometric/authenticate
|
||||
(fn [_ [{:keys [on-success]}]]
|
||||
(rf/dispatch on-success)))
|
||||
(rf/reg-fx :keychain/get-user-password
|
||||
(fn [[_ on-success]]
|
||||
(on-success))))
|
||||
(let [on-success-called? (atom false)
|
||||
args (assoc default-args :on-auth-success #(reset! on-success-called? true))]
|
||||
(rf/dispatch [:standard-auth/authorize args])
|
||||
(rf-test/wait-for [:standard-auth/on-biometric-success]
|
||||
(is @on-success-called?))))))
|
||||
|
||||
(deftest standard-auth-biometric-authorize-cancel
|
||||
(testing "falling back to password authorization when biometrics canceled"
|
||||
(h/log-headline :standard-auth-authorize-cancel)
|
||||
(rf-test/run-test-async
|
||||
(do
|
||||
(rf/reg-fx :biometric/check-if-available
|
||||
(fn [{:keys [on-success]}]
|
||||
(rf/dispatch on-success)))
|
||||
(rf/reg-event-fx :biometric/authenticate
|
||||
(fn [_ [{:keys [on-cancel]}]]
|
||||
(rf/dispatch on-cancel)))
|
||||
(rf/reg-event-fx :show-bottom-sheet
|
||||
(fn [_])))
|
||||
(rf/dispatch [:standard-auth/authorize default-args])
|
||||
(rf-test/wait-for [:show-bottom-sheet]
|
||||
(is true)))))
|
||||
|
||||
(deftest standard-auth-biometric-authorize-fail
|
||||
(testing "showing biometric error message when authorization failed"
|
||||
(h/log-headline :standard-auth-authorize-fail)
|
||||
(rf-test/run-test-async
|
||||
(let [on-fail-called? (atom false)
|
||||
expected-error-cause :bad-error
|
||||
error (atom nil)
|
||||
args (assoc default-args
|
||||
:on-auth-fail
|
||||
(fn [err]
|
||||
(reset! on-fail-called? true)
|
||||
(reset! error err)))]
|
||||
(rf/reg-fx :biometric/check-if-available
|
||||
(fn [{:keys [on-success]}]
|
||||
(rf/dispatch on-success)))
|
||||
(rf/reg-event-fx :biometric/authenticate
|
||||
(fn [_ [{:keys [on-fail]}]]
|
||||
(rf/dispatch (conj on-fail (ex-info "error" {} expected-error-cause)))))
|
||||
(rf/reg-fx :biometric/show-message
|
||||
(fn [_]))
|
||||
(rf/dispatch [:standard-auth/authorize args])
|
||||
(rf-test/wait-for [:biometric/show-message]
|
||||
(is @on-fail-called?)
|
||||
(is (= expected-error-cause (ex-cause @error))))))))
|
||||
|
||||
(deftest standard-auth-password-authorize-fallback
|
||||
(testing "falling back to password when biometrics is not available"
|
||||
(h/log-headline :standard-auth-password-authorize-fallback)
|
||||
(rf-test/run-test-async
|
||||
(do
|
||||
(rf/reg-fx :biometric/check-if-available
|
||||
(fn [{:keys [on-fail]}]
|
||||
(rf/dispatch on-fail)))
|
||||
(rf/reg-event-fx :show-bottom-sheet
|
||||
(fn [_])))
|
||||
(rf/dispatch [:standard-auth/authorize default-args])
|
||||
(rf-test/wait-for [:standard-auth/authorize-with-password]
|
||||
(is true)))))
|
||||
@@ -55,6 +55,7 @@
|
||||
(reg-root-key-sub :get-pairing-installations :pairing/installations)
|
||||
(reg-root-key-sub :tooltips :tooltips)
|
||||
(reg-root-key-sub :biometric/supported-type :biometric/supported-type)
|
||||
(reg-root-key-sub :biometric/auth-pending? :biometric/auth-pending?)
|
||||
(reg-root-key-sub :app-state :app-state)
|
||||
(reg-root-key-sub :home-items-show-number :home-items-show-number)
|
||||
(reg-root-key-sub :waku/v2-peer-stats :peer-stats)
|
||||
|
||||
Reference in New Issue
Block a user