Socket
Socket
Sign inDemoInstall

react-native-keychain

Package Overview
Dependencies
0
Maintainers
1
Versions
46
Alerts
File Explorer

Advanced tools

Install Socket

Detect and block malicious and high-risk dependencies

Install

Comparing version 2.0.0 to 3.0.0-rc.1

.prettierrc

136

index.js
import { NativeModules, Platform } from 'react-native';
const { RNKeychainManager } = NativeModules;
export const ACCESSIBLE = {
WHEN_UNLOCKED: 'AccessibleWhenUnlocked',
AFTER_FIRST_UNLOCK: 'AccessibleAfterFirstUnlock',
ALWAYS: 'AccessibleAlways',
WHEN_PASSCODE_SET_THIS_DEVICE_ONLY: 'AccessibleWhenPasscodeSetThisDeviceOnly',
WHEN_UNLOCKED_THIS_DEVICE_ONLY: 'AccessibleWhenUnlockedThisDeviceOnly',
AFTER_FIRST_UNLOCK_THIS_DEVICE_ONLY:
'AccessibleAfterFirstUnlockThisDeviceOnly',
ALWAYS_THIS_DEVICE_ONLY: 'AccessibleAlwaysThisDeviceOnly',
};
export const ACCESS_CONTROL = {
USER_PRESENCE: 'UserPresence',
BIOMETRY_ANY: 'BiometryAny',
BIOMETRY_CURRENT_SET: 'BiometryCurrentSet',
DEVICE_PASSCODE: 'DevicePasscode',
APPLICATION_PASSWORD: 'ApplicationPassword',
BIOMETRY_ANY_OR_DEVICE_PASSCODE: 'BiometryAnyOrDevicePasscode',
BIOMETRY_CURRENT_SET_OR_DEVICE_PASSCODE: 'BiometryCurrentSetOrDevicePasscode',
};
export const AUTHENTICATION_TYPE = {
DEVICE_PASSCODE_OR_BIOMETRICS: 'AuthenticationWithBiometricsDevicePasscode',
BIOMETRICS: 'AuthenticationWithBiometrics',
};
export const BIOMETRY_TYPE = {
TOUCH_ID: 'TouchID',
FACE_ID: 'FaceID',
};
type SecAccessible =

@@ -11,11 +42,49 @@ | 'AccessibleWhenUnlocked'

