Allow returning users to sign in with biometrics in Expo
Clerk's useLocalCredentials()
hook enables you to store a user's password credentials on their device and subsequently use biometrics for sign-in.
This guide shows you how to use the useLocalCredentials()
hook to enhance your user experience by allowing users to sign in using biometrics when they re-sign in to your Expo app.
These packages are required to be installed in order to use the useLocalCredentials()
hook.
terminal npm install expo-local-authentication expo-secure-store
terminal yarn add expo-local-authentication expo-secure-store
terminal pnpm add expo-local-authentication expo-secure-store
Update your app.json file as instructed in the Expo documentation:
The following example demonstrates how to use useLocalCredentials()
in a custom flow for signing users in.
app /sign-in.tsx import { useSignIn } from '@clerk/clerk-expo'
import { useLocalCredentials } from '@clerk/clerk-expo/local-credentials'
import { Link , Stack , useRouter } from 'expo-router'
import { Text , TextInput , Button , View , TouchableOpacity , StyleSheet } from 'react-native'
import React from 'react'
import { SymbolView } from 'expo-symbols'
export default function Page () {
const { signIn , setActive , isLoaded } = useSignIn ()
const router = useRouter ()
const [ emailAddress , setEmailAddress ] = React .useState ( '' )
const [ password , setPassword ] = React .useState ( '' )
const { hasCredentials , setCredentials , authenticate , biometricType } = useLocalCredentials ()
const onSignInPress = React .useCallback (
async (useLocal = false ) => {
if ( ! isLoaded) {
return
}
try {
const signInAttempt =
hasCredentials && useLocal
? await authenticate ()
: await signIn .create ({
identifier : emailAddress ,
password ,
})
if ( signInAttempt .status === 'complete' ) {
if ( ! useLocal) {
await setCredentials ({
identifier : emailAddress ,
password ,
})
}
await setActive ({ session : signInAttempt .createdSessionId })
// navigate away
} else {
// handle other statuses of sign in
}
} catch (err : any ) {
// handle any other error
}
} ,
[isLoaded , emailAddress , password] ,
)
return (
< View >
< TextInput
value = {emailAddress}
onChangeText = {(emailAddress) => setEmailAddress (emailAddress)}
/>
< TextInput value = {password} onChangeText = {(password) => setPassword (password)} />
< Button title = "Sign In" onPress = {() => onSignInPress ()} />
{hasCredentials && biometricType && (
< TouchableOpacity onPress = {() => onSignInPress ( true )}>
< SymbolView
name = {biometricType === 'face-recognition' ? 'faceid' : 'touchid' }
type = "monochrome"
/>
</ TouchableOpacity >
)}
</ View >
)
}
The following example demonstrates how to use the userOwnsCredentials
and clearCredentials
properties of the useLocalCredentials()
hook in order to remove the stored credentials if those belong to the signed in user.
app /user.tsx import { useUser , useClerk } from '@clerk/clerk-expo'
import { useLocalCredentials } from '@clerk/clerk-expo/local-credentials'
export default function Page () {
const { user } = useUser ()
const { signOut } = useClerk ()
const { userOwnsCredentials , clearCredentials } = useLocalCredentials ()
return (
< View >
< Text >Settings, { user ?.emailAddresses[ 0 ].emailAddress}</ Text >
< Button title = "Sign out" onPress = {() => signOut ()} />
{userOwnsCredentials && (
< Button title = "Remove biometric credentials" onPress = {() => clearCredentials ()} />
)}
</ View >
)
}
The following example demonstrates how to use userOwnsCredentials
and setCredentials
properties of the useLocalCredentials()
hook in order to update the stored credentials if those belong to the signed in user.
app /update-user.tsx import { useUser , useClerk } from '@clerk/clerk-expo'
import { useLocalCredentials } from '@clerk/clerk-expo/local-credentials'
export default function Page () {
const { user } = useUser ()
const [ currentPassword , setCurrentPassword ] = React .useState ( '' )
const [ password , setPassword ] = React .useState ( '' )
const { userOwnsCredentials , setCredentials } = useLocalCredentials ()
const changePassword = React .useCallback ( async () => {
try {
await user ?.updatePassword ({
currentPassword : currentPassword ,
newPassword : password ,
})
if (userOwnsCredentials) {
await setCredentials ({
password ,
})
}
} catch (err : any ) {
console .error ( JSON .stringify (err , null , 2 ))
}
} , [currentPassword , password])
return (
< View >
< TextInput
autoCapitalize = "none"
value = {currentPassword}
placeholder = "Current password..."
secureTextEntry = { true }
onChangeText = {(currentPassword) => setCurrentPassword (currentPassword)}
/>
< TextInput
value = {password}
placeholder = "Password..."
secureTextEntry = { true }
onChangeText = {(password) => setPassword (password)}
/>
< Button title = "Update password" onPress = {changePassword} />
</ View >
)
}