Skip to content

Commit

Permalink
Update README.md, API for iOS & Android
Browse files Browse the repository at this point in the history
added strings for copy/paste

fixed anchors

cleanup the code

iOS fix, documentation become more accurate now

improved ios part

last minute changes

updated list of ignored files

Sync api method changes

cleanup
  • Loading branch information
OleksandrKucherenko authored and Oleksandr Kucherenko committed Feb 25, 2020
1 parent f24a7dc commit fa43f37
Show file tree
Hide file tree
Showing 12 changed files with 217 additions and 157 deletions.
1 change: 1 addition & 0 deletions .editorconfig
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ insert_final_newline = true

[*.md]
trim_trailing_whitespace = false
max_line_length = 100


[*.java]
Expand Down
5 changes: 2 additions & 3 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -62,9 +62,8 @@ buck-out/
*.jsbundle

# CocoaPods
/ios/Pods/
/KeychainExample/ios/Pods/

ios/Pods/
KeychainExample/ios/Pods/

# Logs
*.log
Expand Down
1 change: 1 addition & 0 deletions KeychainExample/.gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ DerivedData
*.ipa
*.xcuserstate
project.xcworkspace
ios/Pods/

# Android/IntelliJ
#
Expand Down
34 changes: 34 additions & 0 deletions KeychainExample/App.js
Original file line number Diff line number Diff line change
Expand Up @@ -109,6 +109,27 @@ export default class KeychainExample extends Component {
}
}

async ios_specifics() {
try {
const reply = await Keychain.setSharedWebCredentials(
'server',
'username',
'password',
{}
);
console.log(`setSharedWebCredentials: ${JSON.stringify(reply)}`);
} catch (err) {
alert(`setSharedWebCredentials: ${err}`);
}

try {
const reply = await Keychain.requestSharedWebCredentials({});
console.log(`requestSharedWebCredentials: ${JSON.stringify(reply)}`)
} catch (err) {
alert(`requestSharedWebCredentials: ${err}`);
}
}

render() {
const VALUES =
Platform.OS === 'ios'
Expand Down Expand Up @@ -255,6 +276,19 @@ export default class KeychainExample extends Component {
</TouchableHighlight>
</View>
)}

{Platform.OS === 'ios' && (
<View style={styles.buttons}>
<TouchableHighlight
onPress={() => this.ios_specifics()}
style={styles.button}
>
<View style={styles.load}>
<Text style={styles.buttonText}>Test Other APIs</Text>
</View>
</TouchableHighlight>
</View>
)}
</View>
</KeyboardAvoidingView>
);
Expand Down
45 changes: 24 additions & 21 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,31 +8,32 @@

