React-native-keychain: How to follow best practice with this package (i.e. not using username/password) ?

Created on 6 Jan 2020  路  6Comments  路  Source: oblador/react-native-keychain

Hi,

I am new to this package so apologies in advance if this sounds basic question. I understand the following statement which is on the readme.md.

Follow best practices and do not store user credentials on a device. Instead use tokens or other forms of authentication and re-ask for user credentials before performing sensitive operations.

However all the methods in the documentation and in he keychainexample is using username/passwords. So I am not sure how do I use tokens to store the sensitive data to the device using this library?!

Thanks

Most helpful comment

So you should store the access token rather than username/password.

I think the OP is actually asking what is the best practice for securely storing arbitrary data (such as a token) using this library. The API seems to be heavily focused on storing username and passwords. For example, the method setGenericPassword has the following signature

setGenericPassword(username, password, [{ accessControl, accessible, accessGroup, service, securityLevel }])

That seems more than just an example to make usage clearer, I would expect something more like

setItem(key, value, options)
getItem(key)

All 6 comments

The username/password examples are just to make the API usage clear I think.

What you need to store depends on your particular situation. Think of it like this: what can you store so that if there is a security breach, the impact will be minimal?

Token-based approach is not about this library, but about your login system. If you use oauth2 on your service/website for example, you will likely get some access token after logging in. This access token is sent with each API call to show the server you're authenticated. It can also be revoked. So you should store the access token rather than username/password.

In the event of a security breach, you can simply revoke the token and there will be no further damage. If the attacker gets a username/password, the impact is very big. They can for example try this username/password on other services. Storing passwords should not be taken lightly.

The other question to ask is: should you be storing this info at all? Why do you want to store a password? Wouldn't it be safer if the user just has to re-type their password? Maybe you can avoid the whole issue in the first place.

To add to @Brqqq's answer, if you want to authenticate users on your server and tokens is not a solution (for whatever reason), maybe you should consider following what react-native-biometrics recommends: https://github.com/SelfLender/react-native-biometrics#usage.

When a user enrolls in biometrics, a key pair is generated. The private key is stored securely on the device and the public key is sent to a server for registration. When the user wishes to authenticate, the user is prompted for biometrics, which unlocks the securely stored private key. Then a cryptographic signature is generated and sent to the server for verification. The server then verifies the signature. If the verification was successful, the server returns an appropriate response and authorizes the user.

See also https://android-developers.googleblog.com/2015/10/new-in-android-samples-authenticating.html

So you should store the access token rather than username/password.

I think the OP is actually asking what is the best practice for securely storing arbitrary data (such as a token) using this library. The API seems to be heavily focused on storing username and passwords. For example, the method setGenericPassword has the following signature

setGenericPassword(username, password, [{ accessControl, accessible, accessGroup, service, securityLevel }])

That seems more than just an example to make usage clearer, I would expect something more like

setItem(key, value, options)
getItem(key)

I have 2 values (tokens) that i would like to store but I am not sure what is the best way to store them.. any recommendations?

I found two ways how it can be implemented (but both are some kind of hack)

Interface

type SetSecureValue = (key: string, value: string) => Promise<void>
type GetSecureValue = (key: string) => Promise<string | false>
type RemoveSecureValue = (key: string) => Promise<void>
  1. With generic password (use optional service param)
const setSecureValue: SetSecureValue = (key, value) => 
  Keychain.setGenericPassword(key /* <- can be a random string */, value, { service: key })

const getSecureValue: GetSecureValue = async (key) => {
  const result = await Keychain.getGenericPassword({ service: key })
  if (result) {
    return result.password
  }
  return false
}

const removeSecureValue: RemoveSecureValue = (key) =>
  Keychain.resetGenericPassword({ service: key })
  1. With internet credentials
const setSecureValue: SetSecureValue = (key, value) =>
  Keychain.setInternetCredentials(key, key /* <- can be a random string */, value)

const getSecureValue: GetSecureValue = async (key) => {
  const result = await Keychain.getInternetCredentials(key)
  if (result) {
    return result.password
  }
  return false
}

const removeSecureValue: RemoveSecureValue = (key) =>
  Keychain.resetInternetCredentials(key)

p.s. There is no options in examples, but it shouldn't be a problem to add them

This should really make it into the README.md

Was this page helpful?
0 / 5 - 0 ratings