Browse Source

Add Keychain Sharing capability

yjang 6 years ago
parent
commit
1563edaaca
5 changed files with 83 additions and 23 deletions
  1. 15 7
      README.md
  2. 1 1
      plugin.xml
  3. 2 2
      src/ios/A0SimpleKeychain.m
  4. 48 3
      src/ios/CDVKeychain.m
  5. 17 10
      www/keychain.js

+ 15 - 7
README.md

@@ -2,12 +2,15 @@ Keychain Plugin for Apache Cordova
 =====================================
 created by Shazron Abdullah
 
-Updated by Max Lynch <max@ionic.io>
+Updated by Max Lynch max@ionic.io
+
+Updated by [YJ Ang](https://github.com/yj-ang)
+ - Add on: Keychain Sharing capabilities
 
 ### Installation
 
 ```shell
-cordova plugin add https://github.com/ionic-team/cordova-plugin-ios-keychain
+cordova plugin add https://github.com/yj-ang/cordova-plugin-ios-keychain
 ```
 
 ### iCloud keychain enabled
@@ -26,8 +29,9 @@ See the **example** folder for example usage.
  @param failureCallback returns the error string as the argument to the callback, for a failure
  @param key the key to retrieve
  @param TouchIDMessage the message to show underneath the TouchID prompt (if any)
+ @param keychainGroup The name of keychain group (Optional)
  */
-Keychain.get(successCallback, failureCallback, 'key', 'TouchID Message');
+Keychain.get(successCallback, failureCallback, 'key', 'TouchID Message', 'com.group.sharedKeysApp1andApp2');
 
 /*
  Sets a value for a key
@@ -37,8 +41,9 @@ Keychain.get(successCallback, failureCallback, 'key', 'TouchID Message');
  @param key the key to set
  @param value the value to set
  @param useTouchID whether to store the value with security such that TouchID will be needed to grab it
+ @param keychainGroup The name of keychain group (Optional)
  */
-Keychain.set(successCallback, failureCallback, 'key', 'value', useTouchID);
+Keychain.set(successCallback, failureCallback, 'key', 'value', useTouchID, 'com.group.sharedKeysApp1andApp2');
 
 /*
  Removes a value for a key
@@ -46,8 +51,9 @@ Keychain.set(successCallback, failureCallback, 'key', 'value', useTouchID);
  @param successCallback returns when successful
  @param failureCallback returns the error string as the argument to the callback
  @param key the key to remove
+ @param keychainGroup The name of keychain group (Optional)
  */
-Keychain.remove(successCallback, failureCallback, 'key');
+Keychain.remove(successCallback, failureCallback, 'key', 'com.group.sharedKeysApp1andApp2');
 
 /*
  Sets a JSON value for a key 
@@ -57,8 +63,9 @@ Keychain.remove(successCallback, failureCallback, 'key');
  @param key the key to set
  @param value the value to set
  @param useTouchID whether to store the value with security such that TouchID will be needed to grab it
+ @param keychainGroup The name of keychain group (Optional)
  */
-Keychain.setJson(successCallback, failureCallback, 'key', 'value', useTouchID);
+Keychain.setJson(successCallback, failureCallback, 'key', 'value', useTouchID, 'com.group.sharedKeysApp1andApp2');
 
 /*
  Gets a JSON value for a key 
@@ -68,8 +75,9 @@ Keychain.setJson(successCallback, failureCallback, 'key', 'value', useTouchID);
  @param key the key to set
  @param value the value to set
  @param useTouchID whether to store the value with security such that TouchID will be needed to grab it
+ @param keychainGroup The name of keychain group (Optional)
  */
-Keychain.getJson(successCallback, failureCallback, 'key', useTouchID);
+Keychain.getJson(successCallback, failureCallback, 'key', useTouchID 'com.group.sharedKeysApp1andApp2');
 ```
 
 ### License 

+ 1 - 1
plugin.xml

@@ -1,7 +1,7 @@
 <?xml version="1.0" encoding="UTF-8"?>
 <plugin xmlns="http://apache.org/cordova/ns/plugins/1.0"
     id="cordova-plugin-ios-keychain"
-    version="3.0.1">
+    version="3.0.2">
 
     <name>KeyChain Plugin for Cordova iOS</name>
     <description>This plugin allows your app access to the iOS KeyChain from Cordova. See: https://developer.apple.com/library/mac/documentation/security/conceptual/keychainServConcepts/iPhoneTasks/iPhoneTasks.html</description>

+ 2 - 2
src/ios/A0SimpleKeychain.m

@@ -280,11 +280,11 @@
                                          (__bridge id)kSecClass: (__bridge id)kSecClassGenericPassword,
                                          (__bridge id)kSecAttrService: self.service,
                                          } mutableCopy];
-#if !TARGET_IPHONE_SIMULATOR
+// #if !TARGET_IPHONE_SIMULATOR
     if (self.accessGroup) {
         attributes[(__bridge id)kSecAttrAccessGroup] = self.accessGroup;
     }
-#endif
+// #endif
 
     return attributes;
 }

+ 48 - 3
src/ios/CDVKeychain.m

@@ -36,13 +36,21 @@
 
     NSString *key = [arguments objectAtIndex:0];
     NSString *touchIDMessage = [arguments objectAtIndex:1];
+    NSString *keychainGroup = [arguments objectAtIndex:2];
 
     NSString *message = NSLocalizedString(@"Please Authenticate", nil);
     if(![touchIDMessage isEqual:[NSNull null]]) {
       message = NSLocalizedString(touchIDMessage, @"Prompt TouchID message");
     }
 
-    A0SimpleKeychain *keychain = [A0SimpleKeychain keychain];
+    A0SimpleKeychain *keychain = nil;
+    
+    if (keychainGroup == [NSNull null]) {
+      keychain = [A0SimpleKeychain keychain];
+    } else {
+      NSString *accessGroup = [NSString stringWithFormat: @"%@%@%@", [self getBundleSeedID], @".", keychainGroup];
+      keychain = [A0SimpleKeychain keychainWithService: keychainGroup accessGroup: accessGroup];
+    }
 
     keychain.useAccessControl = YES;
     keychain.defaultAccessiblity = A0SimpleKeychainItemAccessibleWhenPasscodeSetThisDeviceOnly;
@@ -69,8 +77,16 @@
     NSString* key = [arguments objectAtIndex:0];
     NSString* value = [arguments objectAtIndex:1];
     BOOL useTouchID = [[arguments objectAtIndex:2] boolValue];
+    NSString *keychainGroup = [arguments objectAtIndex:3];
    
-    A0SimpleKeychain *keychain = [A0SimpleKeychain keychain];
+    A0SimpleKeychain *keychain = nil;
+    
+    if (keychainGroup == [NSNull null]) {
+      keychain = [A0SimpleKeychain keychain];
+    } else {
+      NSString *accessGroup = [NSString stringWithFormat: @"%@%@%@", [self getBundleSeedID], @".", keychainGroup];
+      keychain = [A0SimpleKeychain keychainWithService: keychainGroup accessGroup: accessGroup];
+    }
 
     if(useTouchID) {
       keychain.useAccessControl = YES;
@@ -97,8 +113,17 @@
     }
 
     NSString *key = [arguments objectAtIndex:0];
+    NSString *keychainGroup = [arguments objectAtIndex:1];
+   
+    A0SimpleKeychain *keychain = nil;
+    
+    if (keychainGroup == [NSNull null]) {
+      keychain = [A0SimpleKeychain keychain];
+    } else {
+      NSString *accessGroup = [NSString stringWithFormat: @"%@%@%@", [self getBundleSeedID], @".", keychainGroup];
+      keychain = [A0SimpleKeychain keychainWithService: keychainGroup accessGroup: accessGroup];
+    }
 
-    A0SimpleKeychain *keychain = [A0SimpleKeychain keychain];
     [keychain deleteEntryForKey:key];
 
     pluginResult = [CDVPluginResult resultWithStatus:CDVCommandStatus_OK];
@@ -106,4 +131,24 @@
   }];
 }
 
+- (NSString *) getBundleSeedID {
+  NSDictionary *query = [NSDictionary dictionaryWithObjectsAndKeys:
+                         (__bridge NSString *)kSecClassGenericPassword, (__bridge NSString *)kSecClass,
+                         @"bundleSeedID", kSecAttrAccount,
+                         @"", kSecAttrService,
+                         (id)kCFBooleanTrue, kSecReturnAttributes,
+                         nil];
+  CFDictionaryRef result = nil;
+  OSStatus status = SecItemCopyMatching((__bridge CFDictionaryRef)query, (CFTypeRef *)&result);
+  if (status == errSecItemNotFound)
+      status = SecItemAdd((__bridge CFDictionaryRef)query, (CFTypeRef *)&result);
+  if (status != errSecSuccess)
+      return nil;
+  NSString *accessGroup = [(__bridge NSDictionary *)result objectForKey:(__bridge NSString *)kSecAttrAccessGroup];
+  NSArray *components = [accessGroup componentsSeparatedByString:@"."];
+  NSString *bundleSeedID = [[components objectEnumerator] nextObject];
+  CFRelease(result);
+  return bundleSeedID;
+}
+
 @end

+ 17 - 10
www/keychain.js

@@ -25,14 +25,19 @@ var exec = require('cordova/exec');
 var Keychain = {
 	serviceName: "Keychain",
 
-	get: function(success, error, key, touchIDMessage) {
-		exec(success, error, this.serviceName, "get", [key, touchIDMessage]);
+	get: function(success, error, key, touchIDMessage, keychainGroup) {
+		touchIDMessage = !touchIDMessage ? null : touchIDMessage;
+		keychainGroup = !keychainGroup ? null : keychainGroup;
+		exec(success, error, this.serviceName, "get", [key, touchIDMessage, keychainGroup]);
 	},
-	set: function(success, error, key, value, useTouchID) {
-		exec(success, error, this.serviceName, "set", [key, value, !!useTouchID]);
+
+	set: function(success, error, key, value, useTouchID, keychainGroup) {
+		keychainGroup = !keychainGroup ? null : keychainGroup;
+		exec(success, error, this.serviceName, "set", [key, value, !!useTouchID, keychainGroup]);
 	},
 
-	setJson: function(success, error, key, obj, useTouchID) {
+	setJson: function(success, error, key, obj, useTouchID, keychainGroup) {
+		keychainGroup = !keychainGroup ? null : keychainGroup;
 		var value = JSON.stringify(obj);
 		value = value
 			.replace(/[\\]/g, '\\\\')
@@ -44,10 +49,11 @@ var Keychain = {
 			.replace(/[\r]/g, '\\r')
 			.replace(/[\t]/g, '\\t');
 
-		exec(success, error, this.serviceName, "set", [key, value, !!useTouchID]);
+		exec(success, error, this.serviceName, "set", [key, value, !!useTouchID, keychainGroup]);
 	},
 
-	getJson: function(success, error, key, touchIDMessage) {
+	getJson: function(success, error, key, touchIDMessage, keychainGroup) {
+		keychainGroup = !keychainGroup ? null : keychainGroup;
 		var cb = function(v) {
 			if(!v) {
 				success(null);
@@ -62,11 +68,12 @@ var Keychain = {
 				error(e);
 			}
 		};
-		exec(cb, error, this.serviceName, "get", [key, touchIDMessage]);
+		exec(cb, error, this.serviceName, "get", [key, touchIDMessage, keychainGroup]);
 	},
 
-	remove: function(successCallback, failureCallback, key) {
-		exec(successCallback, failureCallback, this.serviceName, "remove", [key]);
+	remove: function(successCallback, failureCallback, key, keychainGroup) {
+		keychainGroup = !keychainGroup ? null : keychainGroup;
+		exec(successCallback, failureCallback, this.serviceName, "remove", [key, keychainGroup]);
 	}
 };