Akshay Rana I'm a self-taught, full-time programmer. I have experience working with various technologies including React, React Native, WordPress, Node.js, Express.js, Raspberry Pi, Python, REST APIs, and GraphQL. I love to explore new technologies and update my skills.

Storing credentials using react-native-keychain

4 min read 1395

Storing Credentials Using react-native-keychain

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.

Build a basic UI

First, let’s build a basic UI for the login screen. See below the image for how our login screen will look.

Basic UI for react-native-keychain App

A pretty basic login UI with a dark theme (P.S.: I love dark themes 😉 ).

Here is the code for this UI:

We made a custom demo for .
No really. Click here to check it out.

// 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",
  }
});

Install react-native-keychain

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.

Implement react-native-keychain

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.

Build a welcome screen

After login, we will show a welcome screen with the username. Our welcome screen will look like this:

Welcome Screen for react-native-keychain App

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.

Conclusion

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.

: Full visibility into your web apps

LogRocket is a frontend application monitoring solution that lets you replay problems as if they happened in your own browser. Instead of guessing why errors happen, or asking users for screenshots and log dumps, LogRocket lets you replay the session to quickly understand what went wrong. It works perfectly with any app, regardless of framework, and has plugins to log additional context from Redux, Vuex, and @ngrx/store.

In addition to logging Redux actions and state, LogRocket records console logs, JavaScript errors, stacktraces, network requests/responses with headers + bodies, browser metadata, and custom logs. It also instruments the DOM to record the HTML and CSS on the page, recreating pixel-perfect videos of even the most complex single-page apps.

.
Akshay Rana I'm a self-taught, full-time programmer. I have experience working with various technologies including React, React Native, WordPress, Node.js, Express.js, Raspberry Pi, Python, REST APIs, and GraphQL. I love to explore new technologies and update my skills.

Testing accessibility with Storybook

One big challenge when building a component library is prioritizing accessibility. Accessibility is usually seen as one of those “nice-to-have” features, and unfortunately, we’re...
Laura Carballo
4 min read

Leave a Reply