Clean up biometrics and standard-auth and add tests (#18756)
* ref(biometric): events using reg-event-fx & other * test(biometric-events): show-message Signed-off-by: Cristian Lungu <lungucristian95@gmail.com> * fix(biometrics): removed unnecessary event Signed-off-by: Cristian Lungu <lungucristian95@gmail.com> * test(biometrics): tests & schemas for events Signed-off-by: Cristian Lungu <lungucristian95@gmail.com> * ref(standard-auth): refactored authorize Signed-off-by: Cristian Lungu <lungucristian95@gmail.com> * test(biometrics): fixed event tests Signed-off-by: Cristian Lungu <lungucristian95@gmail.com> * ref(standard-auth): authorize using events * feat(standard-auth): schemas and removed old auth Signed-off-by: Cristian Lungu <lungucristian95@gmail.com> * test(standard-auth): added integration tests Signed-off-by: Cristian Lungu <lungucristian95@gmail.com> * feat(biometric): added missing schema Signed-off-by: Cristian Lungu <lungucristian95@gmail.com> * ref(biometrics): moved schemas to a separate file Signed-off-by: Cristian Lungu <lungucristian95@gmail.com> * fix(biometric): pressing biometric quickly errors Signed-off-by: Cristian Lungu <lungucristian95@gmail.com> * fix: removed namespaced keywords for errors Signed-off-by: Cristian Lungu <lungucristian95@gmail.com> * ref: removed the use of vector cb for biometric fx Signed-off-by: Cristian Lungu <lungucristian95@gmail.com> * fix: removed :maybe from fx schema Signed-off-by: Cristian Lungu <lungucristian95@gmail.com> * ref: remove event-fx schema Signed-off-by: Cristian Lungu <lungucristian95@gmail.com> * feat: added nested biometrics db keys * fix: using ExceptionInfo for the error schema Signed-off-by: Cristian Lungu <lungucristian95@gmail.com> * test: removed clearing fixture and small refactor Signed-off-by: Cristian Lungu <lungucristian95@gmail.com> * fix: reset db password inside standard-authentication Signed-off-by: Cristian Lungu <lungucristian95@gmail.com> * ref: use naming convention for effects Signed-off-by: Cristian Lungu <lungucristian95@gmail.com> * ref: renamed not-canceled? to success? Signed-off-by: Cristian Lungu <lungucristian95@gmail.com> * rev: removed biometrics schemas :( Signed-off-by: Cristian Lungu <lungucristian95@gmail.com> * fix: removed set-in & fix on-close bug Signed-off-by: Cristian Lungu <lungucristian95@gmail.com> * fix: onboarding biometric not triggered Signed-off-by: Cristian Lungu <lungucristian95@gmail.com> * fix: invalid props bug on onboarding welcome Signed-off-by: Cristian Lungu <lungucristian95@gmail.com> * fix: moved side effect cb call into effect Signed-off-by: Cristian Lungu <lungucristian95@gmail.com> * test: moved integration test to new location Signed-off-by: Cristian Lungu <lungucristian95@gmail.com> * fix: password input theme Signed-off-by: Cristian Lungu <lungucristian95@gmail.com> * fix: disabled biometric not triggering fail when checking biometric Signed-off-by: Cristian Lungu <lungucristian95@gmail.com> --------- Signed-off-by: Cristian Lungu <lungucristian95@gmail.com>
This commit is contained in:
parent
c57e5cd6db
commit
637efa24cf
|
@ -37,7 +37,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]
|
||||
|
@ -120,7 +119,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
|
||||
|
@ -142,7 +141,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]}]
|
||||
|
|
|
@ -13,15 +13,14 @@
|
|||
(native-module/logout)))
|
||||
|
||||
(rf/defn initialize-app-db
|
||||
[{{:keys [keycard initials-avatar-font-file]
|
||||
:biometric/keys [supported-type]
|
||||
:network/keys [type]}
|
||||
[{{:keys [keycard initials-avatar-font-file biometrics]
|
||||
:network/keys [type]}
|
||||
:db}]
|
||||
{:db (assoc db/app-db
|
||||
:network/type type
|
||||
:initials-avatar-font-file initials-avatar-font-file
|
||||
:keycard (dissoc keycard :secrets :pin :application-info)
|
||||
:biometric/supported-type supported-type
|
||||
:biometrics biometrics
|
||||
:syncing nil)})
|
||||
|
||||
(rf/defn logout-method
|
||||
|
|
|
@ -39,13 +39,13 @@
|
|||
[message]
|
||||
(let [cause (if platform/android?
|
||||
(condp = message
|
||||
android-not-enrolled-error-message ::not-enrolled
|
||||
android-not-available-error-message ::not-available
|
||||
::unknown)
|
||||
android-not-enrolled-error-message :biometrics/not-enrolled-error
|
||||
android-not-available-error-message :biometrics/not-available-error
|
||||
:biometrics/unknown-error)
|
||||
|
||||
(condp #(string/includes? %2 %1) message
|
||||
ios-not-enrolled-error-message ::not-enrolled
|
||||
::unknown))]
|
||||
ios-not-enrolled-error-message :biometrics/not-enrolled-error
|
||||
:biometrics/unknown-error))]
|
||||
(ex-info "Failed to authenticate with biometrics"
|
||||
{:orig-error-message message}
|
||||
cause)))
|
||||
|
|
|
@ -29,10 +29,15 @@
|
|||
[:on-success [:or fn? [:cat keyword? [:* :any]]]]
|
||||
[:on-error [:or fn? [:cat keyword? [:* :any]]]]]])
|
||||
|
||||
(def ^:private ?exception
|
||||
[:fn {:error/message "schema.common/exception should be of type ExceptionInfo"}
|
||||
(fn [v] (instance? ExceptionInfo 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 ::exception ?exception))
|
||||
|
|
|
@ -0,0 +1,72 @@
|
|||
(ns status-im.common.biometric.effects
|
||||
(:require
|
||||
[react-native.biometrics :as biometrics]
|
||||
[status-im.common.biometric.utils :as utils]
|
||||
[status-im.common.keychain.events :as keychain]
|
||||
[status-im.constants :as constant]
|
||||
[taoensso.timbre :as log]
|
||||
[utils.i18n :as i18n]
|
||||
[utils.re-frame :as rf]))
|
||||
|
||||
(rf/reg-fx
|
||||
:effects.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 utils/android-device-blacklisted?))
|
||||
(-> (biometrics/get-supported-type)
|
||||
(.then (fn [type]
|
||||
(rf/dispatch [:biometric/set-supported-type type])))))))))
|
||||
|
||||
(rf/reg-fx
|
||||
:effects.biometric/authenticate
|
||||
(fn [{:keys [prompt-message on-success on-fail on-cancel on-done]
|
||||
:or {on-done identity
|
||||
on-success identity
|
||||
on-cancel identity
|
||||
on-fail identity}}]
|
||||
(-> (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 [success?]
|
||||
(on-done)
|
||||
(if success?
|
||||
(on-success)
|
||||
(on-cancel))))
|
||||
(.catch (fn [err]
|
||||
(on-done)
|
||||
(on-fail err))))))
|
||||
|
||||
(rf/reg-fx
|
||||
:effects.biometric/check-if-available
|
||||
(fn [{:keys [key-uid on-success on-fail]
|
||||
:or {on-success identity
|
||||
on-fail identity}}]
|
||||
(keychain/can-save-user-password?
|
||||
(fn [can-save?]
|
||||
(if-not can-save?
|
||||
(on-fail (ex-info "cannot-save-user-password"
|
||||
{:effect :effects.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]
|
||||
(if (= auth-method constant/auth-method-biometric)
|
||||
(on-success auth-method)
|
||||
(throw (js/Error. "biometric-not-enabled")))))
|
||||
(.catch (fn [err]
|
||||
(let [message (.-message err)]
|
||||
(on-fail (ex-info message
|
||||
{:err err
|
||||
:effect :effects.biometric/check-if-available}))
|
||||
(when-not (or (= message "biometric-not-available")
|
||||
(= message "biometric-not-enabled"))
|
||||
(log/error "Failed to check if biometrics is available"
|
||||
{:error err
|
||||
:key-uid key-uid
|
||||
:effect :effects.biometric/check-biometric})))))))))))
|
|
@ -1,120 +1,62 @@
|
|||
(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]
|
||||
[status-im.common.keychain.events :as keychain]
|
||||
status-im.common.biometric.effects
|
||||
[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 set-supported-type
|
||||
[{:keys [db]} [supported-type]]
|
||||
{:db (assoc-in db [:biometrics :supported-type] supported-type)})
|
||||
|
||||
(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)))
|
||||
(rf/reg-event-fx :biometric/set-supported-type set-supported-type)
|
||||
|
||||
(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
|
||||
(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?))
|
||||
(-> (biometrics/get-supported-type)
|
||||
(.then (fn [type]
|
||||
(rf/dispatch [:biometric/get-supported-biometric-type-success type])))))))))
|
||||
|
||||
(rf/defn get-supported-biometric-auth-success
|
||||
{:events [:biometric/get-supported-biometric-type-success]}
|
||||
[{: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
|
||||
::biometrics/not-available}
|
||||
(defn show-message
|
||||
[_ [code]]
|
||||
(let [content (if (#{:biometrics/not-enrolled-error
|
||||
:biometrics/not-available-error}
|
||||
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}]]}))
|
||||
|
||||
(rf/reg-event-fx :biometric/show-message show-message)
|
||||
|
||||
(re-frame/reg-fx
|
||||
:biometric/authenticate
|
||||
(fn [{:keys [on-success on-fail prompt-message]}]
|
||||
(-> (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))))
|
||||
(.catch (fn [err]
|
||||
(when on-fail
|
||||
(on-fail err)))))))
|
||||
(defn on-authentication-done
|
||||
[{:keys [db]}]
|
||||
{:db (assoc-in db [:biometrics :auth-pending?] false)})
|
||||
|
||||
(rf/defn authenticate
|
||||
{:events [:biometric/authenticate]}
|
||||
[_ opts]
|
||||
{:biometric/authenticate opts})
|
||||
(rf/reg-event-fx :biometric/on-authentication-done on-authentication-done)
|
||||
|
||||
(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}]})))
|
||||
(defn authenticate
|
||||
[{:keys [db]} [opts]]
|
||||
(let [pending? (get-in db [:biometrics :auth-pending?])]
|
||||
;;NOTE: prompting biometric check while another one is pending triggers error
|
||||
(when-not pending?
|
||||
{:db (assoc-in db [:biometrics :auth-pending?] true)
|
||||
:fx [[:effects.biometric/authenticate
|
||||
(assoc opts :on-done #(rf/dispatch [:biometric/on-authentication-done]))]]})))
|
||||
|
||||
(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 %])}]}))
|
||||
(rf/reg-event-fx :biometric/authenticate authenticate)
|
||||
|
||||
(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})))
|
||||
(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/enable enable-biometrics)
|
||||
|
||||
(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]]}))
|
||||
|
||||
(rf/reg-event-fx :biometric/disable disable-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}))))))))))
|
||||
|
|
|
@ -0,0 +1,68 @@
|
|||
(ns status-im.common.biometric.events-test
|
||||
(:require [cljs.test :refer [deftest testing is]]
|
||||
matcher-combinators.test
|
||||
[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-in (:db cofx) [:biometrics :supported-type] supported-type)}]
|
||||
(is (match? expected (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-error])))))
|
||||
|
||||
(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 {:biometrics {: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]))))))
|
||||
|
||||
(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,22 @@
|
|||
(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]))
|
||||
|
||||
(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))
|
|
@ -1,11 +1,93 @@
|
|||
(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 [[:effects.biometric/check-if-available
|
||||
{:key-uid key-uid
|
||||
:on-success #(rf/dispatch [:standard-auth/authorize-with-biometric args])
|
||||
:on-fail #(rf/dispatch [: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 #(rf/dispatch [:standard-auth/authorize-with-password
|
||||
args-with-biometric-btn])
|
||||
:on-success #(rf/dispatch [:standard-auth/on-biometric-success on-auth-success])
|
||||
:on-fail (fn [err]
|
||||
(when on-auth-fail (on-auth-fail err))
|
||||
(rf/dispatch [:standard-auth/on-biometric-fail err]))}]]]}))
|
||||
|
||||
(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]]
|
||||
[:dispatch [:standard-auth/reset-login-password]]]}))
|
||||
|
||||
(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
|
||||
[_ [error]]
|
||||
(log/error (ex-message error)
|
||||
(-> error
|
||||
ex-data
|
||||
(assoc :code (ex-cause error)
|
||||
:event :standard-auth/on-biometric-fail)))
|
||||
{:fx [[:dispatch [:standard-auth/reset-login-password]]
|
||||
[: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]
|
||||
(rf/dispatch [:standard-auth/reset-login-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 (fn []
|
||||
(rf/dispatch [:standard-auth/reset-login-password])
|
||||
(when 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]]]
|
||||
:any])
|
||||
|
||||
(def ?authorize-with-biometric
|
||||
[:=>
|
||||
[:catn
|
||||
[:cofx :schema.re-frame/cofx]
|
||||
[:args
|
||||
[:tuple ?authorize-map]]]
|
||||
:any])
|
||||
|
||||
(def ?on-biometric-success
|
||||
[:=>
|
||||
[:catn
|
||||
[:cofx :schema.re-frame/cofx]
|
||||
[:args
|
||||
[:tuple fn?]]]
|
||||
:any])
|
||||
|
||||
(def ?on-biometrics-fail
|
||||
[:=>
|
||||
[:catn
|
||||
[:cofx :schema.re-frame/cofx]
|
||||
[:args
|
||||
[:tuple
|
||||
[:maybe fn?]
|
||||
[:maybe :schema.common/exception]]]]
|
||||
:any])
|
||||
|
||||
(def ?authorize-with-password
|
||||
[:=>
|
||||
[:catn
|
||||
[:cofx :schema.re-frame/cofx]
|
||||
[:args
|
||||
[:tuple ?authorize-map]]]
|
||||
:any])
|
|
@ -56,6 +56,7 @@
|
|||
error? (boolean (seq error-message))
|
||||
default-value (rn/use-ref-atom "") ;;bug on Android
|
||||
;;https://github.com/status-im/status-mobile/issues/19004
|
||||
theme (quo.theme/use-theme-value)
|
||||
on-change-password (rn/use-callback
|
||||
(fn [entered-password]
|
||||
(reset! default-value entered-password)
|
||||
|
@ -69,6 +70,7 @@
|
|||
[quo/input
|
||||
{:container-style {:flex 1}
|
||||
:type :password
|
||||
:theme theme
|
||||
:default-value @default-value
|
||||
:blur? blur?
|
||||
:disabled? processing
|
||||
|
|
|
@ -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 {}))))
|
|
@ -3,7 +3,6 @@
|
|||
[quo.core :as quo]
|
||||
[quo.theme :as quo.theme]
|
||||
[react-native.core :as rn]
|
||||
[status-im.common.standard-authentication.standard-auth.authorize :as authorize]
|
||||
[status-im.constants :as constants]
|
||||
[utils.re-frame :as rf]))
|
||||
|
||||
|
@ -16,14 +15,15 @@
|
|||
biometric-auth? (= auth-method constants/auth-method-biometric)
|
||||
on-complete (rn/use-callback
|
||||
(fn [reset]
|
||||
(authorize/authorize {:on-close #(js/setTimeout reset 200)
|
||||
: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}))
|
||||
(rf/dispatch [:standard-auth/authorize
|
||||
{:on-close #(js/setTimeout reset 200)
|
||||
: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}]))
|
||||
[theme])]
|
||||
[quo/slide-button
|
||||
{:container-style container-style
|
||||
|
|
|
@ -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.blacklist :as blacklist]
|
||||
[status-im.common.parallax.view :as parallax]
|
||||
[status-im.common.resources :as resources]
|
||||
|
@ -24,7 +24,7 @@
|
|||
|
||||
(defn enable-biometrics-buttons
|
||||
[insets]
|
||||
(let [supported-biometric-type (rf/sub [:biometric/supported-type])
|
||||
(let [supported-biometric-type (rf/sub [:biometrics/supported-type])
|
||||
bio-type-label (biometric/get-label-by-type supported-biometric-type)
|
||||
profile-color (or (:color (rf/sub [:onboarding/profile]))
|
||||
(rf/sub [:profile/customization-color]))
|
||||
|
|
|
@ -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]}
|
||||
|
@ -85,7 +87,7 @@
|
|||
(rf/defn password-set
|
||||
{:events [:onboarding/password-set]}
|
||||
[{:keys [db]} password]
|
||||
(let [supported-type (:biometric/supported-type db)]
|
||||
(let [supported-type (get-in db [:biometrics :supported-type])]
|
||||
{:db (-> db
|
||||
(assoc-in [:onboarding/profile :password] password)
|
||||
(assoc-in [:onboarding/profile :auth-method] constants/auth-method-password))
|
||||
|
@ -96,7 +98,7 @@
|
|||
(rf/defn navigate-to-enable-biometrics
|
||||
{:events [:onboarding/navigate-to-enable-biometrics]}
|
||||
[{:keys [db]}]
|
||||
(let [supported-type (:biometric/supported-type db)]
|
||||
(let [supported-type (get-in db [:biometrics :supported-type])]
|
||||
{:dispatch (if supported-type
|
||||
[:open-modal :enable-biometrics]
|
||||
[:open-modal :enable-notifications])}))
|
||||
|
|
|
@ -9,9 +9,7 @@
|
|||
|
||||
(defn page-illustration
|
||||
[width]
|
||||
{:resize-mode :stretch
|
||||
:resize-method :scale
|
||||
:width width
|
||||
{:width width
|
||||
:margin-top 12
|
||||
:margin-bottom 4})
|
||||
|
||||
|
|
|
@ -45,8 +45,10 @@
|
|||
:on-press #(rf/dispatch [:navigate-back-within-stack :enable-notifications])}]
|
||||
[page-title]
|
||||
[rn/image
|
||||
{:style (style/page-illustration (:width window))
|
||||
:source (resources/get-image :welcome-illustration)}]
|
||||
{:style (style/page-illustration (:width window))
|
||||
:resize-mode :stretch
|
||||
:resize-method :scale
|
||||
:source (resources/get-image :welcome-illustration)}]
|
||||
[rn/view {:style (style/buttons insets)}
|
||||
(when rn/small-screen?
|
||||
[linear-gradient/linear-gradient
|
||||
|
|
|
@ -177,12 +177,15 @@
|
|||
(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])]})
|
||||
{:effects.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 +221,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,21 +13,22 @@
|
|||
(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-auth-success (fn [password]
|
||||
(rf/dispatch [:hide-bottom-sheet])
|
||||
(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]
|
||||
(let [auth-method (rf/sub [:auth-method])
|
||||
biometric-type (rf/sub [:biometric/supported-type])
|
||||
biometric-type (rf/sub [:biometrics/supported-type])
|
||||
label (biometric/get-label-by-type biometric-type)
|
||||
icon (biometric/get-icon-by-type biometric-type)
|
||||
supported? (boolean biometric-type)
|
||||
|
@ -45,7 +45,7 @@
|
|||
:action-props {:disabled? (not supported?)
|
||||
:on-change press-handler
|
||||
:checked? biometric-on?}
|
||||
:on-press press-handler}))
|
||||
:on-press (when supported? press-handler)}))
|
||||
|
||||
(defn- get-change-password-item
|
||||
[]
|
||||
|
|
|
@ -39,7 +39,7 @@
|
|||
{:db db/app-db
|
||||
:theme/init-theme nil
|
||||
:network/listen-to-network-info nil
|
||||
:biometric/get-supported-biometric-type nil
|
||||
:effects.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,8 @@
|
|||
(ns status-im.subs.biometrics
|
||||
(:require [re-frame.core :as rf]))
|
||||
|
||||
(rf/reg-sub
|
||||
:biometrics/supported-type
|
||||
:<- [:biometrics]
|
||||
(fn [biometrics]
|
||||
(get biometrics :supported-type)))
|
|
@ -3,6 +3,7 @@
|
|||
[re-frame.core :as re-frame]
|
||||
status-im.subs.activity-center
|
||||
status-im.subs.alert-banner
|
||||
status-im.subs.biometrics
|
||||
status-im.subs.chats
|
||||
status-im.subs.communities
|
||||
status-im.subs.contact
|
||||
|
@ -57,7 +58,6 @@
|
|||
(reg-root-key-sub :networks/manage :networks/manage)
|
||||
(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 :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)
|
||||
|
@ -159,6 +159,9 @@
|
|||
;;wallet
|
||||
(reg-root-key-sub :wallet :wallet)
|
||||
|
||||
;;biometrics
|
||||
(reg-root-key-sub :biometrics :biometrics)
|
||||
|
||||
;;debug
|
||||
(when js/goog.DEBUG
|
||||
(reg-root-key-sub :dev/previewed-component :dev/previewed-component))
|
||||
|
|
|
@ -0,0 +1,94 @@
|
|||
(ns tests.integration-test.standard-auth-test
|
||||
(:require
|
||||
[cljs.test :refer [deftest testing is]]
|
||||
[day8.re-frame.test :as rf-test]
|
||||
re-frame.core
|
||||
[test-helpers.integration :as h]
|
||||
[utils.re-frame :as rf]))
|
||||
|
||||
(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})
|
||||
|
||||
(defn auth-success-fixtures
|
||||
[]
|
||||
(rf/reg-fx :effects.biometric/check-if-available
|
||||
(fn [{:keys [on-success]}] (on-success)))
|
||||
(rf/reg-event-fx :biometric/authenticate
|
||||
(fn [_ [{:keys [on-success]}]] (on-success)))
|
||||
(rf/reg-fx :keychain/get-user-password
|
||||
(fn [[_ on-success]] (on-success))))
|
||||
|
||||
(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
|
||||
(auth-success-fixtures)
|
||||
(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?))))))
|
||||
|
||||
(defn auth-cancel-fixtures
|
||||
[]
|
||||
(rf/reg-fx :effects.biometric/check-if-available
|
||||
(fn [{:keys [on-success]}] (on-success)))
|
||||
(rf/reg-event-fx :biometric/authenticate
|
||||
(fn [_ [{:keys [on-cancel]}]] (on-cancel)))
|
||||
(rf/reg-event-fx :show-bottom-sheet identity))
|
||||
|
||||
(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
|
||||
(auth-cancel-fixtures)
|
||||
(rf/dispatch [:standard-auth/authorize default-args])
|
||||
(rf-test/wait-for [:show-bottom-sheet]
|
||||
(is true)))))
|
||||
|
||||
(defn auth-fail-fixtures
|
||||
[expected-error-cause]
|
||||
(rf/reg-fx :effects.biometric/check-if-available
|
||||
(fn [{:keys [on-success]}] (on-success)))
|
||||
(rf/reg-event-fx :biometric/authenticate
|
||||
(fn [_ [{:keys [on-fail]}]] (on-fail (ex-info "error" {} expected-error-cause))))
|
||||
(rf/reg-event-fx :biometric/show-message identity))
|
||||
|
||||
(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)))]
|
||||
(auth-fail-fixtures expected-error-cause)
|
||||
(rf/dispatch [:standard-auth/authorize args])
|
||||
(rf-test/wait-for [:biometric/show-message]
|
||||
(is @on-fail-called?)
|
||||
(is (= expected-error-cause (ex-cause @error))))))))
|
||||
|
||||
(defn auth-password-fallback-fixtures
|
||||
[]
|
||||
(rf/reg-fx :effects.biometric/check-if-available
|
||||
(fn [{:keys [on-fail]}] (on-fail)))
|
||||
(rf/reg-event-fx :show-bottom-sheet identity))
|
||||
|
||||
(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
|
||||
(auth-password-fallback-fixtures)
|
||||
(rf/dispatch [:standard-auth/authorize default-args])
|
||||
(rf-test/wait-for [:standard-auth/authorize-with-password]
|
||||
(is true)))))
|
Loading…
Reference in New Issue