diff --git a/KeychainExample/App.js b/KeychainExample/App.js
index dc5cf01..d5fd757 100644
--- a/KeychainExample/App.js
+++ b/KeychainExample/App.js
@@ -2,6 +2,7 @@ import React, { Component } from 'react';
import {
KeyboardAvoidingView,
Platform,
+ SegmentedControlIOS,
StyleSheet,
Text,
TextInput,
@@ -11,12 +12,16 @@ import {
import * as Keychain from 'react-native-keychain';
+const ACCESS_CONTROL_OPTIONS = ['None', 'Passcode', 'Password'];
+const ACCESS_CONTROL_MAP = [null, Keychain.ACCESS_CONTROL.DEVICE_PASSCODE, Keychain.ACCESS_CONTROL.APPLICATION_PASSWORD, Keychain.ACCESS_CONTROL.BIOMETRY_CURRENT_SET]
+
export default class KeychainExample extends Component {
state = {
username: '',
password: '',
status: '',
biometryType: null,
+ accessControl: null,
};
componentDidMount() {
@@ -25,24 +30,13 @@ export default class KeychainExample extends Component {
});
}
- async save() {
+ async save(accessControl) {
try {
- if (this.state.biometryType) {
- await Keychain.setPasswordWithAuthentication(
- this.state.username,
- this.state.password,
- {
- accessControl:
- Keychain.ACCESS_CONTROL.TOUCH_ID_ANY_OR_DEVICE_PASSCODE,
- authenticationType: Keychain.AUTHENTICATION_TYPE.BIOMETRICS,
- }
- );
- } else {
- await Keychain.setGenericPassword(
- this.state.username,
- this.state.password
- );
- }
+ await Keychain.setGenericPassword(
+ this.state.username,
+ this.state.password,
+ { accessControl: this.state.accessControl }
+ );
this.setState({ status: 'Credentials saved!' });
} catch (err) {
this.setState({ status: 'Could not save credentials, ' + err });
@@ -51,13 +45,7 @@ export default class KeychainExample extends Component {
async load() {
try {
- const credentials = await (this.state.biometryType
- ? Keychain.getPasswordWithAuthentication({
- accessControl:
- Keychain.ACCESS_CONTROL.TOUCH_ID_ANY_OR_DEVICE_PASSCODE,
- authenticationType: Keychain.AUTHENTICATION_TYPE.BIOMETRICS,
- })
- : Keychain.getGenericPassword());
+ const credentials = await Keychain.getGenericPassword();
if (credentials) {
this.setState({ ...credentials, status: 'Credentials loaded!' });
} else {
@@ -113,14 +101,24 @@ export default class KeychainExample extends Component {
underlineColorAndroid="transparent"
/>
+ {Platform.OS === 'ios' && (
+
+ Access Control
+ {
+ this.setState({
+ accessControl: ACCESS_CONTROL_MAP[nativeEvent.selectedSegmentIndex],
+ });
+ }}
+ />
+
+ )}
{!!this.state.status && (
{this.state.status}
)}
- {!!this.state.biometryType && (
-
- Supported biometry: {this.state.biometryType}
-
- )}
+
this.save()}
@@ -156,12 +154,11 @@ export default class KeychainExample extends Component {
const styles = StyleSheet.create({
container: {
flex: 1,
- alignItems: 'center',
justifyContent: 'center',
backgroundColor: '#F5FCFF',
},
content: {
- width: 250,
+ marginHorizontal: 20,
},
title: {
fontSize: 28,
diff --git a/KeychainExample/ios/KeychainExample/AppDelegate.m b/KeychainExample/ios/KeychainExample/AppDelegate.m
index 3ba5994..2112a27 100644
--- a/KeychainExample/ios/KeychainExample/AppDelegate.m
+++ b/KeychainExample/ios/KeychainExample/AppDelegate.m
@@ -11,26 +11,9 @@
#import
#import
-#import
-
-@interface AppDelegate()
-
-@end
@implementation AppDelegate
-@synthesize willPromptForAuthentication = _willPromptForAuthentication;
-
-- (void)setWillPromptForAuthentication:(BOOL)willPromptForAuthentication {
- _willPromptForAuthentication = willPromptForAuthentication;
-
- if (willPromptForAuthentication) {
- NSLog(@"APPDELEGATE::: will prompt TouchId");
- } else {
- NSLog(@"APPDELEGATE::: ended prompt TouchId");
- }
-}
-
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
NSURL *jsCodeLocation;
diff --git a/README.md b/README.md
index 0471de5..9911738 100644
--- a/README.md
+++ b/README.md
@@ -127,12 +127,12 @@ Keychain
| Key | Applies to | Description | Default |
|---|---|---|---|
-|**`accessControl`**|`PasswordWithAuthentication`|This dictates how a keychain item may be used, see possible values in `Keychain.ACCESS_CONTROL`. |*`Keychain.ACCESS_CONTROL.TOUCH_ID_CURRENT_SET_OR_DEVICE_PASSCODE`*|
-|**`accessible`**|`GenericPassword`, `InternetCredentials`|This dictates when a keychain item is accessible, see possible values in `Keychain.ACCESSIBLE`. |*`Keychain.ACCESSIBLE.WHEN_UNLOCKED`*|
-|**`accessGroup`**|`GenericPassword`, `InternetCredentials`, `PasswordWithAuthentication`|In which App Group to share the keychain. Requires additional setup with entitlements. |*None*|
-|**`authenticationPrompt`**|`PasswordWithAuthentication`|What to prompt the user when unlocking the keychain with biometry or device password. |`Authenticate to retrieve secret!`|
+|**`accessControl`**|`setGenericPassword`, `setInternetCredentials`|This dictates how a keychain item may be used, see possible values in `Keychain.ACCESS_CONTROL`. |*None*|
+|**`accessible`**|`setGenericPassword`, `setInternetCredentials`|This dictates when a keychain item is accessible, see possible values in `Keychain.ACCESSIBLE`. |*`Keychain.ACCESSIBLE.WHEN_UNLOCKED`*|
+|**`accessGroup`**|`setGenericPassword`, `setInternetCredentials`|In which App Group to share the keychain. Requires additional setup with entitlements. |*None*|
+|**`authenticationPrompt`**|`getGenericPassword`, `getInternetCredentials`|What to prompt the user when unlocking the keychain with biometry or device password. |`Authenticate to retrieve secret`|
|**`authenticationType`**|`canImplyAuthentication`|Policies specifying which forms of authentication are acceptable. |`Keychain.AUTHENTICATION_TYPE.DEVICE_PASSCODE_OR_BIOMETRICS`|
-|**`service`**|`GenericPassword`, `PasswordWithAuthentication`|Reverse domain name qualifier for the service associated with password. |*App bundle ID*|
+|**`service`**|`setGenericPassword`, `getGenericPassword`|Reverse domain name qualifier for the service associated with password. |*App bundle ID*|
#### `Keychain.ACCESS_CONTROL` enum
diff --git a/RNKeychain.xcodeproj/project.pbxproj b/RNKeychain.xcodeproj/project.pbxproj
index 3a2f04a..b2c4692 100644
--- a/RNKeychain.xcodeproj/project.pbxproj
+++ b/RNKeychain.xcodeproj/project.pbxproj
@@ -10,11 +10,7 @@
5D82368F1B0CE3CB005A9EF3 /* RNKeychainManager.m in Sources */ = {isa = PBXBuildFile; fileRef = 5D82368C1B0CE2A6005A9EF3 /* RNKeychainManager.m */; };
5D8236911B0CE3D6005A9EF3 /* Security.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 5D8236901B0CE3D6005A9EF3 /* Security.framework */; };
5DE632D52043423E004F9598 /* LocalAuthentication.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 5DE632D42043423E004F9598 /* LocalAuthentication.framework */; };
- 5DE632D720434276004F9598 /* RNKeychainAuthenticationListener.h in Copy Headers */ = {isa = PBXBuildFile; fileRef = 5DE632D62043426A004F9598 /* RNKeychainAuthenticationListener.h */; };
- 5DE632D920434291004F9598 /* RNKeychainAuthenticationListener.h in Headers */ = {isa = PBXBuildFile; fileRef = 5DE632D62043426A004F9598 /* RNKeychainAuthenticationListener.h */; };
5DE632DB204342AE004F9598 /* Security.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 5DE632DA204342AE004F9598 /* Security.framework */; };
- 5DE632DC204342B5004F9598 /* RNKeychainAuthenticationListener.h in Copy Headers */ = {isa = PBXBuildFile; fileRef = 5DE632D62043426A004F9598 /* RNKeychainAuthenticationListener.h */; };
- 5DE632DE204342C3004F9598 /* RNKeychainAuthenticationListener.h in Headers */ = {isa = PBXBuildFile; fileRef = 5DE632D62043426A004F9598 /* RNKeychainAuthenticationListener.h */; };
6478986B1F38BFA100DA1C12 /* RNKeychainManager.m in Sources */ = {isa = PBXBuildFile; fileRef = 5D82368C1B0CE2A6005A9EF3 /* RNKeychainManager.m */; };
/* End PBXBuildFile section */
@@ -25,7 +21,6 @@
dstPath = "include/$(PRODUCT_NAME)";
dstSubfolderSpec = 16;
files = (
- 5DE632D720434276004F9598 /* RNKeychainAuthenticationListener.h in Copy Headers */,
);
name = "Copy Headers";
runOnlyForDeploymentPostprocessing = 0;
@@ -36,7 +31,6 @@
dstPath = "include/$(PRODUCT_NAME)";
dstSubfolderSpec = 16;
files = (
- 5DE632DC204342B5004F9598 /* RNKeychainAuthenticationListener.h in Copy Headers */,
);
name = "Copy Headers";
runOnlyForDeploymentPostprocessing = 0;
@@ -46,10 +40,9 @@
/* Begin PBXFileReference section */
5D82366F1B0CE05B005A9EF3 /* libRNKeychain.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = libRNKeychain.a; sourceTree = BUILT_PRODUCTS_DIR; };
5D82368B1B0CE2A6005A9EF3 /* RNKeychainManager.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RNKeychainManager.h; sourceTree = ""; };
- 5D82368C1B0CE2A6005A9EF3 /* RNKeychainManager.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RNKeychainManager.m; sourceTree = ""; };
+ 5D82368C1B0CE2A6005A9EF3 /* RNKeychainManager.m */ = {isa = PBXFileReference; fileEncoding = 4; indentWidth = 2; lastKnownFileType = sourcecode.c.objc; path = RNKeychainManager.m; sourceTree = ""; tabWidth = 2; };
5D8236901B0CE3D6005A9EF3 /* Security.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Security.framework; path = System/Library/Frameworks/Security.framework; sourceTree = SDKROOT; };
5DE632D42043423E004F9598 /* LocalAuthentication.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = LocalAuthentication.framework; path = System/Library/Frameworks/LocalAuthentication.framework; sourceTree = SDKROOT; };
- 5DE632D62043426A004F9598 /* RNKeychainAuthenticationListener.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RNKeychainAuthenticationListener.h; sourceTree = ""; };
5DE632DA204342AE004F9598 /* Security.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Security.framework; path = Platforms/AppleTVOS.platform/Developer/SDKs/AppleTVOS11.2.sdk/System/Library/Frameworks/Security.framework; sourceTree = DEVELOPER_DIR; };
6478985F1F38BF9100DA1C12 /* libRNKeychain.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = libRNKeychain.a; sourceTree = BUILT_PRODUCTS_DIR; };
/* End PBXFileReference section */
@@ -97,7 +90,6 @@
5D82368A1B0CE2A6005A9EF3 /* RNKeychainManager */ = {
isa = PBXGroup;
children = (
- 5DE632D62043426A004F9598 /* RNKeychainAuthenticationListener.h */,
5D82368B1B0CE2A6005A9EF3 /* RNKeychainManager.h */,
5D82368C1B0CE2A6005A9EF3 /* RNKeychainManager.m */,
);
@@ -121,7 +113,6 @@
isa = PBXHeadersBuildPhase;
buildActionMask = 2147483647;
files = (
- 5DE632D920434291004F9598 /* RNKeychainAuthenticationListener.h in Headers */,
);
runOnlyForDeploymentPostprocessing = 0;
};
@@ -129,7 +120,6 @@
isa = PBXHeadersBuildPhase;
buildActionMask = 2147483647;
files = (
- 5DE632DE204342C3004F9598 /* RNKeychainAuthenticationListener.h in Headers */,
);
runOnlyForDeploymentPostprocessing = 0;
};
diff --git a/RNKeychainManager/RNKeychainAuthenticationListener.h b/RNKeychainManager/RNKeychainAuthenticationListener.h
deleted file mode 100644
index 214f07d..0000000
--- a/RNKeychainManager/RNKeychainAuthenticationListener.h
+++ /dev/null
@@ -1,22 +0,0 @@
-//
-// TouchIdPromptListener.h
-// RNKeychain
-//
-// Created by Steffen Blümm on 05/04/17.
-// Copyright © 2017 Joel Arvidsson. All rights reserved.
-//
-
-#import
-
-/**
- This is a protocol to be implemented by the AppDelegate in case
- the AppDelegate takes precautions to obfuscate the screen when
- the app resigns active state.
- Thus the AppDelegate can avoid to obfuscating the screen when
- the TouchId-prompt is brought up by the OS
- */
-@protocol RNKeychainAuthenticationListener
-
-@property (nonatomic, assign) BOOL willPromptForAuthentication;
-
-@end
diff --git a/RNKeychainManager/RNKeychainManager.m b/RNKeychainManager/RNKeychainManager.m
index 8fbedb6..381981b 100644
--- a/RNKeychainManager/RNKeychainManager.m
+++ b/RNKeychainManager/RNKeychainManager.m
@@ -15,8 +15,6 @@
#import
#import
-#import "RNKeychainAuthenticationListener.h"
-
@implementation RNKeychainManager
@synthesize bridge = _bridge;
@@ -148,43 +146,69 @@ LAPolicy authPolicy(NSDictionary *options)
return LAPolicyDeviceOwnerAuthentication;
}
-SecAccessControlCreateFlags secureAccessControl(NSDictionary *options)
+SecAccessControlCreateFlags accessControlValue(NSDictionary *options)
{
- if (options && options[kAccessControlType]) {
- if ([ options[kAccessControlType] isEqualToString: kAccessControlUserPresence ]) {
+ if (options && options[kAccessControlType] && [options[kAccessControlType] isKindOfClass:[NSString class]]) {
+ if ([options[kAccessControlType] isEqualToString: kAccessControlUserPresence]) {
return kSecAccessControlUserPresence;
}
- else if ([ options[kAccessControlType] isEqualToString: kAccessControlBiometryAny ]) {
+ else if ([options[kAccessControlType] isEqualToString: kAccessControlBiometryAny]) {
return kSecAccessControlTouchIDAny;
}
- else if ([ options[kAccessControlType] isEqualToString: kAccessControlBiometryCurrentSet ]) {
+ else if ([options[kAccessControlType] isEqualToString: kAccessControlBiometryCurrentSet]) {
return kSecAccessControlTouchIDCurrentSet;
}
- else if ([ options[kAccessControlType] isEqualToString: kAccessControlDevicePasscode ]) {
+ else if ([options[kAccessControlType] isEqualToString: kAccessControlDevicePasscode]) {
return kSecAccessControlDevicePasscode;
}
- else if ([ options[kAccessControlType] isEqualToString: kAccessControlBiometryAnyOrDevicePasscode ]) {
+ else if ([options[kAccessControlType] isEqualToString: kAccessControlBiometryAnyOrDevicePasscode]) {
return kSecAccessControlTouchIDAny|kSecAccessControlOr|kSecAccessControlDevicePasscode;
}
- else if ([ options[kAccessControlType] isEqualToString: kAccessControlBiometryCurrentSetOrDevicePasscode ]) {
+ else if ([options[kAccessControlType] isEqualToString: kAccessControlBiometryCurrentSetOrDevicePasscode]) {
return kSecAccessControlTouchIDCurrentSet|kSecAccessControlOr|kSecAccessControlDevicePasscode;
}
- else if ([ options[kAccessControlType] isEqualToString: kAccessControlApplicationPassword ]) {
+ else if ([options[kAccessControlType] isEqualToString: kAccessControlApplicationPassword]) {
return kSecAccessControlApplicationPassword;
}
}
- return kSecAccessControlTouchIDCurrentSet|kSecAccessControlOr|kSecAccessControlDevicePasscode;
+ return 0;
}
-//LAPolicyDeviceOwnerAuthenticationWithBiometrics | LAPolicyDeviceOwnerAuthentication
-
-- (void)insertKeychainEntry:(NSDictionary *)attributes withAccessGroup:(NSString * __nullable)accessGroup resolver:(RCTPromiseResolveBlock)resolve rejecter:(RCTPromiseRejectBlock)reject
+- (void)insertKeychainEntry:(NSDictionary *)attributes withOptions:(NSDictionary * __nullable)options resolver:(RCTPromiseResolveBlock)resolve rejecter:(RCTPromiseRejectBlock)reject
{
- if (accessGroup != nil) {
- NSMutableDictionary *mAttributes = attributes.mutableCopy;
- mAttributes[(__bridge NSString *)kSecAttrAccessGroup] = accessGroup;
- attributes = [NSDictionary dictionaryWithDictionary:mAttributes];
+ NSString *accessGroup = accessGroupValue(options);
+ CFStringRef accessible = accessibleValue(options);
+ SecAccessControlCreateFlags accessControl = accessControlValue(options);
+
+ NSMutableDictionary *mAttributes = attributes.mutableCopy;
+
+ if (accessControl) {
+ NSError *aerr = nil;
+ BOOL canAuthenticate = [[LAContext new] canEvaluatePolicy:LAPolicyDeviceOwnerAuthentication error:&aerr];
+ if (aerr || !canAuthenticate) {
+ return rejectWithError(reject, aerr);
+ }
+
+ CFErrorRef error = NULL;
+ SecAccessControlRef sacRef = SecAccessControlCreateWithFlags(kCFAllocatorDefault,
+ accessible,
+ accessControl,
+ &error);
+
+ if (error) {
+ return rejectWithError(reject, aerr);
+ }
+ mAttributes[(__bridge NSString *)kSecAttrAccessControl] = (__bridge id)sacRef;
+ } else {
+ mAttributes[(__bridge NSString *)kSecAttrAccessible] = (__bridge id)accessible;
}
+
+ if (accessGroup != nil) {
+ mAttributes[(__bridge NSString *)kSecAttrAccessGroup] = accessGroup;
+ }
+
+ attributes = [NSDictionary dictionaryWithDictionary:mAttributes];
+
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
OSStatus osStatus = SecItemAdd((__bridge CFDictionaryRef) attributes, NULL);
@@ -223,7 +247,7 @@ SecAccessControlCreateFlags secureAccessControl(NSDictionary *options)
return SecItemDelete((__bridge CFDictionaryRef) query);
}
-#pragma mark - Proposed functionality - RCT_EXPORT_METHOD
+#pragma mark - RNKeychain
#if TARGET_OS_IOS
RCT_EXPORT_METHOD(canCheckAuthentication:(NSDictionary *)options resolver:(RCTPromiseResolveBlock)resolve rejecter:(RCTPromiseRejectBlock)reject)
@@ -261,129 +285,28 @@ RCT_EXPORT_METHOD(getSupportedBiometryType:(RCTPromiseResolveBlock)resolve rejec
}
#endif
-
-RCT_EXPORT_METHOD(setPasswordWithAuthentication:(NSDictionary *)options withUsername:(NSString *)username withPassword:(NSString *)password resolver:(RCTPromiseResolveBlock)resolve rejecter:(RCTPromiseRejectBlock)reject)
-{
- NSString *service = serviceValue(options);
- NSError *aerr = nil;
- BOOL canAuthenticate = [[LAContext new] canEvaluatePolicy:LAPolicyDeviceOwnerAuthentication error:&aerr];
- if (aerr || !canAuthenticate) {
- return rejectWithError(reject, aerr);
- }
-
- [self deletePasswordsForService:service];
-
- CFErrorRef error = NULL;
- SecAccessControlRef sacRef = SecAccessControlCreateWithFlags(kCFAllocatorDefault,
- kSecAttrAccessibleWhenUnlockedThisDeviceOnly, //kSecAttrAccessibleWhenPasscodeSetThisDeviceOnly,
- secureAccessControl(options),
- &error);
-
- if (error) {
- // ok: failed
- return rejectWithError(reject, aerr);
- }
-
- NSDictionary *attributes = @{
- (__bridge NSString *)kSecClass: (__bridge id)(kSecClassGenericPassword),
- (__bridge NSString *)kSecAttrService: service,
- (__bridge NSString *)kSecAttrAccount: username,
- (__bridge NSString *)kSecValueData: [password dataUsingEncoding:NSUTF8StringEncoding],
- (__bridge NSString *)kSecAttrAccessControl: (__bridge id)sacRef
- };
-
- [self insertKeychainEntry:attributes withAccessGroup:accessGroupValue(options) resolver:resolve rejecter:reject];
-}
-
-RCT_EXPORT_METHOD(getPasswordWithAuthentication:(NSDictionary *)options resolver:(RCTPromiseResolveBlock)resolve rejecter:(RCTPromiseRejectBlock)reject)
-{
- NSString *service = serviceValue(options);
- NSString *promptMessage = @"Authenticate to retrieve secret!";
- if (options && options[kAuthenticationPromptMessage]) {
- promptMessage = options[kAuthenticationPromptMessage];
- }
-
- NSDictionary *query = @{
- (__bridge NSString *)kSecClass: (__bridge id)(kSecClassGenericPassword),
- (__bridge NSString *)kSecAttrService: service,
- (__bridge NSString *)kSecReturnAttributes: (__bridge id)kCFBooleanTrue,
- (__bridge NSString *)kSecReturnData: (__bridge id)kCFBooleanTrue,
- (__bridge NSString *)kSecMatchLimit: (__bridge NSString *)kSecMatchLimitOne,
- (__bridge NSString *)kSecUseOperationPrompt: promptMessage
- };
-
- // Notify AppDelegate
- dispatch_async(dispatch_get_main_queue(), ^{
- [self notifyAuthenticationListener: YES];
- });
-
- // Look up password for service in the keychain
- dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
- __block NSDictionary* found = nil;
- CFTypeRef foundTypeRef = NULL;
- OSStatus osStatus = SecItemCopyMatching((__bridge CFDictionaryRef) query, (CFTypeRef*)&foundTypeRef);
-
- dispatch_async(dispatch_get_main_queue(), ^{
- [self notifyAuthenticationListener: NO];
-
- if (osStatus != noErr && osStatus != errSecItemNotFound) {
- NSError *error = [NSError errorWithDomain:NSOSStatusErrorDomain code:osStatus userInfo:nil];
- return rejectWithError(reject, error);
- }
-
- found = (__bridge NSDictionary*)(foundTypeRef);
- if (!found) {
- return resolve(@(NO));
- }
-
- // Found
- NSString *username = (NSString *) [found objectForKey:(__bridge id)(kSecAttrAccount)];
- NSString *password = [[NSString alloc] initWithData:[found objectForKey:(__bridge id)(kSecValueData)] encoding:NSUTF8StringEncoding];
-
- return resolve(@{
- @"service": service,
- @"username": username,
- @"password": password
- });
- });
- });
-}
-
-- (void) notifyAuthenticationListener:(BOOL)willPresent {
- id appDelegate = [ UIApplication sharedApplication ].delegate;
-
- if ([ appDelegate conformsToProtocol:@protocol(RNKeychainAuthenticationListener) ]) {
- ((id)appDelegate).willPromptForAuthentication = willPresent;
- }
-}
-
-#pragma mark - RNKeychain
-
RCT_EXPORT_METHOD(setGenericPasswordForOptions:(NSDictionary *)options withUsername:(NSString *)username withPassword:(NSString *)password resolver:(RCTPromiseResolveBlock)resolve rejecter:(RCTPromiseRejectBlock)reject)
{
NSString *service = serviceValue(options);
-
- [self deletePasswordsForService:service];
-
- // Create dictionary of parameters to add
- NSDictionary *attributes = @{
+ NSDictionary *attributes = attributes = @{
(__bridge NSString *)kSecClass: (__bridge id)(kSecClassGenericPassword),
(__bridge NSString *)kSecAttrService: service,
(__bridge NSString *)kSecAttrAccount: username,
- (__bridge NSString *)kSecValueData: [password dataUsingEncoding:NSUTF8StringEncoding],
- (__bridge NSString *)kSecAttrAccessible: (__bridge id)accessibleValue(options)
+ (__bridge NSString *)kSecValueData: [password dataUsingEncoding:NSUTF8StringEncoding]
};
- [self insertKeychainEntry:attributes withAccessGroup:accessGroupValue(options) resolver:resolve rejecter:reject];
+ [self deletePasswordsForService:service];
+
+ [self insertKeychainEntry:attributes withOptions:options resolver:resolve rejecter:reject];
}
RCT_EXPORT_METHOD(getGenericPasswordForOptions:(NSDictionary *)options resolver:(RCTPromiseResolveBlock)resolve rejecter:(RCTPromiseRejectBlock)reject)
{
NSString *service = serviceValue(options);
-
- // secure compatibility with TouchId / Passcode secured stored items
- // http://stackoverflow.com/questions/42339000/ksecuseauthenticationuiskip-how-to-use-it
- // Silently skip any items that require user authentication. Only use this value with the SecItemCopyMatching function.
+ NSString *authenticationPrompt = @"Authenticate to retrieve secret";
+ if (options && options[kAuthenticationPromptMessage]) {
+ authenticationPrompt = options[kAuthenticationPromptMessage];
+ }
NSDictionary *query = @{
(__bridge NSString *)kSecClass: (__bridge id)(kSecClassGenericPassword),
@@ -391,7 +314,7 @@ RCT_EXPORT_METHOD(getGenericPasswordForOptions:(NSDictionary *)options resolver:
(__bridge NSString *)kSecReturnAttributes: (__bridge id)kCFBooleanTrue,
(__bridge NSString *)kSecReturnData: (__bridge id)kCFBooleanTrue,
(__bridge NSString *)kSecMatchLimit: (__bridge NSString *)kSecMatchLimitOne,
- (__bridge NSString *)kSecUseAuthenticationUI: (__bridge id)kSecUseAuthenticationUISkip
+ (__bridge NSString *)kSecUseOperationPrompt: authenticationPrompt
};
// Look up service in the keychain
@@ -443,26 +366,20 @@ RCT_EXPORT_METHOD(setInternetCredentialsForServer:(NSString *)server withUsernam
(__bridge NSString *)kSecClass: (__bridge id)(kSecClassInternetPassword),
(__bridge NSString *)kSecAttrServer: server,
(__bridge NSString *)kSecAttrAccount: username,
- (__bridge NSString *)kSecValueData: [password dataUsingEncoding:NSUTF8StringEncoding],
- (__bridge NSString *)kSecAttrAccessible: (__bridge id)accessibleValue(options)
+ (__bridge NSString *)kSecValueData: [password dataUsingEncoding:NSUTF8StringEncoding]
};
- [self insertKeychainEntry:attributes withAccessGroup:accessGroupValue(options) resolver:resolve rejecter:reject];
+ [self insertKeychainEntry:attributes withOptions:options resolver:resolve rejecter:reject];
}
RCT_EXPORT_METHOD(getInternetCredentialsForServer:(NSString *)server withOptions:(NSDictionary *)options resolver:(RCTPromiseResolveBlock)resolve rejecter:(RCTPromiseRejectBlock)reject)
{
- // secure compatibility with TouchId / Passcode secured stored items
- // http://stackoverflow.com/questions/42339000/ksecuseauthenticationuiskip-how-to-use-it
- // Silently skip any items that require user authentication. Only use this value with the SecItemCopyMatching function.
-
NSDictionary *query = @{
(__bridge NSString *)kSecClass: (__bridge id)(kSecClassInternetPassword),
(__bridge NSString *)kSecAttrServer: server,
(__bridge NSString *)kSecReturnAttributes: (__bridge id)kCFBooleanTrue,
(__bridge NSString *)kSecReturnData: (__bridge id)kCFBooleanTrue,
- (__bridge NSString *)kSecMatchLimit: (__bridge NSString *)kSecMatchLimitOne,
- (__bridge NSString *)kSecUseAuthenticationUI: (__bridge id)kSecUseAuthenticationUISkip
+ (__bridge NSString *)kSecMatchLimit: (__bridge NSString *)kSecMatchLimitOne
};
// Look up server in the keychain
@@ -502,7 +419,6 @@ RCT_EXPORT_METHOD(resetInternetCredentialsForServer:(NSString *)server withOptio
}
return resolve(@(YES));
-
}
#if TARGET_OS_IOS
diff --git a/index.js b/index.js
index 4cca283..90af9a7 100644
--- a/index.js
+++ b/index.js
@@ -41,12 +41,6 @@ type SecAccessible =
| 'AccessibleAfterFirstUnlockThisDeviceOnly'
| 'AccessibleAlwaysThisDeviceOnly';
-type Options = {
- accessGroup?: string,
- accessible?: SecAccessible,
- service?: string,
-};
-
type SecAccessControl =
| 'UserPresence'
| 'BiometryAny'
@@ -58,9 +52,10 @@ type SecAccessControl =
type LAPolicy = 'Authentication' | 'AuthenticationWithBiometrics';
-type SecureOptions = {
+type Options = {
accessControl?: SecAccessControl,
accessGroup?: string,
+ accessible?: SecAccessible,
authenticationPrompt?: string,
authenticationType?: LAPolicy,
service?: string,
@@ -72,7 +67,7 @@ type SecureOptions = {
* @param {object} options LAPolicy option, iOS only
* @return {Promise} Resolves to `true` when supported, otherwise `false`
*/
-export function canImplyAuthentication(options?: SecureOptions): Promise {
+export function canImplyAuthentication(options?: Options): Promise {
if (RNKeychainManager.canCheckAuthentication) {
return Promise.reject(
new Error(`canImplyAuthentication() is not supported on this platform`)
@@ -93,50 +88,6 @@ export function getSupportedBiometryType(): Promise {
return RNKeychainManager.getSupportedBiometryType();
}
-/**
- * Saves the `username` and `password` combination securely - needs authentication to retrieve it.
- * @param {string} username Associated username or e-mail to be saved.
- * @param {string} password Associated password to be saved.
- * @param {object} options Keychain options, iOS only
- * @return {Promise} Resolves to `true` when successful
- */
-export function setPasswordWithAuthentication(
- username: string,
- password: string,
- options?: SecureOptions
-): Promise {
- if (Platform.OS !== 'ios') {
- return Promise.reject(
- new Error(
- `setPasswordWithAuthentication() is not supported on ${Platform.OS} yet`
- )
- );
- }
- return RNKeychainManager.setPasswordWithAuthentication(
- options,
- username,
- password
- );
-}
-
-/**
- * Fetches login combination for `service` - demands for authentication if necessary.
- * @param {string|object} serviceOrOptions Reverse domain name qualifier for the service, defaults to `bundleId` or an options object.
- * @return {Promise} Resolves to `{ service, username, password }` when successful
- */
-export function getPasswordWithAuthentication(
- options?: SecureOptions
-): Promise {
- if (Platform.OS !== 'ios') {
- return Promise.reject(
- new Error(
- `getPasswordWithAuthentication() is not supported on ${Platform.OS} yet`
- )
- );
- }
- return RNKeychainManager.getPasswordWithAuthentication(options);
-}
-
/**
* Saves the `username` and `password` combination for `server`.
* @param {string} server URL to server.
@@ -185,7 +136,7 @@ export function resetInternetCredentials(
return RNKeychainManager.resetInternetCredentialsForServer(server, options);
}
-function getOptionsArgument(serviceOrOptions?: string | KeychainOptions) {
+function getOptionsArgument(serviceOrOptions?: string | Options) {
if (Platform.OS !== 'ios') {
return typeof serviceOrOptions === 'object'
? serviceOrOptions.service
@@ -206,7 +157,7 @@ function getOptionsArgument(serviceOrOptions?: string | KeychainOptions) {
export function setGenericPassword(
username: string,
password: string,
- serviceOrOptions?: string | KeychainOptions
+ serviceOrOptions?: string | Options
): Promise {
return RNKeychainManager.setGenericPasswordForOptions(
getOptionsArgument(serviceOrOptions),
@@ -221,7 +172,7 @@ export function setGenericPassword(
* @return {Promise} Resolves to `{ service, username, password }` when successful
*/
export function getGenericPassword(
- serviceOrOptions?: string | KeychainOptions
+ serviceOrOptions?: string | Options
): Promise {
return RNKeychainManager.getGenericPasswordForOptions(
getOptionsArgument(serviceOrOptions)
@@ -234,7 +185,7 @@ export function getGenericPassword(
* @return {Promise} Resolves to `true` when successful
*/
export function resetGenericPassword(
- serviceOrOptions?: string | KeychainOptions
+ serviceOrOptions?: string | Options
): Promise {
return RNKeychainManager.resetGenericPasswordForOptions(
getOptionsArgument(serviceOrOptions)
diff --git a/typings/react-native-keychain.d.ts b/typings/react-native-keychain.d.ts
index c4b65a0..c3373b6 100644
--- a/typings/react-native-keychain.d.ts
+++ b/typings/react-native-keychain.d.ts
@@ -11,7 +11,7 @@ declare module 'react-native-keychain' {
password: string;
}
- export interface SecureOptions {
+ export interface Options {
accessControl?: string;
accessGroup?: string;
authenticationPrompt?: string;
@@ -20,26 +20,17 @@ declare module 'react-native-keychain' {
}
function canImplyAuthentication(
- options?: SecureOptions
+ options?: Options
): Promise;
function getSupportedBiometryType(
): Promise;
- function setSecurePassword(
- username: string,
- password: string,
- options?: SecureOptions
- ): Promise;
-
- function getSecurePassword(
- options?: SecureOptions
- ): Promise;
-
function setInternetCredentials(
server: string,
username: string,
- password: string
+ password: string,
+ options?: Options
): Promise;
function getInternetCredentials(
@@ -53,15 +44,15 @@ declare module 'react-native-keychain' {
function setGenericPassword(
username: string,
password: string,
- service?: string
+ options?: Options
): Promise;
function getGenericPassword(
- service?: string
+ options?: Options
): Promise;
function resetGenericPassword(
- service?: string
+ options?: Options
): Promise
function requestSharedWebCredentials (