Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Authentication screen #58

Merged
merged 22 commits into from
Jul 7, 2021
Merged

Conversation

jrbuhl93
Copy link
Contributor

@jrbuhl93 jrbuhl93 commented Jun 20, 2021

Feature for #15

I have added biometric authentication using this library: https://github.com/hieuvp/react-native-fingerprint-scanner. The library should work with all devices according to the documentation including those that use face id instead of fingerprint and ios. I have only thoroughly tested with my device: an Android Pixel 2 that uses fingerprint authentication. If the library does not detect a biometry sensor available, the app should go into the Primary screen (untested).

The authentication in this pull request is mandatory and cannot be turned off in the settings. If you think this is something users would want, Opt-in/out authentication would require a boolean property added to the user object (on servers too) and a another item in the settings.

I hope we can thoroughly test this feature before merging it into the main codebase.

})

const checkIfBiometryIsAvailable = async () => {
const biometryIsAvailable = await isSensorAvailable()
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

are there some odds that isSensorAvailable could throw?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Reworked it to be then/catch

@jrbuhl93
Copy link
Contributor Author

jrbuhl93 commented Jun 21, 2021

I should mention https://github.com/hieuvp/react-native-fingerprint-scanner is no longer being actively maintained by the owner: hieuvp/react-native-fingerprint-scanner#178. I'm wasn't able to find an actively maintained library that seemed like a good fit. https://github.com/oblador/react-native-keychain seems maintained and GaloyMoney already has it installed, but I was running into issues with it on my Android Pixel 2.

Also, how do you think the user experience of this feature will be? I could see it being a problem if some users have dirt on their hands from working, but I haven't been to El Zonte yet. An alternate way to authenticate besides logging out and then back in might be needed.

@nicolasburtey
Copy link
Member

A quick test.

I have tried it on an iPhone X with FaceId (have not tried android yet) and the flow is a bit awkward: after login in, nothing really changes as of now.

IMG_5494

but then, when you restart the app, it's asking if you want to set up FaceId at the second launch. this is not the place to ask for FaceId setup. this should be either after a succesful login, or in the settings tab.

I have not tried the flow of clicking on "don't allow" here. something to give a look at.

also, if you log out, the faceId registration is not being reset. need to re-install the app for that.

But otherwise the faceId works as expected after it has been set up. if you don't faceId you can't log in. I think there could be a fallback with a pin number of the iOS. that would be nice to have.

Comment on lines 53 to 73
const securityAction = () => {
var isBiometricsEnabled = null
var isPinEnabled = null

RNSecureKeyStore.get("isBiometricsEnabled")
.then((res) => {
if (isPinEnabled !== null) {
navigation.navigate("security", { mIsBiometricsEnabled: true, mIsPinEnabled: isPinEnabled })
} else {
isBiometricsEnabled = true
}
}, (err) => {
if (isPinEnabled !== null) {
navigation.navigate("security", { mIsBiometricsEnabled: false, mIsPinEnabled: isPinEnabled })
} else {
isBiometricsEnabled = false
}
})

RNSecureKeyStore.get("PIN")
.then((res) => {
if (isBiometricsEnabled !== null) {
navigation.navigate("security", { mIsBiometricsEnabled: isBiometricsEnabled, mIsPinEnabled: true })
} else {
isPinEnabled = true
}
}, (err) => {
if (isBiometricsEnabled !== null) {
navigation.navigate("security", { mIsBiometricsEnabled: isBiometricsEnabled, mIsPinEnabled: false })
} else {
isPinEnabled = false
}
})

}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Let's make this an util function in case we need this check somewhere else in the future

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This sounds like pre-mature optimisation.

in case we need

I think its fine to keep it in the this file as long as its the only place we're using it.

Not a big deal though.

Copy link
Member

@bodymindarts bodymindarts left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks good over all.... see comments for small cleanup suggestions.

app/screens/authentication-screen/pin-screen.tsx Outdated Show resolved Hide resolved
app/screens/authentication-screen/pin-screen.tsx Outdated Show resolved Hide resolved
app/screens/settings-screen/security-screen.tsx Outdated Show resolved Hide resolved
const [isPinEnabled, setIsPinEnabled] = useState(mIsPinEnabled)

useFocusEffect(() => {
RNSecureKeyStore.get("isBiometricsEnabled")
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can these strings "PIN" and "isBiometricsEnabled" be extracted into const variables?

app/screens/settings-screen/security-screen.tsx Outdated Show resolved Hide resolved
app/screens/settings-screen/settings-screen.tsx Outdated Show resolved Hide resolved
Comment on lines 53 to 73
const securityAction = () => {
var isBiometricsEnabled = null
var isPinEnabled = null

RNSecureKeyStore.get("isBiometricsEnabled")
.then((res) => {
if (isPinEnabled !== null) {
navigation.navigate("security", { mIsBiometricsEnabled: true, mIsPinEnabled: isPinEnabled })
} else {
isBiometricsEnabled = true
}
}, (err) => {
if (isPinEnabled !== null) {
navigation.navigate("security", { mIsBiometricsEnabled: false, mIsPinEnabled: isPinEnabled })
} else {
isBiometricsEnabled = false
}
})

RNSecureKeyStore.get("PIN")
.then((res) => {
if (isBiometricsEnabled !== null) {
navigation.navigate("security", { mIsBiometricsEnabled: isBiometricsEnabled, mIsPinEnabled: true })
} else {
isPinEnabled = true
}
}, (err) => {
if (isBiometricsEnabled !== null) {
navigation.navigate("security", { mIsBiometricsEnabled: isBiometricsEnabled, mIsPinEnabled: false })
} else {
isPinEnabled = false
}
})

}
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This sounds like pre-mature optimisation.

in case we need

I think its fine to keep it in the this file as long as its the only place we're using it.

Not a big deal though.

app/utils/biometricAuthentication.ts Outdated Show resolved Hide resolved
app/utils/logout.ts Outdated Show resolved Hide resolved
app/utils/logout.ts Outdated Show resolved Hide resolved
@bodymindarts
Copy link
Member

Actually had a thought regarding the accesses with magic strings ie: RNSecureKeyStore.get("PIN"). Perhaps you could wrap access to RNSecureKeyStore. In a dedicated object and use it via:

KeyStoreWrapper.getPin()

I think that would make it cleaner all together. Group all functions regarding that KeyStore in a wrapper.

@bodymindarts bodymindarts self-requested a review June 27, 2021 18:46
@jrbuhl93 jrbuhl93 force-pushed the authentication-screen branch from 7f7fd29 to 2e22710 Compare June 29, 2021 22:17
export const AuthenticationCheckScreen: ScreenType = ({ navigation }: Props) => {
useFocusEffect(() => {
checkForAuthentication()
})
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

  useEffect(() => {
    checkForAuthentication()
  }, [])

checkForAuthentication()
})

const checkForAuthentication = async () => {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

put function within useEffect scope

navigation: any,
}

export const AuthenticationScreen: ScreenType = ({ route, navigation }: Props) => {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

add type related to route.params

attemptAuthentication()
})

const attemptAuthentication = () => {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

async?

Copy link
Contributor Author

@jrbuhl93 jrbuhl93 Jul 2, 2021

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't think attemptAuthentication needs to be async even though BiometricWrapper.authenticate is async. Unless we want to await a return value of BiometricWrapper.authenticate instead of using callbacks. Is there another reason to declare attemptAuthentication async?

} else {
description = translate("AuthenticationScreen.setUpAuthenticationDescription")
}
BiometricWrapper.authenticate(description, handleAuthenticationSuccess, handleAuthenticationFailure)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

add 1 line comment on what this does

]
})
} else {
if (pinAttempts < 2) {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

should be const outside of the loop

category: translate('common.security'),
icon: 'lock-closed-outline',
id: 'security',
action: () => securityAction(),
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

action: securityAction

@jrbuhl93 jrbuhl93 force-pushed the authentication-screen branch from 79af9a7 to ff913f0 Compare July 6, 2021 18:12
@nicolasburtey
Copy link
Member

some text cropped on iOS:

image

@nicolasburtey
Copy link
Member

App running in the background. When coming back to the app, not asking for faceId again. But I think it ask back FaceId because the app can stay in the background for a long time.

@nicolasburtey
Copy link
Member

nicolasburtey commented Jul 7, 2021

If I go to setPin, I am being stuck on this screen. I can put 4 digits and nothing happens.

IMG_5A7B11B86475-1

@nicolasburtey
Copy link
Member

after too many attempt of bad pin, the "too many attempt to login, log out" text appeared maybe only for 0.2 seconds. I could barely see it before the screen had reset.

@jrbuhl93
Copy link
Contributor Author

jrbuhl93 commented Jul 7, 2021

Would you like me to add authentication when the device goes from background -> active. I have not implemented this yet, only when the app starts from terminated. Also do you think the best way to handle the "too many attempt to login, log out" is to add an await sleep(1000)?

@nicolasburtey
Copy link
Member

Would you like me to add authentication when the device goes from background -> active. I have not implemented this yet, only when the app starts from terminated

yes. this could be done as a follow up PR.

@nicolasburtey
Copy link
Member

Also do you think the best way to handle the "too many attempt to login, log out" is to add an await sleep(1000)?

can be a good option yes. or it could be a popup. and when you click on ok you're being redirected at this stage.

@nicolasburtey
Copy link
Member

If I go to setPin, I am being stuck on this screen. I can put 4 digits and nothing happens.

I have this issue on Android as well. this is the biggest issue I see so far on my end.

@nicolasburtey nicolasburtey merged commit 44beaef into GaloyMoney:main Jul 7, 2021
@jrbuhl93 jrbuhl93 deleted the authentication-screen branch July 8, 2021 17:08
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

4 participants