In this tutorial, we will show you how you can store user credentials in a React Native app securely and use those credentials to authenticate the user.
When you are implementing your login system in an app, you need a way to save the login credentials. For example, you may want to save a user login token or refresh token to avoid re-entering a username and password whenever users open the app.
So the question is, where we can save the login credentials? Some will say, “Let’s save this info in the AsyncStorage
,” but trust me, you should not save critical information in a simple, plain, and unsecured environment.
To store the information securely natively, you should use react-native-keychain.
Want to know how to use react-native-keychain in your React Native app? To understand something, the best thing is to learn by example.
So we are going to make a small, basic app that mimics login flow, and it will show you how to save tokens securely and use a token to authenticate the user.
First, let’s build a basic UI for the login screen. See below the image for how our login screen will look.
A pretty basic login UI with a dark theme (P.S.: I love dark themes 😉 ).
Here is the code for this UI:
// App.js import React from "react"; import { StyleSheet, Text, TextInput, View, Dimensions } from "react-native"; export default function App() { return ( <View style={styles.container}> <View> <Text style={styles.helloText}>Hello There!</Text> <TextInput placeholder="email" style={styles.textInput} /> <TextInput placeholder="password" secureTextEntry style={styles.textInput} /> <Text style={styles.loginBtn}> Login </Text> </View> </View> ); } const screenWidth = Dimensions.get("screen").width; const styles = StyleSheet.create({ container: { flex: 1, backgroundColor: "#1f1f1f", alignItems: "center", paddingTop: 250, }, helloText: { color: "white", marginBottom: 20, fontSize: 30, }, textInput: { padding: 5, paddingStart: 15, backgroundColor: "#3b3b3b", width: screenWidth * 0.8, borderRadius: 25, marginBottom: 15, color: "white", fontWeight: "600", }, loginBtn: { paddingHorizontal: 25, paddingVertical: 10, backgroundColor: "#ff1178", borderRadius: 25, color: "white", textAlign: "center", } });
We have the login UI; now, let’s install the keychain to securely store login credentials.
To install, run this command in your project’s root:
yarn add react-native-keychain
Or, if you use npm:
npm i -S react-native-keychain
After installing the react-native-keychain, we can continue to implement the login system.
This keychain provides many methods that we can use in various scenarios, but for the sake of simplicity, we will use three main methods to save, retrieve, and delete credentials.
These methods are:
setGenericPassword
getGenericPassword
resetGenericPassword
It is self-explanatory what these methods do. But, I will give a brief overview.
To store the credentials, we use the setGenericPassword
method and pass two arguments. The first is a username and the second argument is a token or password.
To read the values of stored credentials, we can use the getGenericPassword
method without any arguments.
Lastly, to remove the credentials, we can use the resetGenericPassword
method without any argument.
Let’s store the user credentials:
// App.js import React, { useEffect, useState } from "react"; import { StyleSheet, Text, TextInput, View, Dimensions } from "react-native"; import * as Keychain from "react-native-keychain"; export default function App() { const [isLoggedIn, setIsLoggedIn] = useState(false); const [userDetails, setUserDetails] = useState({}); const handleLogin = async () => { // login api call here const token = "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiaWF0IjoxNTE2MjM5MDIyfQ.SflKxwRJSMeKKF2QT4fwpMeJf36POk6yJV_adQssw5c"; const username = "Akshay"; await Keychain.setGenericPassword(username, token); setIsLoggedIn(true); setUserDetails({token, username}); }; return ( <View style={styles.container}> <View> <Text style={styles.helloText}>Hello There!</Text> <TextInput placeholder="email" style={styles.textInput} /> <TextInput placeholder="password" secureTextEntry style={styles.textInput} /> <Text style={styles.loginBtn} onPress={handleLogin}> Login </Text> </View> </View> ); } const screenWidth = Dimensions.get("screen").width; const styles = StyleSheet.create({ container: { flex: 1, backgroundColor: "#1f1f1f", alignItems: "center", // justifyContent: 'center', paddingTop: 250, }, helloText: { color: "white", marginBottom: 20, fontSize: 30, }, textInput: { padding: 5, paddingStart: 15, backgroundColor: "#3b3b3b", width: screenWidth * 0.8, borderRadius: 25, marginBottom: 15, color: "white", fontWeight: "600", }, loginBtn: { paddingHorizontal: 25, paddingVertical: 10, backgroundColor: "#ff1178", borderRadius: 25, color: "white", textAlign: "center", } });
As you can see in the above code, I have created a handleLogin
function and set it to the login text’s onPress
.
In the handleLogin
function, we haven’t implemented a full login system but we are assuming that the user is authorized and the login server returned user details and a token.
So, we will save this information in secure storage.
Note that we are saving the user token, not the password itself. It is not a good practice to save user passwords on the client side. Always go with a token with some expiry.
For saving the information, we are using the setGenericPassword
method and passing the username and token as arguments.
After successfully saving, we are updating our states to change the login screen to the welcome screen.
After login, we will show a welcome screen with the username. Our welcome screen will look like this:
Just a welcome message followed by the username and a logout button.
Based on the state change of isLoggedIn
, we will show a welcome UI and hide the login UI.
Our code will look like this:
// App.js import React, { useEffect, useState } from "react"; import { StyleSheet, Text, TextInput, View, Dimensions } from "react-native"; import * as Keychain from "react-native-keychain"; export default function App() { const [isLoggedIn, setIsLoggedIn] = useState(false); const [userDetails, setUserDetails] = useState({}); useEffect(() => { (async () => { try { const credentials = await Keychain.getGenericPassword(); if (credentials) { setIsLoggedIn(true); setUserDetails(credentials); } else { console.log("No credentials stored"); } } catch (error) { console.log("Keychain couldn't be accessed!", error); } })(); }, []); const handleLogin = async () => { // login api call here const token = "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiaWF0IjoxNTE2MjM5MDIyfQ.SflKxwRJSMeKKF2QT4fwpMeJf36POk6yJV_adQssw5c"; const username = "Akshay"; await Keychain.setGenericPassword(username, token); setIsLoggedIn(true); setUserDetails({token, username}); }; const handleLogout = async()=>{ const logout = await Keychain.resetGenericPassword(); console.log({logout}); if(logout){ setIsLoggedIn(false); setUserDetails({}); } } return ( <View style={styles.container}> {!isLoggedIn ? ( <View> <Text style={styles.helloText}>Hello There!</Text> <TextInput placeholder="email" style={styles.textInput} /> <TextInput placeholder="password" secureTextEntry style={styles.textInput} /> <Text style={styles.loginBtn} onPress={handleLogin}> Login </Text> </View> ) : ( <View> <Text style={styles.welcomeText}> Welcome back! {userDetails.username} </Text> <Text style={styles.logoutBtn} onPress={handleLogout} >Logout</Text> </View> )} </View> ); } const screenWidth = Dimensions.get("screen").width; const styles = StyleSheet.create({ container: { flex: 1, backgroundColor: "#1f1f1f", alignItems: "center", // justifyContent: 'center', paddingTop: 250, }, helloText: { color: "white", marginBottom: 20, fontSize: 30, }, textInput: { padding: 5, paddingStart: 15, backgroundColor: "#3b3b3b", width: screenWidth * 0.8, borderRadius: 25, marginBottom: 15, color: "white", fontWeight: "600", }, loginBtn: { paddingHorizontal: 25, paddingVertical: 10, backgroundColor: "#ff1178", borderRadius: 25, color: "white", textAlign: "center", }, welcomeText: { color: "white", marginBottom: 20, fontSize: 30, }, logoutBtn: { paddingHorizontal: 25, paddingVertical: 10, backgroundColor: "#ff1178", borderRadius: 25, color: "white", textAlign: "center", }, });
Our whole code will be like this:
First, our state isLoggedIn
is set to false
in the initial stage. So, we will show our login UI, and when a user successfully logs in, then we are saving the credentials in the secure storage and updating the login state and user detail state.
After state update, we are showing a welcome UI with a username along with a logout button.
On the logout button, we have added a handleLogout
function. In this function, we are resetting or removing the user’s credentials from our secure storage and setting our states to the initial data.
You may have noticed there is a useEffect
hook that is also written. In this hook, we are retrieving the user credentials from the secure storage and updating the states based on this information when the user will come back to the app.
If a user has saved their information in the storage, then we will show them the welcome UI. Otherwise, we will show them the login UI that tells them that they are not logged in.
This is how you can easily store and read user credentials without compromising the user’s security.
In this tutorial, you have learned how you can securely save user’s information on their device without compromising their security using react-native-keychain. Now you can implement your own complete login system in your app.
LogRocket is a React Native monitoring solution that helps you reproduce issues instantly, prioritize bugs, and understand performance in your React Native apps.
LogRocket also helps you increase conversion rates and product usage by showing you exactly how users are interacting with your app. LogRocket's product analytics features surface the reasons why users don't complete a particular flow or don't adopt a new feature.
Start proactively monitoring your React Native apps — try LogRocket for free.
Would you be interested in joining LogRocket's developer community?
Join LogRocket’s Content Advisory Board. You’ll help inform the type of content we create and get access to exclusive meetups, social accreditation, and swag.
Sign up nowThe useReducer React Hook is a good alternative to tools like Redux, Recoil, or MobX.
Node.js v22.5.0 introduced a native SQLite module, which is is similar to what other JavaScript runtimes like Deno and Bun already have.
Understanding and supporting pinch, text, and browser zoom significantly enhances the user experience. Let’s explore a few ways to do so.
Playwright is a popular framework for automating and testing web applications across multiple browsers in JavaScript, Python, Java, and C#. […]
One Reply to "Storing credentials using react-native-keychain"
Hi, Great article.
I have few questions though.
Does it work on both Android and iOS?
Does it work when Expo used and testing in iOS or Android Simulators?
Thanks