I'd like to persist a user's account credentials (i.e. username and password) in my React Native app. Should I use AsyncStorage?
In other words, I want to know if and how AsyncStorage protects its contents. The docs are silent on that.
(I'm using RN v0.28)
Is AsyncStorage secure?
No AsyncStorage is not secure, the docs says:
AsyncStorage is a simple, unencrypted, asynchronous, persistent,
key-value storage system that is global to the app. It should be used
instead of LocalStorage.
To store secure information on the native side, I really recommand you to use react-native-keychain with react-native
For iOS it use Keychain Sharing Capabilities
For Android it use:
API level 16-22 use Facebook Conceal
API level 23+ use Android Keystore
This is a simple example:
// Generic Password, service argument optional
Keychain
.setGenericPassword(username, password)
.then(function() {
console.log('Credentials saved successfully!');
});
// service argument optional
Keychain
.getGenericPassword()
.then(function(credentials) {
console.log('Credentials successfully loaded for user ' + credentials.username);
}).catch(function(error) {
console.log('Keychain couldn\'t be accessed! Maybe no value set?', error);
});
If you are using Expo sdk, you can use SecureStore for sensitive information.
NO (at least on iOS, RN v0.28)
AsyncStorage saves key-value pairs as a plaintext JSON file in the Documents directory.
If you run it in the iOS Simulator, you can find its contents on ~/Library/Developer/CoreSimulator/Devices
Should have been obvious from the source code for RCTAsyncLocalStorage
You should NEVER save the username and password in plain text in client applications. Please note, never save sensitive data in plain text. You should use a token to authenticate the user.
Regarding the security of the AsyncStorage read this answer. TL;DR the data is safe unless the attacker have access to the device or the device is rooted(android)/jailbroken(iOS). The data is not encrypted. So, with root or physical access to the device (and the device is not protected) it is possible to access to that data.
Related
I am making an expo application that has some user authentication features and I am trying to find a way to store the access token persistently. I have come across some library such as Asyncstorage and expo-secure-store. But I have some concerned about choosing between each of these two.
Asyncstorage --> I understand that it is an unencrypted, persistent data so I believe it means that if I want to store the token here securely, I should encrypt the token with some encryption method before store it into the storage.
expo-secure-store --> I take it that it stores that data in an encrypted way which make it more secure than bare Asyncstorage.
However, in my application, I also planned to use Redux for state management and I thinks that Asyncstorage might work with it. Based on this, should I use Asyncstorage to store my tokens or should I use both of them for different purposes or do you guys have any idea on how this will works out? Thanks!
React Native - : How do I delete asyncstore when the application is closed.
Or What other method can I log in?
UseState in App.js is safe ?
AsyncStorage is an unencrypted, asynchronous, persistent, key-value storage system that is global to the app. It should be used instead of LocalStorage.
If you don't wan't to persist the user data, probably you don't even need to use the AsyncStorage.
Normally AsyncStorage is used to store the user token and other info from the login information to keep the user logged in (unless they logout manually). If you don't want this behavior of a persistent storage, you can keep the usertoken in a local state of the application. This way, once you close the application, the local state will reset and the token will also get deleted, when the user access your application, they will have to login every time to use the app.
If this is something that you are looking for, you can look for maybe the react-context apis or redux reducers to store the login info. If you want to go with a persistent storage option, you can opt for AsyncStorage.
You can save data to AsyncStorage with
AsyncStorage.setItem('#storage_Key', value)
And delete the data with
AsyncStorage.removeItem('#storage_Key', value)
You can read more about the AsyncStorage at https://react-native-async-storage.github.io/async-storage/docs/usage
Or you can choose react-native-keychain or react-native-sensitive-info which is more secure than using the AsyncStorage option.
I have a react native app that uses Expo (managed, not detached) and that uses Firebase auth to provide Facebook login and email/password login.
I now need to implement “Sign in with Apple” as per Apple’s new rules.
Expo provides a way to do this, and it works, returning the user’s info. But because all users are managed through Firebase auth, I need to take what Apple sends me and pass it to Firebase auth.
The Firebase docs explain how to do this using signInWithCustomToken. But that requires that I create the token. In Node this would be simple, but this app is serverless and I haven’t found a tool that can generate an RS256 token on the client. From the Firebase docs it seems that RS256 is a requirement. I’ve tried using expo-jwt with HS256 and Firebase returns an error that the token is badly formed. But besides using HS256 instead of RS256 I see no other possible problems. The token is encoded and decoded successfully as follows.
const appleJwt = JWT.encode(
{
familyName: 'M',
givenName: 'Greg',
email: 'apple_user#example.com',
alg: 'HS256',
iss:
'https://identitytoolkit.googleapis.com/google.identity.identitytoolkit.v1.IdentityToolkit',
sub: serviceAccountEmail,
aud: serviceAccountEmail,
iat: Math.floor(Date.now() / 1000),
exp: Math.floor(Date.now() / 1000) + 3600,
uid: appleUid
},
key,
{
algorithm: 'HS256'
}
);
console.log('TCL: loginWithApple -> appleJwt', appleJwt);
const appleJwtDecoded = JWT.decode(appleJwt, key);
console.log('TCL: loginWithApple -> appleJwtDecoded', appleJwtDecoded);
It’s only when I try to use it with Firebase auth that it returns an error that the token is badly formatted.
return Firebase.auth().signInWithCustomToken(appleJwt).then(...
Note that the key and the serviceAccountEmail were retrieved from the firebase console.
I’m wondering if perhaps there’s some simpler solution that I’m overlooking. The community is awaiting word from Firebase on if they’ll provide out of the box login with Apple, like they do for other providers, so maybe I just need to be patient. But I’d prefer to find a solution.
A big thanks in advance for any advice.
Update 2019-10-15
I built a simple node server with an API that my app could use to generate the token with RS256, but Firebase still responds that the token is badly formatted when I pass it to signInWithCustomToken. Can’t see what’s wrong with it.
So, since I had the node server built, I just configured the Firebase Admin SDK and used the provided createCustomToken to generate the token. Firebase accepts it now when I pass it to signInWithCustomToken, which was my problem, so this issue is settled for me. After the custom Firebase sign in succeeds the first time, I write all the user data to Firestore. For subsequent sign ins, it just updates the last login date in Firestore. Hopefully Firebase will still provide their own solution soon too, since having a separate node server just for this is not ideal.
Firebase Auth now supports Apple sign in across all 3 platforms:
https://firebase.google.com/docs/auth/ios/apple
https://firebase.google.com/docs/auth/android/apple
https://firebase.google.com/docs/auth/web/apple
The Firebase team is working on implementing this in the official sdk
https://github.com/firebase/firebase-ios-sdk/issues/3145#issuecomment-510178359
I’m building a react native app that will interact with APIs that I also write/manage. I have found Auth0 documentation for implementing this flow, but I’m not sure on where/when to save the tokens. I want to be sure I nail this step, because I feel like it has the potential to reduce the security of the flow by a great deal if I don’t do it correctly.
Here is the flow as I understand it (no error handling, only happy-path for sake of brevity):
A user enters the app for the first time, or is not already logged in
They log in using the Auth0 web-login-thingy
I receive a token
I can use the token to authenticate with my API
Questions:
Do I store that token? I don’t want my users to have to log in every time they use the app. If I do store the token, where do I store it?
If I’m not storing it, what do I do? Do I ping an authentication/authorization endpoint with Auth0 every time they open the app and get a new token?
Say I am storing the tokens, if I'm using the ID token for user data, should I be hitting the API again regularly to keep it up to date? Only when the user opens the app again? Not until they trigger a change in the app?
Instead of using the ID token for user data, should I just use that to get the user's ID and ping my database for user data?
I have the basics of this flow, and I'm able to sandbox it, but I want to start applying production-ready app logic to this flow and that's where I'm stuck. I’m a little lost here, so any help is good help.
Thanks!!
Here's a brief answer to your questions when using Auth0:
Yes! you store it, the most secure way to store the token is in your device's local storage, that way it is not kept either in application's state or in a global variable.
2&3. See above, but to add more information, you can configure your tokens to have an expiry length. in theory you would convert this 'expiry time from inception' to a date object, and can do one of two things; you can request a new token using the Refresh Token (that comes with the original) once the expiry has been reached, or force the user to re-log in and re issue a new token at this time (i prefer the latter, prevents people from just renewing their tokens forever as long as they remain logged in)
Use the auth token to request user information after login, this can be stored in app state/global variables/wherever. You then want to use the auth token in the Authorization Header for each API call, along with whatever data you are sending. this ensures that even once someone is INSIDE the application, they need to have a valid token to actually do anything involving data (imagine someone back-dooring into your app and skipping the authorization, or using something like postman to just hammer your API with garbage). it would work something like this: GET userData { Header: auth token } -> GET userProfile (by sending your user ID returned from GET userData) PLUS {Header: auth token }
I can give more in depth examples if you wish, and i apologize if i misunderstood any of the question and gave redundant/incorrect answers
Edit: Resources about using secure storage for keys
Document for when to use in-memory storage Vs persistent storage. The TL;DR is use in-memory if the key is expected to expire before a standard session duration, and persistent for storing a key between sessions
https://hackernoon.com/mobile-api-security-techniques-682a5da4fe10
link to Keychain Services doc
https://developer.apple.com/documentation/security/keychain_services#//apple_ref/doc/uid/TP30000897-CH203-TP1
link to SharedPreferences doc
https://developer.android.com/reference/android/content/SharedPreferences.html
AsyncStorage is a simple, unencrypted, asynchronous, persistent,
key-value storage system that is global to the app. [1]
You could store it in your AsyncStorage, but thats not necessarily a secure location itself (e.g. not encrypted, accessible on rooted devices...). Typically clients will issue access tokens that last anywhere from several hours to a couple days and these will provide their owner access to your API-resources. If there is sensitive data behind your login screen, you're probably better off simply re-doing the auth-flow and invalidate older access tokens on login.
What is the best way to use Expos SecureStore to save login credentials (say Email + Password) so that the iOS Keychain (and Android's Keystore system at the same time if possible) can associate it with the App that stores the credentials?
Of interest here is the third param of SecureStore.setItemAsync(…, …, options). It would be optimal if the options map was set to save the credentials in a way that iOS will (automatically) allow the User to retrieve them from the Keychain when the App is started and the User needs to log in in the future.
Using SecureStore automatically associates the keychain with the saving app. As long as you use the same key (the first parameter) your app should be able to retrieve the value (the second parameter).
There is no option exposed to automatically retrieve the app when the app is started, but you can achieve the same effect by retrieving the keys using getItemAsync when you load the root view of your app.
The options parameter controls if you need extra security around when the app can retrieve the value using the key (for instance if it can access the keys while the device is locked, or after the app is moved to a new device)
https://docs.expo.io/versions/latest/sdk/securestore