[![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/Keystore Access for React Native.
# Keychain/Keystore Access for React Native

- [Keychain/Keystore Access for React Native.](#keychainkeystore-access-for-react-native)
- [Keychain/Keystore Access for React Native](#keychainkeystore-access-for-react-native)
- [Installation](#installation)
- [Usage](#usage)
- [API](#api)
- [`setGenericPassword(username, password, [{ accessControl, accessible, accessGroup, service, securityLevel }])`](#setgenericpasswordusername-password--accesscontrol-accessible-accessgroup-service-securitylevel)
- [`getGenericPassword([{ authenticationPrompt, service }])`](#getgenericpassword-authenticationprompt-service)
- [`resetGenericPassword([{ service }])`](#resetgenericpassword-service)
- [`setInternetCredentials(server, username, password, [{ accessControl, accessible, accessGroup, securityLevel }])`](#setinternetcredentialsserver-username-password--accesscontrol-accessible-accessgroup-securitylevel)
- [`hasInternetCredentials(server, [{ authenticationPrompt }])`](#hasinternetcredentialsserver--authenticationprompt)
- [`getInternetCredentials(server, [{ authenticationPrompt }])`](#getinternetcredentialsserver--authenticationprompt)
- [`resetInternetCredentials(server, [{}])`](#resetinternetcredentialsserver)
- [`setGenericPassword(username, password, [{ accessControl, accessible, accessGroup, service, securityLevel }])`](#setgenericpasswordusername-password--accesscontrol-accessible-accessgroup-service-securitylevel-)
- [`getGenericPassword([{ authenticationPrompt, service }])`](#getgenericpassword-authenticationprompt-service-)
- [`resetGenericPassword([{ service }])`](#resetgenericpassword-service-)
- [`setInternetCredentials(server, username, password, [{ accessControl, accessible, accessGroup, securityLevel }])`](#setinternetcredentialsserver-username-password--accesscontrol-accessible-accessgroup-securitylevel-)
- [`hasInternetCredentials(server, [{ authenticationPrompt }])`](#hasinternetcredentialsserver--authenticationprompt-)
- [`getInternetCredentials(server, [{ authenticationPrompt }])`](#getinternetcredentialsserver--authenticationprompt-)
- [`resetInternetCredentials(server, [{}])`](#resetinternetcredentialsserver-)
- [`requestSharedWebCredentials()` (iOS only)](#requestsharedwebcredentials-ios-only)
- [`setSharedWebCredentials(server, username, password)` (iOS only)](#setsharedwebcredentialsserver-username-password-ios-only)
- [`canImplyAuthentication([{ authenticationType }])` (iOS only)](#canimplyauthentication-authenticationtype--ios-only)
- [`getSupportedBiometryType([{}])`](#getsupportedbiometrytype)
- [`getSecurityLevel([{}])` (Android only)](#getsecuritylevel-android-only)
- [`getSecurityLevel([{ accessControl }])` (Android only)](#getsecuritylevel-accesscontrol--android-only)
- [Options](#options)
- [`Keychain.ACCESS_CONTROL` enum](#keychainaccesscontrol-enum)
- [Data Structure Properties/Fields](#data-structure-propertiesfields)
- [`Keychain.ACCESS_CONTROL` enum](#keychainaccess_control-enum)
- [`Keychain.ACCESSIBLE` enum](#keychainaccessible-enum)
- [`Keychain.AUTHENTICATION_TYPE` enum](#keychainauthenticationtype-enum)
- [`Keychain.BIOMETRY_TYPE` enum](#keychainbiometrytype-enum)
- [`Keychain.SECURITY_LEVEL` enum (Android only)](#keychainsecuritylevel-enum-android-only)
- [`Keychain.STORAGE_TYPE` enum (Android only)](#keychainstoragetype-enum-android-only)
- [`Keychain.AUTHENTICATION_TYPE` enum](#keychainauthentication_type-enum)
- [`Keychain.BIOMETRY_TYPE` enum](#keychainbiometry_type-enum)
- [`Keychain.SECURITY_LEVEL` enum (Android only)](#keychainsecurity_level-enum-android-only)
- [`Keychain.STORAGE_TYPE` enum (Android only)](#keychainstorage_type-enum-android-only)
- [`Keychain.RULES` enum (Android only)](#keychainrules-enum-android-only)
- [Important Behavior](#important-behavior)
- [Rule 1: Automatic Security Level Upgrade](#rule-1-automatic-security-level-upgrade)
Expand Down Expand Up @@ -108,11 +109,11 @@ Will retrieve the username/password combination from the secure storage. Resolve

### `resetGenericPassword([{ service }])`

Will remove the username/password combination from the secure storage. Returns `true` in case of success.
Will remove the username/password combination from the secure storage. Resolves to `true` in case of success.

### `setInternetCredentials(server, username, password, [{ accessControl, accessible, accessGroup, securityLevel }])`

Will store the server/username/password combination in the secure storage. Returns `{ username, password, service, storage }`;
Will store the server/username/password combination in the secure storage. Resolves to `{ username, password, service, storage }`;

### `hasInternetCredentials(server, [{ authenticationPrompt }])`

Expand All @@ -124,7 +125,7 @@ Will retrieve the server/username/password combination from the secure storage.

### `resetInternetCredentials(server, [{}])`

Will remove the server/username/password combination from the secure storage.
Will remove the server/username/password combination from the secure storage. Method accept `options` object instance but ignores all it values for now. Its reserved for future functionality.

### `requestSharedWebCredentials()` (iOS only)

Expand All @@ -140,16 +141,18 @@ Inquire if the type of local authentication policy is supported on this device w

### `getSupportedBiometryType([{}])`

Get what type of hardware biometry support the device has. Resolves to a `Keychain.BIOMETRY_TYPE` value when supported, otherwise `null`.
Get what type of hardware biometry support the device has. Resolves to a `Keychain.BIOMETRY_TYPE` value when supported, otherwise `null`. Method accept `options` object instance but ignores all it values for now. Its reserved for future functionality.

> This method returns `null`, if the device haven't enrolled into fingerprint/FaceId. Even though it has hardware for it.
### `getSecurityLevel([{}])` (Android only)
### `getSecurityLevel([{ accessControl }])` (Android only)

Get security level that is supported on the current device with the current OS.
Get security level that is supported on the current device with the current OS. Resolves to `Keychain.SECURITY_LEVEL` enum value.

### Options

#### Data Structure Properties/Fields

| Key | Platform | Description | Default |
| -------------------------- | ------------ | ------------------------------------------------------------------------------------------------ | ------------------------------------------------------------ |
| **`accessControl`** | All | This dictates how a keychain item may be used, see possible values in `Keychain.ACCESS_CONTROL`. | *None* (iOS), `BIOMETRY_ANY` default for Android. |
Expand Down
80 changes: 62 additions & 18 deletions RNKeychainManager/RNKeychainManager.m
Original file line number Diff line number Diff line change
Expand Up @@ -174,7 +174,10 @@ SecAccessControlCreateFlags accessControlValue(NSDictionary *options)
return 0;
}

- (void)insertKeychainEntry:(NSDictionary *)attributes withOptions:(NSDictionary * __nullable)options resolver:(RCTPromiseResolveBlock)resolve rejecter:(RCTPromiseRejectBlock)reject
- (void)insertKeychainEntry:(NSDictionary *)attributes
withOptions:(NSDictionary * __nullable)options
resolver:(RCTPromiseResolveBlock)resolve
rejecter:(RCTPromiseRejectBlock)reject
{
NSString *accessGroup = accessGroupValue(options);
CFStringRef accessible = accessibleValue(options);
Expand Down Expand Up @@ -217,7 +220,11 @@ - (void)insertKeychainEntry:(NSDictionary *)attributes withOptions:(NSDictionary
NSError *error = [NSError errorWithDomain:NSOSStatusErrorDomain code:osStatus userInfo:nil];
return rejectWithError(reject, error);
} else {
return resolve(@(YES));
NSString *service = serviceValue(options);
return resolve(@{
@"server": service,
@"storage": @"keychain"
});
}
});
});
Expand Down Expand Up @@ -250,7 +257,9 @@ - (OSStatus)deleteCredentialsForServer:(NSString *)server
#pragma mark - RNKeychain

#if TARGET_OS_IOS
RCT_EXPORT_METHOD(canCheckAuthentication:(NSDictionary *)options resolver:(RCTPromiseResolveBlock)resolve rejecter:(RCTPromiseRejectBlock)reject)
RCT_EXPORT_METHOD(canCheckAuthentication:(NSDictionary * __nullable)options
resolver:(RCTPromiseResolveBlock)resolve
rejecter:(RCTPromiseRejectBlock)reject)
{
LAPolicy policyToEvaluate = authPolicy(options);

Expand All @@ -266,7 +275,8 @@ - (OSStatus)deleteCredentialsForServer:(NSString *)server
#endif

#if TARGET_OS_IOS
RCT_EXPORT_METHOD(getSupportedBiometryType:(RCTPromiseResolveBlock)resolve rejecter:(RCTPromiseRejectBlock)reject)
RCT_EXPORT_METHOD(getSupportedBiometryType:(RCTPromiseResolveBlock)resolve
rejecter:(RCTPromiseRejectBlock)reject)
{
NSError *aerr = nil;
LAContext *context = [LAContext new];
Expand All @@ -285,9 +295,14 @@ - (OSStatus)deleteCredentialsForServer:(NSString *)server
}
#endif

RCT_EXPORT_METHOD(setGenericPasswordForOptions:(NSDictionary *)options withUsername:(NSString *)username withPassword:(NSString *)password withSecurityLevel:(__unused NSString *)level resolver:(RCTPromiseResolveBlock)resolve rejecter:(RCTPromiseRejectBlock)reject)
RCT_EXPORT_METHOD(setGenericPasswordForOptions:(NSString *)service
withUsername:(NSString *)username
withPassword:(NSString *)password
withOptions:(NSDictionary * __nullable)options
resolver:(RCTPromiseResolveBlock)resolve
rejecter:(RCTPromiseRejectBlock)reject)
{
NSString *service = serviceValue(options);
if(service == NULL) service = serviceValue(options);
NSDictionary *attributes = attributes = @{
(__bridge NSString *)kSecClass: (__bridge id)(kSecClassGenericPassword),
(__bridge NSString *)kSecAttrService: service,
Expand All @@ -300,9 +315,12 @@ - (OSStatus)deleteCredentialsForServer:(NSString *)server
[self insertKeychainEntry:attributes withOptions:options resolver:resolve rejecter:reject];
}

RCT_EXPORT_METHOD(getGenericPasswordForOptions:(NSDictionary *)options resolver:(RCTPromiseResolveBlock)resolve rejecter:(RCTPromiseRejectBlock)reject)
RCT_EXPORT_METHOD(getGenericPasswordForOptions:(NSString *)service
withOptions:(NSDictionary * __nullable)options
resolver:(RCTPromiseResolveBlock)resolve
rejecter:(RCTPromiseRejectBlock)reject)
{
NSString *service = serviceValue(options);
if(service == NULL) service = serviceValue(options);
NSString *authenticationPrompt = @"Authenticate to retrieve secret";
if (options && options[kAuthenticationPromptMessage]) {
authenticationPrompt = options[kAuthenticationPromptMessage];
Expand Down Expand Up @@ -340,12 +358,15 @@ - (OSStatus)deleteCredentialsForServer:(NSString *)server
return resolve(@{
@"service": service,
@"username": username,
@"password": password
@"password": password,
@"storage": @"keychain"
});

}

RCT_EXPORT_METHOD(resetGenericPasswordForOptions:(NSDictionary *)options resolver:(RCTPromiseResolveBlock)resolve rejecter:(RCTPromiseRejectBlock)reject)
RCT_EXPORT_METHOD(resetGenericPasswordForOptions:(NSDictionary * __nullable)options
resolver:(RCTPromiseResolveBlock)resolve
rejecter:(RCTPromiseRejectBlock)reject)
{
NSString *service = serviceValue(options);

Expand All @@ -359,7 +380,12 @@ - (OSStatus)deleteCredentialsForServer:(NSString *)server
return resolve(@(YES));
}

RCT_EXPORT_METHOD(setInternetCredentialsForServer:(NSString *)server withUsername:(NSString*)username withPassword:(NSString*)password withSecurityLevel:(__unused NSString *)level withOptions:(NSDictionary *)options resolver:(RCTPromiseResolveBlock)resolve rejecter:(RCTPromiseRejectBlock)reject)
RCT_EXPORT_METHOD(setInternetCredentialsForServer:(NSString *)server
withUsername:(NSString*)username
withPassword:(NSString*)password
withOptions:(NSDictionary * __nullable)options
resolver:(RCTPromiseResolveBlock)resolve
rejecter:(RCTPromiseRejectBlock)reject)
{
[self deleteCredentialsForServer:server];

Expand All @@ -373,7 +399,9 @@ - (OSStatus)deleteCredentialsForServer:(NSString *)server
[self insertKeychainEntry:attributes withOptions:options resolver:resolve rejecter:reject];
}

RCT_EXPORT_METHOD(hasInternetCredentialsForServer:(NSString *)server resolver:(RCTPromiseResolveBlock)resolve rejecter:(RCTPromiseRejectBlock)reject)
RCT_EXPORT_METHOD(hasInternetCredentialsForServer:(NSString *)server
resolver:(RCTPromiseResolveBlock)resolve
rejecter:(RCTPromiseRejectBlock)reject)
{
NSMutableDictionary *queryParts = [[NSMutableDictionary alloc] init];
queryParts[(__bridge NSString *)kSecClass] = (__bridge id)(kSecClassInternetPassword);
Expand Down Expand Up @@ -402,7 +430,10 @@ - (OSStatus)deleteCredentialsForServer:(NSString *)server
return rejectWithError(reject, error);
}

RCT_EXPORT_METHOD(getInternetCredentialsForServer:(NSString *)server withOptions:(NSDictionary *)options resolver:(RCTPromiseResolveBlock)resolve rejecter:(RCTPromiseRejectBlock)reject)
RCT_EXPORT_METHOD(getInternetCredentialsForServer:(NSString *)server
withOptions:(NSDictionary * __nullable)options
resolver:(RCTPromiseResolveBlock)resolve
rejecter:(RCTPromiseRejectBlock)reject)
{
NSDictionary *query = @{
(__bridge NSString *)kSecClass: (__bridge id)(kSecClassInternetPassword),
Expand Down Expand Up @@ -435,12 +466,16 @@ - (OSStatus)deleteCredentialsForServer:(NSString *)server
return resolve(@{
@"server": server,
@"username": username,
@"password": password
@"password": password,
@"storage": @"keychain"
});

}

RCT_EXPORT_METHOD(resetInternetCredentialsForServer:(NSString *)server withOptions:(NSDictionary *)options resolver:(RCTPromiseResolveBlock)resolve rejecter:(RCTPromiseRejectBlock)reject)
RCT_EXPORT_METHOD(resetInternetCredentialsForServer:(NSString *)server
withOptions:(NSDictionary * __nullable)options
resolver:(RCTPromiseResolveBlock)resolve
rejecter:(RCTPromiseRejectBlock)reject)
{
OSStatus osStatus = [self deleteCredentialsForServer:server];

Expand All @@ -453,8 +488,11 @@ - (OSStatus)deleteCredentialsForServer:(NSString *)server
}

#if TARGET_OS_IOS && !TARGET_OS_UIKITFORMAC
RCT_EXPORT_METHOD(requestSharedWebCredentials:(RCTPromiseResolveBlock)resolve rejecter:(RCTPromiseRejectBlock)reject)
RCT_EXPORT_METHOD(requestSharedWebCredentials:(NSDictionary * __nullable)options
resolver:(RCTPromiseResolveBlock)resolve
rejecter:(RCTPromiseRejectBlock)reject)
{
/* Options can be used for specifying the [domain & account == server & username] */
SecRequestSharedWebCredential(NULL, NULL, ^(CFArrayRef credentials, CFErrorRef error) {
if (error != NULL) {
NSError *nsError = (__bridge NSError *)error;
Expand All @@ -470,15 +508,21 @@ - (OSStatus)deleteCredentialsForServer:(NSString *)server
return resolve(@{
@"server": server,
@"username": username,
@"password": password
@"password": password,
@"storage": @"keychain"
});
}
return resolve(@(NO));
});
}


RCT_EXPORT_METHOD(setSharedWebCredentialsForServer:(NSString *)server withUsername:(NSString *)username withPassword:(NSString *)password resolver:(RCTPromiseResolveBlock)resolve rejecter:(RCTPromiseRejectBlock)reject)
RCT_EXPORT_METHOD(setSharedWebCredentialsForServer:(NSString *)server
withUsername:(NSString *)username
withPassword:(NSString *)password
withOptions:(NSDictionary * __nullable)options
resolver:(RCTPromiseResolveBlock)resolve
rejecter:(RCTPromiseRejectBlock)reject)
{
SecAddSharedWebCredential(
(__bridge CFStringRef)server,
Expand Down
5 changes: 5 additions & 0 deletions android/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,11 @@
react-native start --reset-cache
```

```bash
# set working dir to: 'react-native-keychain/KeychainExample'
/usr/bin/env node node_modules/.bin/react-native start --reset-cache
```

> Important! to set checkbox `Allow parallel run`.
### Create Automatic self-refreshed TCP ports binding
Expand Down
Loading

0 comments on commit fa43f37

Please sign in to comment.