| 'AccessibleAfterFirstUnlockThisDeviceOnly'
| 'AccessibleAlwaysThisDeviceOnly'
| 'AccessibleAlwaysThisDeviceOnly';
type SecAccessControl =
| 'UserPresence'
| 'BiometryAny'
| 'BiometryCurrentSet'
| 'DevicePasscode'
| 'ApplicationPassword'
| 'BiometryAnyOrDevicePasscode'
| 'BiometryCurrentSetOrDevicePasscode';
type LAPolicy = 'Authentication' | 'AuthenticationWithBiometrics';
type Options = {
accessible?: SecAccessible;
accessGroup?: string;
service?: string;
accessControl?: SecAccessControl,
accessGroup?: string,
accessible?: SecAccessible,
authenticationPrompt?: string,
authenticationType?: LAPolicy,
service?: string,
};
/**
* Inquire if the type of local authentication policy (LAPolicy) is supported
* on this device with the device settings the user chose.
* @param {object} options LAPolicy option, iOS only
* @return {Promise} Resolves to `true` when supported, otherwise `false`
*/
export function canImplyAuthentication(options?: Options): Promise {
if (!RNKeychainManager.canCheckAuthentication) {
return Promise.resolve(false);
}
return RNKeychainManager.canCheckAuthentication(options);
}
/**
* Get what type of hardware biometry support the device has.
* @return {Promise} Resolves to a `BIOMETRY_TYPE` when supported, otherwise `null`
*/
export function getSupportedBiometryType(): Promise {
if (!RNKeychainManager.getSupportedBiometryType) {
return Promise.resolve(null);
}
return RNKeychainManager.getSupportedBiometryType();
}
/**
* Saves the `username` and `password` combination for `server`.

@@ -34,3 +103,8 @@ * @param {string} server URL to server.

): Promise {
return RNKeychainManager.setInternetCredentialsForServer(server, username, password, options);
return RNKeychainManager.setInternetCredentialsForServer(
server,
username,
password,
options
);
}

@@ -64,7 +138,11 @@

function getOptionsArgument(serviceOrOptions?: string | KeychainOptions) {
function getOptionsArgument(serviceOrOptions?: string | Options) {
if (Platform.OS !== 'ios') {
return typeof serviceOrOptions === 'object' ? serviceOrOptions.service : serviceOrOptions;
return typeof serviceOrOptions === 'object'
? serviceOrOptions.service
: serviceOrOptions;
}
return typeof serviceOrOptions === 'string' ? { service: serviceOrOptions } : serviceOrOptions;
return typeof serviceOrOptions === 'string'
? { service: serviceOrOptions }
: serviceOrOptions;
}

@@ -82,5 +160,9 @@

password: string,
serviceOrOptions?: string | KeychainOptions
serviceOrOptions?: string | Options
): Promise {
return RNKeychainManager.setGenericPasswordForOptions(getOptionsArgument(serviceOrOptions), username, password);
return RNKeychainManager.setGenericPasswordForOptions(
getOptionsArgument(serviceOrOptions),
username,
password
);
}

@@ -94,5 +176,7 @@

export function getGenericPassword(
serviceOrOptions?: string | KeychainOptions
serviceOrOptions?: string | Options
): Promise {
return RNKeychainManager.getGenericPasswordForOptions(getOptionsArgument(serviceOrOptions));
return RNKeychainManager.getGenericPasswordForOptions(
getOptionsArgument(serviceOrOptions)
);
}

@@ -106,5 +190,7 @@

export function resetGenericPassword(
serviceOrOptions?: string | KeychainOptions
serviceOrOptions?: string | Options
): Promise {
return RNKeychainManager.resetGenericPasswordForOptions(getOptionsArgument(serviceOrOptions));
return RNKeychainManager.resetGenericPasswordForOptions(
getOptionsArgument(serviceOrOptions)
);
}

@@ -117,5 +203,9 @@

*/
export function requestSharedWebCredentials() : Promise {
export function requestSharedWebCredentials(): Promise {
if (Platform.OS !== 'ios') {
return Promise.reject(new Error(`requestSharedWebCredentials() is not supported on ${Platform.OS} yet`));
return Promise.reject(
new Error(
`requestSharedWebCredentials() is not supported on ${Platform.OS} yet`
)
);
}

@@ -136,7 +226,15 @@ return RNKeychainManager.requestSharedWebCredentials();

password: string
) : Promise {
): Promise {
if (Platform.OS !== 'ios') {
return Promise.reject(new Error(`setSharedWebCredentials() is not supported on ${Platform.OS} yet`));
return Promise.reject(
new Error(
`setSharedWebCredentials() is not supported on ${Platform.OS} yet`
)
);
}
return RNKeychainManager.setSharedWebCredentialsForServer(server, username, password);
return RNKeychainManager.setSharedWebCredentialsForServer(
server,
username,
password
);
}

2

package.json
{
"name": "react-native-keychain",
"version": "2.0.0",
"version": "3.0.0-rc.1",
"description": "Keychain Access for React Native",

@@ -5,0 +5,0 @@ "main": "index.js",

@@ -1,124 +0,137 @@

# react-native-keychain
<p align="center"><img src="https://user-images.githubusercontent.com/378279/36642269-6195b10c-1a3d-11e8-9e1b-37a3d1bcf7b3.png" align="center" width="150" height="201" alt="" /></p>
<h1 align="center">react-native-keychain</h1>
[![Travis](https://img.shields.io/travis/oblador/react-native-keychain.svg)](https://travis-ci.org/oblador/react-native-keychain) [![npm](https://img.shields.io/npm/v/react-native-keychain.svg)](https://npmjs.com/package/react-native-keychain) [![npm](https://img.shields.io/npm/dm/react-native-keychain.svg)](https://npmjs.com/package/react-native-keychain)
Keychain Access for React Native. Currently functionality is limited to just storing internet and generic passwords.
Keychain/Keystore Access for React Native.
### New 2.0.0 with improved android implementation
## Installation
The KeychainModule will now automatically use the appropriate CipherStorage implementation based on API level:
1. `$ yarn add react-native-keychain`
2. `$ react-native link react-native-keychain` and check `MainApplication.java` to verify the package was added.
3. Rebuild your project with `react-native run-ios/android`
* API level 16-22 will en/de crypt using Facebook Conceal
* API level 23+ will en/de crypt using Android Keystore
See manual installation below if you have issues with `react-native link`.
Encrypted data is stored in SharedPreferences.
## Usage
## Installation
```js
import * as Keychain from 'react-native-keychain';
1 . `$ npm install --save react-native-keychain`
async () => {
const username = 'zuck';
const password = 'poniesRgr8';
or
// Store the credentials
await Keychain.setGenericPassword(username, password);
`$ yarn add react-native-keychain`
try {
// Retreive the credentials
const credentials = Keychain.getGenericPassword();
if (credentials) {
console.log('Credentials successfully loaded for user ' + credentials.username);
} else {
console.log('No credentials stored')
}
} catch (error) {
console.log('Keychain couldn\'t be accessed!', error);
}
await Keychain.resetGenericPassword()
}
```
See `KeychainExample` for fully working project example.
2 . `$ react-native link react-native-keychain` and check `MainApplication.java` to verify the package was added.
Both `setGenericPassword` and `setInternetCredentials` are limited to strings only, so if you need to store objects etc, please use `JSON.stringify`/`JSON.parse` when you store/access it.
3 .  rebuild your project
### `setGenericPassword(username, password, [{ accessControl, accessible, accessGroup, service }])`
Will store the username/password combination in the secure storage. Resolves to `{ username, password }` if an entry exists or `false` if it doesn't.
* on Android, the `setInternetCredentials(server, username, password)` call will be resolved as call to `setGenericPassword(username, password, server)`. Use the `server` argument to distinguish between multiple entries.
### `getGenericPassword([{ authenticationPrompt, service }])`
Check out the "releases" tab for breaking changes and RN version compatibility. v1.0.0 is for RN >= 0.40
Will retreive the username/password combination from the secure storage. It will reject only if an unexpected error is encountered like lacking entitlements or permission.
## ❗ Enable `Keychain Sharing` entitlement for iOS 10
### `resetGenericPassword([{ service }])`
For iOS 10 you'll need to enable the `Keychain Sharing` entitlement in the `Capabilities` section of your build target. (See screenshot). Otherwise you'll experience the error shown below.
Will remove the username/password combination from the secure storage.
![screen shot 2016-09-16 at 20 56 33](https://cloud.githubusercontent.com/assets/512692/18597833/15316342-7c50-11e6-92e7-781651e61563.png)
### `setInternetCredentials(server, username, password, [{ accessControl, accessible, accessGroup }])`
```
Error: {
code = "-34018";
domain = NSOSStatusErrorDomain;
message = "The operation couldn\U2019t be completed. (OSStatus error -34018.)";
}
```
Will store the server/username/password combination in the secure storage.
### `getInternetCredentials(server, [{ authenticationPrompt }])`
## Usage
Will retreive the server/username/password combination from the secure storage. Resolves to `{ username, password }` if an entry exists or `false` if it doesn't. It will reject only if an unexpected error is encountered like lacking entitlements or permission.
See `KeychainExample` for fully working project example.
### `resetInternetCredentials(server)`
```js
import * as Keychain from 'react-native-keychain';
Will remove the server/username/password combination from the secure storage.
const username = 'zuck';
const password = 'poniesRgr8';
### `requestSharedWebCredentials()` (iOS only)
// Generic Password, service argument optional
Keychain
.setGenericPassword(username, password)
.then(function() {
console.log('Credentials saved successfully!');
});
Asks the user for a shared web credential. Requires additional setup both in the app and server side, see [Apple documentation](https://developer.apple.com/documentation/security/shared_web_credentials). Resolves to `{ server, username, password }` if approved and `false` if denied and throws an error if not supported on platform or there's no shared credentials.
// 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);
});
### `setSharedWebCredentials(server, username, password)` (iOS only)
// service argument optional
Keychain
.resetGenericPassword()
.then(function() {
console.log('Credentials successfully deleted');
});
Sets a shared web credential. Resolves to `true` when successful.
// Internet Password, server argument required
const server = 'http://facebook.com';
Keychain
.setInternetCredentials(server, username, password)
.then(function() {
console.log('Credentials saved successfully!');
});
### `canImplyAuthentication([{ authenticationType }])`
Keychain
.getInternetCredentials(server)
.then(function(credentials) {
if (credentials) {
console.log('Credentials successfully loaded for user ' + credentials.username);
}
});
Inquire if the type of local authentication policy is supported on this device with the device settings the user chose. Should be used in combination with `accessControl` option in the setter functions. Resolves to `true` if supported.
Keychain
.resetInternetCredentials(server)
.then(function() {
console.log('Credentials successfully deleted');
});
### `getSupportedBiometryType()`
Keychain
.requestSharedWebCredentials()
.then(function(credentials) {
if (credentials) {
console.log('Shared web credentials successfully loaded for user ' + credentials.username);
}
})
Get what type of hardware biometry support the device has. Resolves to a `Keychain.BIOMETRY_TYPE` value when supported, otherwise `null`.
Keychain
.setSharedWebCredentials(server, username, password)
.then(function() {
console.log('Shared web credentials saved successfully!');
})
### Options
```
| Key | Platform | Description | Default |
|---|---|---|---|
|**`accessControl`**|iOS only|This dictates how a keychain item may be used, see possible values in `Keychain.ACCESS_CONTROL`. |*None*|
|**`accessible`**|iOS only|This dictates when a keychain item is accessible, see possible values in `Keychain.ACCESSIBLE`. |*`Keychain.ACCESSIBLE.WHEN_UNLOCKED`*|
|**`accessGroup`**|iOS only|In which App Group to share the keychain. Requires additional setup with entitlements. |*None*|
|**`authenticationPrompt`**|iOS only|What to prompt the user when unlocking the keychain with biometry or device password. |`Authenticate to retrieve secret`|
|**`authenticationType`**|iOS only|Policies specifying which forms of authentication are acceptable. |`Keychain.AUTHENTICATION_TYPE.DEVICE_PASSCODE_OR_BIOMETRICS`|
|**`service`**|All|Reverse domain name qualifier for the service associated with password. |*App bundle ID*|
### Note on security
#### `Keychain.ACCESS_CONTROL` enum
On API levels that do not support Android keystore, Facebook Conceal is used to en/decrypt stored data. The encrypted data is then stored in SharedPreferences. Since Conceal itself stores its encryption key in SharedPreferences, it follows that if the device is rooted (or if an attacker can somehow access the filesystem), the key can be obtained and the stored data can be decrypted. Therefore, on such a device, the conceal encryption is only an obscurity. On API level 23+ the key is stored in the Android Keystore, which makes the key non-exportable and therefore makes the entire process more secure. 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.
| Key | Description |
|-----|-------------|
|**`USER_PRESENCE`**|Constraint to access an item with either Touch ID or passcode.|
|**`BIOMETRY_ANY`**|Constraint to access an item with Touch ID for any enrolled fingers.|
|**`BIOMETRY_CURRENT_SET`**|Constraint to access an item with Touch ID for currently enrolled fingers.|
|**`DEVICE_PASSCODE`**|Constraint to access an item with a passcode.|
|**`APPLICATION_PASSWORD`**|Constraint to use an application-provided password for data encryption key generation.|
|**`BIOMETRY_ANY_OR_DEVICE_PASSCODE`**|Constraint to access an item with Touch ID for any enrolled fingers or passcode.|
|**`BIOMETRY_CURRENT_SET_OR_DEVICE_PASSCODE`**|Constraint to access an item with Touch ID for currently enrolled fingers or passcode.|
#### `Keychain.ACCESSIBLE` enum
| Key | Description |
|-----|-------------|
|**`WHEN_UNLOCKED`**|The data in the keychain item can be accessed only while the device is unlocked by the user.|
|**`AFTER_FIRST_UNLOCK`**|The data in the keychain item cannot be accessed after a restart until the device has been unlocked once by the user.|
|**`ALWAYS`**|The data in the keychain item can always be accessed regardless of whether the device is locked.|
|**`WHEN_PASSCODE_SET_THIS_DEVICE_ONLY`**|The data in the keychain can only be accessed when the device is unlocked. Only available if a passcode is set on the device. Items with this attribute never migrate to a new device.|
|**`WHEN_UNLOCKED_THIS_DEVICE_ONLY`**|The data in the keychain item can be accessed only while the device is unlocked by the user. Items with this attribute do not migrate to a new device.|
|**`AFTER_FIRST_UNLOCK_THIS_DEVICE_ONLY`**|The data in the keychain item cannot be accessed after a restart until the device has been unlocked once by the user. Items with this attribute never migrate to a new device.|
|**`ALWAYS_THIS_DEVICE_ONLY`**|The data in the keychain item can always be accessed regardless of whether the device is locked. Items with this attribute never migrate to a new device.|
#### `Keychain.AUTHENTICATION_TYPE` enum
| Key | Description |
|-----|-------------|
|**`DEVICE_PASSCODE_OR_BIOMETRICS`**|Device owner is going to be authenticated by biometry or device passcode.|
|**`BIOMETRICS`**|Device owner is going to be authenticated using a biometric method (Touch ID or Face ID).|
#### `Keychain.BIOMETRY_TYPE` enum
| Key | Description |
|-----|-------------|
|**`TOUCH_ID`**|Device supports authentication with Touch ID.|
|**`FACE_ID`**|Device supports authentication with Face ID.|
## Manual Installation

@@ -141,2 +154,16 @@

#### Enable `Keychain Sharing` entitlement for iOS 10+
For iOS 10 you'll need to enable the `Keychain Sharing` entitlement in the `Capabilities` section of your build target. (See screenshot). Otherwise you'll experience the error shown below.
![screen shot 2016-09-16 at 20 56 33](https://cloud.githubusercontent.com/assets/512692/18597833/15316342-7c50-11e6-92e7-781651e61563.png)
```
Error: {
code = "-34018";
domain = NSOSStatusErrorDomain;
message = "The operation couldn\U2019t be completed. (OSStatus error -34018.)";
}
```
### Android

@@ -148,49 +175,49 @@

```diff
rootProject.name = 'MyApp'
```diff
rootProject.name = 'MyApp'
include ':app'
include ':app'
+ include ':react-native-keychain'
+ project(':react-native-keychain').projectDir = new File(rootProject.projectDir, '../node_modules/react-native-keychain/android')
```
+ include ':react-native-keychain'
+ project(':react-native-keychain').projectDir = new File(rootProject.projectDir, '../node_modules/react-native-keychain/android')
```
* Edit `android/app/build.gradle` (note: **app** folder) to look like this:
```diff
apply plugin: 'com.android.application'
```diff
apply plugin: 'com.android.application'
android {
...
}
android {
...
}
dependencies {
compile fileTree(dir: 'libs', include: ['*.jar'])
compile 'com.android.support:appcompat-v7:23.0.1'
compile 'com.facebook.react:react-native:0.19.+'
+ compile project(':react-native-keychain')
}
```
dependencies {
compile fileTree(dir: 'libs', include: ['*.jar'])
compile 'com.android.support:appcompat-v7:23.0.1'
compile 'com.facebook.react:react-native:0.19.+'
+ compile project(':react-native-keychain')
}
```
* Edit your `MainApplication.java` (deep in `android/app/src/main/java/...`) to look like this (note **two** places to edit):
```diff
package com.myapp;
```diff
package com.myapp;
+ import com.oblador.keychain.KeychainPackage;
+ import com.oblador.keychain.KeychainPackage;
....
....
public class MainActivity extends extends ReactActivity {
public class MainActivity extends extends ReactActivity {
@Override
protected List<ReactPackage> getPackages() {
return Arrays.<ReactPackage>asList(
new MainReactPackage(),
+ new KeychainPackage()
);
}
...
@Override
protected List<ReactPackage> getPackages() {
return Arrays.<ReactPackage>asList(
new MainReactPackage(),
+ new KeychainPackage()
);
}
```
...
}
```

@@ -214,2 +241,19 @@ #### Proguard Rules

## Notes
### Android
The module will automatically use the appropriate CipherStorage implementation based on API level:
* API level 16-22 will en/de crypt using Facebook Conceal
* API level 23+ will en/de crypt using Android Keystore
Encrypted data is stored in SharedPreferences.
The `setInternetCredentials(server, username, password)` call will be resolved as call to `setGenericPassword(username, password, server)`. Use the `server` argument to distinguish between multiple entries.
### Security
On API levels that do not support Android keystore, Facebook Conceal is used to en/decrypt stored data. The encrypted data is then stored in SharedPreferences. Since Conceal itself stores its encryption key in SharedPreferences, it follows that if the device is rooted (or if an attacker can somehow access the filesystem), the key can be obtained and the stored data can be decrypted. Therefore, on such a device, the conceal encryption is only an obscurity. On API level 23+ the key is stored in the Android Keystore, which makes the key non-exportable and therefore makes the entire process more secure. 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.
## Maintainers

@@ -216,0 +260,0 @@

@@ -14,6 +14,22 @@ declare module 'react-native-keychain' {

export interface Options {
accessControl?: string;
accessGroup?: string;
authenticationPrompt?: string;
authenticationType?: string;
service?: string;
}
function canImplyAuthentication(
options?: Options
): Promise<boolean>;
function getSupportedBiometryType(
): Promise<string>;
function setInternetCredentials(
server: string,
username: string,
password: string
password: string,
options?: Options
): Promise<void>;

@@ -32,11 +48,11 @@

password: string,
service?: string
options?: Options
): Promise<boolean>;
function getGenericPassword(
service?: string
options?: Options
): Promise<boolean | {service: string, username: string, password: string}>;
function resetGenericPassword(
service?: string
options?: Options
): Promise<boolean>

@@ -43,0 +59,0 @@

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

SocketSocket SOC 2 Logo

Product

  • Package Alerts
  • Integrations
  • Docs
  • Pricing
  • FAQ
  • Roadmap

Stay in touch

Get open source security insights delivered straight into your inbox.


  • Terms
  • Privacy
  • Security

Made with ⚡️ by Socket Inc