diff --git a/RNKeychainManager/RNKeychainManager.m b/RNKeychainManager/RNKeychainManager.m index 1050660..fdee2a7 100644 --- a/RNKeychainManager/RNKeychainManager.m +++ b/RNKeychainManager/RNKeychainManager.m @@ -70,12 +70,34 @@ NSString *codeForError(NSError *error) return [NSString stringWithFormat:@"%li", (long)error.code]; } + void rejectWithError(RCTPromiseRejectBlock reject, NSError *error) { return reject(codeForError(error), messageForError(error), nil); } -RCT_EXPORT_METHOD(setGenericPasswordForService:(NSString*)service withUsername:(NSString*)username withPassword:(NSString*)password resolver:(RCTPromiseResolveBlock)resolve rejecter:(RCTPromiseRejectBlock)reject){ +CFStringRef accessibleValue(NSString *jsAccessibleKey) +{ + if (jsAccessibleKey) { + NSDictionary *keyMap = @{ + @"AccessibleWhenUnlocked": (__bridge NSString *)kSecAttrAccessibleWhenUnlocked, + @"AccessibleAfterFirstUnlock": (__bridge NSString *)kSecAttrAccessibleAfterFirstUnlock, + @"AccessibleAlways": (__bridge NSString *)kSecAttrAccessibleAlways, + @"AccessibleWhenPasscodeSetThisDeviceOnly": (__bridge NSString *)kSecAttrAccessibleWhenPasscodeSetThisDeviceOnly, + @"AccessibleWhenUnlockedThisDeviceOnly": (__bridge NSString *)kSecAttrAccessibleWhenUnlockedThisDeviceOnly, + @"AccessibleAfterFirstUnlockThisDeviceOnly": (__bridge NSString *)kSecAttrAccessibleAfterFirstUnlockThisDeviceOnly, + @"AccessibleAlwaysThisDeviceOnly": (__bridge NSString *)kSecAttrAccessibleAlwaysThisDeviceOnly + }; + NSString *result = [keyMap valueForKey:jsAccessibleKey]; + if (result) { + return (__bridge CFStringRef)result; + + } + } + return kSecAttrAccessibleAfterFirstUnlock; +} + +RCT_EXPORT_METHOD(setGenericPasswordForService:(NSString*)service withUsername:(NSString*)username withPassword:(NSString*)password withAccessible:(NSString *)accessible resolver:(RCTPromiseResolveBlock)resolve rejecter:(RCTPromiseRejectBlock)reject){ if(service == nil) { service = [[NSBundle mainBundle] bundleIdentifier]; } @@ -88,7 +110,7 @@ RCT_EXPORT_METHOD(setGenericPasswordForService:(NSString*)service withUsername:( // Create dictionary of parameters to add NSData* passwordData = [password dataUsingEncoding:NSUTF8StringEncoding]; - dict = [NSDictionary dictionaryWithObjectsAndKeys:(__bridge id)(kSecClassGenericPassword), kSecClass, service, kSecAttrService, passwordData, kSecValueData, username, kSecAttrAccount, nil]; + dict = [NSDictionary dictionaryWithObjectsAndKeys:(__bridge id)(kSecClassGenericPassword), kSecClass, accessibleValue(accessible), kSecAttrAccessible, service, kSecAttrService, passwordData, kSecValueData, username, kSecAttrAccount, nil]; // Try to save to keychain osStatus = SecItemAdd((__bridge CFDictionaryRef) dict, NULL); @@ -156,7 +178,7 @@ RCT_EXPORT_METHOD(resetGenericPasswordForService:(NSString*)service resolver:(RC } -RCT_EXPORT_METHOD(setInternetCredentialsForServer:(NSString*)server withUsername:(NSString*)username withPassword:(NSString*)password resolver:(RCTPromiseResolveBlock)resolve rejecter:(RCTPromiseRejectBlock)reject){ +RCT_EXPORT_METHOD(setInternetCredentialsForServer:(NSString*)server withUsername:(NSString*)username withPassword:(NSString*)password withAccessible:(NSString *)accessible resolver:(RCTPromiseResolveBlock)resolve rejecter:(RCTPromiseRejectBlock)reject){ // Create dictionary of search parameters NSDictionary* dict = [NSDictionary dictionaryWithObjectsAndKeys:(__bridge id)(kSecClassInternetPassword), kSecClass, server, kSecAttrServer, kCFBooleanTrue, kSecReturnAttributes, nil]; @@ -165,7 +187,7 @@ RCT_EXPORT_METHOD(setInternetCredentialsForServer:(NSString*)server withUsername // Create dictionary of parameters to add NSData* passwordData = [password dataUsingEncoding:NSUTF8StringEncoding]; - dict = [NSDictionary dictionaryWithObjectsAndKeys:(__bridge id)(kSecClassInternetPassword), kSecClass, server, kSecAttrServer, passwordData, kSecValueData, username, kSecAttrAccount, nil]; + dict = [NSDictionary dictionaryWithObjectsAndKeys:(__bridge id)(kSecClassInternetPassword), kSecClass, accessibleValue(accessible), kSecAttrAccessible, server, kSecAttrServer, passwordData, kSecValueData, username, kSecAttrAccount, nil]; // Try to save to keychain osStatus = SecItemAdd((__bridge CFDictionaryRef) dict, NULL); diff --git a/android/src/main/java/com/oblador/keychain/KeychainModule.java b/android/src/main/java/com/oblador/keychain/KeychainModule.java index a6dccdd..7318685 100644 --- a/android/src/main/java/com/oblador/keychain/KeychainModule.java +++ b/android/src/main/java/com/oblador/keychain/KeychainModule.java @@ -43,7 +43,7 @@ public class KeychainModule extends ReactContextBaseJavaModule { } @ReactMethod - public void setGenericPasswordForService(String service, String username, String password, Promise promise) { + public void setGenericPasswordForService(String service, String username, String password, String accessible, Promise promise) { if (!crypto.isAvailable()) { Log.e(KEYCHAIN_MODULE, "Crypto is missing"); promise.reject("KeychainModule: crypto is missing"); @@ -134,8 +134,8 @@ public class KeychainModule extends ReactContextBaseJavaModule { } @ReactMethod - public void setInternetCredentialsForServer(@NonNull String server, String username, String password, Promise promise) { - setGenericPasswordForService(server, username, password, promise); + public void setInternetCredentialsForServer(@NonNull String server, String username, String password, String accessible, Promise promise) { + setGenericPasswordForService(server, username, password, accessible, promise); } @ReactMethod diff --git a/index.js b/index.js index 7008d56..f3f9404 100644 --- a/index.js +++ b/index.js @@ -1,19 +1,30 @@ import { NativeModules, Platform } from 'react-native'; const { RNKeychainManager } = NativeModules; +type SecAccessible = + | 'AccessibleWhenUnlocked' + | 'AccessibleAfterFirstUnlock' + | 'AccessibleAlways' + | 'AccessibleWhenPasscodeSetThisDeviceOnly' + | 'AccessibleWhenUnlockedThisDeviceOnly' + | 'AccessibleAfterFirstUnlockThisDeviceOnly' + | 'AccessibleAlwaysThisDeviceOnly' + /** * Saves the `username` and `password` combination for `server`. * @param {string} server URL to server. * @param {string} username Associated username or e-mail to be saved. * @param {string} password Associated password to be saved. + * @param {string} accessible (iOS only) kSecAccessibleKey * @return {Promise} Resolves to `true` when successful */ export function setInternetCredentials( server: string, username: string, - password: string + password: string, + accessible?: SecAccessible ): Promise { - return RNKeychainManager.setInternetCredentialsForServer(server, username, password); + return RNKeychainManager.setInternetCredentialsForServer(server, username, password, accessible); } /** @@ -43,14 +54,16 @@ export function resetInternetCredentials( * @param {string} username Associated username or e-mail to be saved. * @param {string} password Associated password to be saved. * @param {string} service Reverse domain name qualifier for the service, defaults to `bundleId`. + * @param {string} accessible (iOS only) kSecAccessibleKey * @return {Promise} Resolves to `true` when successful */ export function setGenericPassword( username: string, password: string, service?: string + accessible?: SecAccessible ): Promise { - return RNKeychainManager.setGenericPasswordForService(service, username, password); + return RNKeychainManager.setGenericPasswordForService(service, username, password, accessible); } /**