Refactor iOS code and add accessGroup to *PasswordWithAuthentication

This commit is contained in:
Joel Arvidsson 2018-02-25 23:53:42 +01:00
parent bb2adaedad
commit abed674800
3 changed files with 132 additions and 147 deletions

View File

@ -111,6 +111,14 @@ NSString *serviceValue(NSDictionary *options)
return [[NSBundle mainBundle] bundleIdentifier]; return [[NSBundle mainBundle] bundleIdentifier];
} }
NSString *accessGroupValue(NSDictionary *options)
{
if (options && options[@"accessGroup"] != nil) {
return options[@"accessGroup"];
}
return nil;
}
#pragma mark - Proposed functionality - Helpers #pragma mark - Proposed functionality - Helpers
#define kAuthenticationType @"authenticationType" #define kAuthenticationType @"authenticationType"
@ -166,6 +174,51 @@ SecAccessControlCreateFlags secureAccessControl(NSDictionary *options)
//LAPolicyDeviceOwnerAuthenticationWithBiometrics | LAPolicyDeviceOwnerAuthentication //LAPolicyDeviceOwnerAuthenticationWithBiometrics | LAPolicyDeviceOwnerAuthentication
- (void)insertKeychainEntry:(NSDictionary *)attributes withAccessGroup:(NSString * __nullable)accessGroup resolver:(RCTPromiseResolveBlock)resolve rejecter:(RCTPromiseRejectBlock)reject
{
if (accessGroup != nil) {
NSMutableDictionary *mAttributes = attributes.mutableCopy;
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);
dispatch_async(dispatch_get_main_queue(), ^{
if (osStatus != noErr && osStatus != errSecItemNotFound) {
NSError *error = [NSError errorWithDomain:NSOSStatusErrorDomain code:osStatus userInfo:nil];
return rejectWithError(reject, error);
} else {
return resolve(@(YES));
}
});
});
}
- (OSStatus)deletePasswordsForService:(NSString *)service
{
NSDictionary *query = @{
(__bridge NSString *)kSecClass: (__bridge id)(kSecClassGenericPassword),
(__bridge NSString *)kSecAttrService: service,
(__bridge NSString *)kSecReturnAttributes: (__bridge id)kCFBooleanTrue,
(__bridge NSString *)kSecReturnData: (__bridge id)kCFBooleanFalse
};
return SecItemDelete((__bridge CFDictionaryRef) query);
}
- (OSStatus)deleteCredentialsForServer:(NSString *)server
{
NSDictionary *query = @{
(__bridge NSString *)kSecClass: (__bridge id)(kSecClassInternetPassword),
(__bridge NSString *)kSecAttrServer: server,
(__bridge NSString *)kSecReturnAttributes: (__bridge id)kCFBooleanTrue,
(__bridge NSString *)kSecReturnData: (__bridge id)kCFBooleanFalse
};
return SecItemDelete((__bridge CFDictionaryRef) query);
}
#pragma mark - Proposed functionality - RCT_EXPORT_METHOD #pragma mark - Proposed functionality - RCT_EXPORT_METHOD
#if TARGET_OS_IOS #if TARGET_OS_IOS
@ -174,7 +227,7 @@ RCT_EXPORT_METHOD(canCheckAuthentication:(NSDictionary *)options resolver:(RCTPr
LAPolicy policyToEvaluate = authPolicy(options); LAPolicy policyToEvaluate = authPolicy(options);
NSError *aerr = nil; NSError *aerr = nil;
BOOL canBeProtected = [self canCheckAuthentication:policyToEvaluate error:&aerr ]; BOOL canBeProtected = [[LAContext new] canEvaluatePolicy:policyToEvaluate error:&aerr];
if (aerr || !canBeProtected) { if (aerr || !canBeProtected) {
return resolve(@(NO)); return resolve(@(NO));
@ -204,32 +257,17 @@ RCT_EXPORT_METHOD(getSupportedBiometryType:(RCTPromiseResolveBlock)resolve rejec
} }
#endif #endif
- (BOOL) canCheckAuthentication:(LAPolicy)policyToEvaluate error:(NSError **)err {
return [[[ LAContext alloc] init ] canEvaluatePolicy:policyToEvaluate error:err ];
}
RCT_EXPORT_METHOD(setPasswordWithAuthentication:(NSDictionary *)options withUsername:(NSString *)username withPassword:(NSString *)password resolver:(RCTPromiseResolveBlock)resolve rejecter:(RCTPromiseRejectBlock)reject) RCT_EXPORT_METHOD(setPasswordWithAuthentication:(NSDictionary *)options withUsername:(NSString *)username withPassword:(NSString *)password resolver:(RCTPromiseResolveBlock)resolve rejecter:(RCTPromiseRejectBlock)reject)
{ {
NSString *service = serviceValue(options); NSString *service = serviceValue(options);
// Delete old entry for that key if Available
NSError *aerr = nil; NSError *aerr = nil;
BOOL canAuthenticate = [ self canCheckAuthentication:LAPolicyDeviceOwnerAuthentication error:&aerr ]; BOOL canAuthenticate = [[LAContext new] canEvaluatePolicy:LAPolicyDeviceOwnerAuthentication error:&aerr];
if (aerr || !canAuthenticate) { if (aerr || !canAuthenticate) {
return rejectWithError(reject, aerr); return rejectWithError(reject, aerr);
} }
NSMutableDictionary *dict = @{ (__bridge NSString *)kSecClass : (__bridge id)(kSecClassGenericPassword), [self deletePasswordsForService:service];
(__bridge NSString *)kSecAttrService: service,
(__bridge NSString *)kSecReturnAttributes: (__bridge id)kCFBooleanTrue
}.mutableCopy;
OSStatus osStatus = SecItemDelete((__bridge CFDictionaryRef) dict);
// make new entry
dict = @{ (__bridge NSString *)kSecClass : (__bridge id)(kSecClassGenericPassword),
(__bridge NSString *)kSecAttrService : service,
(__bridge NSString *)kSecAttrAccount : username
}.mutableCopy;
CFErrorRef error = NULL; CFErrorRef error = NULL;
SecAccessControlRef sacRef = SecAccessControlCreateWithFlags(kCFAllocatorDefault, SecAccessControlRef sacRef = SecAccessControlCreateWithFlags(kCFAllocatorDefault,
@ -242,25 +280,15 @@ RCT_EXPORT_METHOD(setPasswordWithAuthentication:(NSDictionary *)options withUser
return rejectWithError(reject, aerr); return rejectWithError(reject, aerr);
} }
NSData *passwordData = [password dataUsingEncoding:NSUTF8StringEncoding]; NSDictionary *attributes = @{
[dict setObject:(__bridge id)sacRef forKey:kSecAttrAccessControl]; (__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
};
[dict setObject:passwordData forKey:kSecValueData]; [self insertKeychainEntry:attributes withAccessGroup:accessGroupValue(options) resolver:resolve rejecter:reject];
// Try to save to keychain
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
OSStatus osStatus = SecItemAdd((__bridge CFDictionaryRef) dict, NULL);
dispatch_async(dispatch_get_main_queue(), ^{
if (osStatus != noErr && osStatus != errSecItemNotFound) {
NSError *error = [NSError errorWithDomain:NSOSStatusErrorDomain code:osStatus userInfo:nil];
return rejectWithError(reject, error);
} else {
return resolve(@(YES));
}
});
});
} }
RCT_EXPORT_METHOD(getPasswordWithAuthentication:(NSDictionary *)options resolver:(RCTPromiseResolveBlock)resolve rejecter:(RCTPromiseRejectBlock)reject) RCT_EXPORT_METHOD(getPasswordWithAuthentication:(NSDictionary *)options resolver:(RCTPromiseResolveBlock)resolve rejecter:(RCTPromiseRejectBlock)reject)
@ -271,27 +299,28 @@ RCT_EXPORT_METHOD(getPasswordWithAuthentication:(NSDictionary *)options resolver
promptMessage = options[kAuthenticationPromptMessage]; promptMessage = options[kAuthenticationPromptMessage];
} }
NSMutableDictionary *dict = @{ (__bridge NSString *)kSecClass : (__bridge id)(kSecClassGenericPassword), NSDictionary *query = @{
(__bridge NSString *)kSecAttrService : service, (__bridge NSString *)kSecClass: (__bridge id)(kSecClassGenericPassword),
(__bridge NSString *)kSecReturnAttributes : (__bridge id)kCFBooleanTrue, (__bridge NSString *)kSecAttrService: service,
(__bridge NSString *)kSecReturnData : (__bridge id)kCFBooleanTrue, (__bridge NSString *)kSecReturnAttributes: (__bridge id)kCFBooleanTrue,
(__bridge NSString *)kSecMatchLimit : (__bridge NSString *)kSecMatchLimitOne, (__bridge NSString *)kSecReturnData: (__bridge id)kCFBooleanTrue,
(__bridge NSString *)kSecUseOperationPrompt : promptMessage (__bridge NSString *)kSecMatchLimit: (__bridge NSString *)kSecMatchLimitOne,
}.mutableCopy; (__bridge NSString *)kSecUseOperationPrompt: promptMessage
};
// Notify AppDelegate // Notify AppDelegate
dispatch_async(dispatch_get_main_queue(), ^{ dispatch_async(dispatch_get_main_queue(), ^{
[ self notifyAuthenticationListener: YES ]; [self notifyAuthenticationListener: YES];
}); });
// Look up password for service in the keychain // Look up password for service in the keychain
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{ dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
__block NSDictionary* found = nil; __block NSDictionary* found = nil;
CFTypeRef foundTypeRef = NULL; CFTypeRef foundTypeRef = NULL;
OSStatus osStatus = SecItemCopyMatching((__bridge CFDictionaryRef) dict, (CFTypeRef*)&foundTypeRef); OSStatus osStatus = SecItemCopyMatching((__bridge CFDictionaryRef) query, (CFTypeRef*)&foundTypeRef);
dispatch_async(dispatch_get_main_queue(), ^{ dispatch_async(dispatch_get_main_queue(), ^{
[ self notifyAuthenticationListener: NO ]; [self notifyAuthenticationListener: NO];
if (osStatus != noErr && osStatus != errSecItemNotFound) { if (osStatus != noErr && osStatus != errSecItemNotFound) {
NSError *error = [NSError errorWithDomain:NSOSStatusErrorDomain code:osStatus userInfo:nil]; NSError *error = [NSError errorWithDomain:NSOSStatusErrorDomain code:osStatus userInfo:nil];
@ -304,16 +333,15 @@ RCT_EXPORT_METHOD(getPasswordWithAuthentication:(NSDictionary *)options resolver
} }
// Found // Found
NSString* username = (NSString *) [found objectForKey:(__bridge id)(kSecAttrAccount)]; NSString *username = (NSString *) [found objectForKey:(__bridge id)(kSecAttrAccount)];
NSString* password = [[NSString alloc] initWithData:[found objectForKey:(__bridge id)(kSecValueData)] encoding:NSUTF8StringEncoding]; NSString *password = [[NSString alloc] initWithData:[found objectForKey:(__bridge id)(kSecValueData)] encoding:NSUTF8StringEncoding];
return resolve(@{ return resolve(@{
@"service": service, @"service": service,
@"username": username, @"username": username,
@"password": password @"password": password
}); });
}); });
}); });
} }
@ -331,56 +359,41 @@ RCT_EXPORT_METHOD(setGenericPasswordForOptions:(NSDictionary *)options withUsern
{ {
NSString *service = serviceValue(options); NSString *service = serviceValue(options);
// Create dictionary of search parameters [self deletePasswordsForService:service];
NSMutableDictionary *dict = [NSMutableDictionary dictionaryWithObjectsAndKeys:(__bridge id)(kSecClassGenericPassword), kSecClass, service, kSecAttrService, kCFBooleanTrue, kSecReturnAttributes, nil];
if (options && options[@"accessGroup"]) {
[dict setObject:options[@"accessGroup"] forKey:kSecAttrAccessGroup];
}
// Remove any old values from the keychain
OSStatus osStatus = SecItemDelete((__bridge CFDictionaryRef) dict);
// Create dictionary of parameters to add // Create dictionary of parameters to add
NSData *passwordData = [password dataUsingEncoding:NSUTF8StringEncoding]; NSDictionary *attributes = @{
dict = [NSMutableDictionary dictionaryWithObjectsAndKeys:(__bridge id)(kSecClassGenericPassword), kSecClass, accessibleValue(options), kSecAttrAccessible, service, kSecAttrService, passwordData, kSecValueData, username, kSecAttrAccount, nil]; (__bridge NSString *)kSecClass: (__bridge id)(kSecClassGenericPassword),
(__bridge NSString *)kSecAttrService: service,
if (options && options[@"accessGroup"]) { (__bridge NSString *)kSecAttrAccount: username,
[dict setObject:options[@"accessGroup"] forKey:kSecAttrAccessGroup]; (__bridge NSString *)kSecValueData: [password dataUsingEncoding:NSUTF8StringEncoding],
} (__bridge NSString *)kSecAttrAccessible: (__bridge id)accessibleValue(options)
};
// Try to save to keychain
osStatus = SecItemAdd((__bridge CFDictionaryRef) dict, NULL);
if (osStatus != noErr && osStatus != errSecItemNotFound) {
NSError *error = [NSError errorWithDomain:NSOSStatusErrorDomain code:osStatus userInfo:nil];
return rejectWithError(reject, error);
}
return resolve(@(YES));
[self insertKeychainEntry:attributes withAccessGroup:accessGroupValue(options) resolver:resolve rejecter:reject];
} }
RCT_EXPORT_METHOD(getGenericPasswordForOptions:(NSDictionary *)options resolver:(RCTPromiseResolveBlock)resolve rejecter:(RCTPromiseRejectBlock)reject) RCT_EXPORT_METHOD(getGenericPasswordForOptions:(NSDictionary *)options resolver:(RCTPromiseResolveBlock)resolve rejecter:(RCTPromiseRejectBlock)reject)
{ {
NSString *service = serviceValue(options); NSString *service = serviceValue(options);
// Create dictionary of search parameters
NSMutableDictionary *dict = [NSMutableDictionary dictionaryWithObjectsAndKeys:(__bridge id)(kSecClassGenericPassword), kSecClass, service, kSecAttrService, kCFBooleanTrue, kSecReturnAttributes, kCFBooleanTrue, kSecReturnData, nil];
if (options && options[@"accessGroup"]) {
[dict setObject:options[@"accessGroup"] forKey:kSecAttrAccessGroup];
}
// secure compatibility with TouchId / Passcode secured stored items // secure compatibility with TouchId / Passcode secured stored items
// http://stackoverflow.com/questions/42339000/ksecuseauthenticationuiskip-how-to-use-it // 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. // Silently skip any items that require user authentication. Only use this value with the SecItemCopyMatching function.
[dict setObject:kSecUseAuthenticationUISkip forKey:kSecUseAuthenticationUI];
// Look up server in the keychain NSDictionary *query = @{
NSDictionary* found = nil; (__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 *)kSecUseAuthenticationUI: (__bridge id)kSecUseAuthenticationUISkip
};
// Look up service in the keychain
NSDictionary *found = nil;
CFTypeRef foundTypeRef = NULL; CFTypeRef foundTypeRef = NULL;
OSStatus osStatus = SecItemCopyMatching((__bridge CFDictionaryRef) dict, (CFTypeRef*)&foundTypeRef); OSStatus osStatus = SecItemCopyMatching((__bridge CFDictionaryRef) query, (CFTypeRef*)&foundTypeRef);
if (osStatus != noErr && osStatus != errSecItemNotFound) { if (osStatus != noErr && osStatus != errSecItemNotFound) {
NSError *error = [NSError errorWithDomain:NSOSStatusErrorDomain code:osStatus userInfo:nil]; NSError *error = [NSError errorWithDomain:NSOSStatusErrorDomain code:osStatus userInfo:nil];
@ -393,8 +406,8 @@ RCT_EXPORT_METHOD(getGenericPasswordForOptions:(NSDictionary *)options resolver:
} }
// Found // Found
NSString* username = (NSString *) [found objectForKey:(__bridge id)(kSecAttrAccount)]; NSString *username = (NSString *) [found objectForKey:(__bridge id)(kSecAttrAccount)];
NSString* password = [[NSString alloc] initWithData:[found objectForKey:(__bridge id)(kSecValueData)] encoding:NSUTF8StringEncoding]; NSString *password = [[NSString alloc] initWithData:[found objectForKey:(__bridge id)(kSecValueData)] encoding:NSUTF8StringEncoding];
return resolve(@{ return resolve(@{
@"service": service, @"service": service,
@ -408,16 +421,8 @@ RCT_EXPORT_METHOD(resetGenericPasswordForOptions:(NSDictionary *)options resolve
{ {
NSString *service = serviceValue(options); NSString *service = serviceValue(options);
// Create dictionary of search parameters OSStatus osStatus = [self deletePasswordsForService:service];
NSDictionary *query = @{
(__bridge NSString *)kSecClass: (__bridge id)(kSecClassGenericPassword),
(__bridge NSString *)kSecAttrService: service,
(__bridge NSString *)kSecReturnAttributes: (__bridge id)kCFBooleanTrue,
(__bridge NSString *)kSecReturnData: (__bridge id)kCFBooleanFalse
};
// Remove matching entries in the keychain
OSStatus osStatus = SecItemDelete((__bridge CFDictionaryRef) query);
if (osStatus != noErr && osStatus != errSecItemNotFound) { if (osStatus != noErr && osStatus != errSecItemNotFound) {
NSError *error = [NSError errorWithDomain:NSOSStatusErrorDomain code:osStatus userInfo:nil]; NSError *error = [NSError errorWithDomain:NSOSStatusErrorDomain code:osStatus userInfo:nil];
return rejectWithError(reject, error); return rejectWithError(reject, error);
@ -428,53 +433,38 @@ RCT_EXPORT_METHOD(resetGenericPasswordForOptions:(NSDictionary *)options resolve
RCT_EXPORT_METHOD(setInternetCredentialsForServer:(NSString *)server withUsername:(NSString*)username withPassword:(NSString*)password withOptions:(NSDictionary *)options resolver:(RCTPromiseResolveBlock)resolve rejecter:(RCTPromiseRejectBlock)reject) RCT_EXPORT_METHOD(setInternetCredentialsForServer:(NSString *)server withUsername:(NSString*)username withPassword:(NSString*)password withOptions:(NSDictionary *)options resolver:(RCTPromiseResolveBlock)resolve rejecter:(RCTPromiseRejectBlock)reject)
{ {
// Create dictionary of search parameters [self deleteCredentialsForServer:server];
NSMutableDictionary *dict = [NSMutableDictionary dictionaryWithObjectsAndKeys:(__bridge id)(kSecClassInternetPassword), kSecClass, server, kSecAttrServer, kCFBooleanTrue, kSecReturnAttributes, nil];
if (options && options[@"accessGroup"]) { NSDictionary *attributes = @{
[dict setObject:options[@"accessGroup"] forKey:kSecAttrAccessGroup]; (__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)
};
// Remove any old values from the keychain [self insertKeychainEntry:attributes withAccessGroup:accessGroupValue(options) resolver:resolve rejecter:reject];
OSStatus osStatus = SecItemDelete((__bridge CFDictionaryRef) dict);
// Create dictionary of parameters to add
NSData* passwordData = [password dataUsingEncoding:NSUTF8StringEncoding];
dict = [NSMutableDictionary dictionaryWithObjectsAndKeys:(__bridge id)(kSecClassInternetPassword), kSecClass, accessibleValue(options), kSecAttrAccessible, server, kSecAttrServer, passwordData, kSecValueData, username, kSecAttrAccount, nil];
if (options && options[@"accessGroup"]) {
[dict setObject:options[@"accessGroup"] forKey:kSecAttrAccessGroup];
}
// Try to save to keychain
osStatus = SecItemAdd((__bridge CFDictionaryRef) dict, NULL);
if (osStatus != noErr && osStatus != errSecItemNotFound) {
NSError *error = [NSError errorWithDomain:NSOSStatusErrorDomain code:osStatus userInfo:nil];
return rejectWithError(reject, error);
}
return resolve(@(YES));
} }
RCT_EXPORT_METHOD(getInternetCredentialsForServer:(NSString *)server withOptions:(NSDictionary *)options resolver:(RCTPromiseResolveBlock)resolve rejecter:(RCTPromiseRejectBlock)reject) RCT_EXPORT_METHOD(getInternetCredentialsForServer:(NSString *)server withOptions:(NSDictionary *)options resolver:(RCTPromiseResolveBlock)resolve rejecter:(RCTPromiseRejectBlock)reject)
{ {
// Create dictionary of search parameters
NSMutableDictionary *dict = [NSMutableDictionary dictionaryWithObjectsAndKeys:(__bridge id)(kSecClassInternetPassword), kSecClass, server, kSecAttrServer, kCFBooleanTrue, kSecReturnAttributes, kCFBooleanTrue, kSecReturnData, nil];
if (options && options[@"accessGroup"]) {
[dict setObject:options[@"accessGroup"] forKey:kSecAttrAccessGroup];
}
// secure compatibility with TouchId / Passcode secured stored items // secure compatibility with TouchId / Passcode secured stored items
// http://stackoverflow.com/questions/42339000/ksecuseauthenticationuiskip-how-to-use-it // 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. // Silently skip any items that require user authentication. Only use this value with the SecItemCopyMatching function.
[dict setObject:kSecUseAuthenticationUISkip forKey:kSecUseAuthenticationUI];
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
};
// Look up server in the keychain // Look up server in the keychain
NSDictionary *found = nil; NSDictionary *found = nil;
CFTypeRef foundTypeRef = NULL; CFTypeRef foundTypeRef = NULL;
OSStatus osStatus = SecItemCopyMatching((__bridge CFDictionaryRef) dict, (CFTypeRef*)&foundTypeRef); OSStatus osStatus = SecItemCopyMatching((__bridge CFDictionaryRef) query, (CFTypeRef*)&foundTypeRef);
if (osStatus != noErr && osStatus != errSecItemNotFound) { if (osStatus != noErr && osStatus != errSecItemNotFound) {
NSError *error = [NSError errorWithDomain:NSOSStatusErrorDomain code:osStatus userInfo:nil]; NSError *error = [NSError errorWithDomain:NSOSStatusErrorDomain code:osStatus userInfo:nil];
@ -500,15 +490,8 @@ RCT_EXPORT_METHOD(getInternetCredentialsForServer:(NSString *)server withOptions
RCT_EXPORT_METHOD(resetInternetCredentialsForServer:(NSString *)server withOptions:(NSDictionary *)options resolver:(RCTPromiseResolveBlock)resolve rejecter:(RCTPromiseRejectBlock)reject) RCT_EXPORT_METHOD(resetInternetCredentialsForServer:(NSString *)server withOptions:(NSDictionary *)options resolver:(RCTPromiseResolveBlock)resolve rejecter:(RCTPromiseRejectBlock)reject)
{ {
// Create dictionary of search parameters OSStatus osStatus = [self deleteCredentialsForServer:server];
NSMutableDictionary *dict = [NSMutableDictionary dictionaryWithObjectsAndKeys:(__bridge id)(kSecClassInternetPassword), kSecClass, server, kSecAttrServer, kCFBooleanTrue, kSecReturnAttributes, kCFBooleanTrue, kSecReturnData, nil];
if (options && options[@"accessGroup"]) {
[dict setObject:options[@"accessGroup"] forKey:kSecAttrAccessGroup];
}
// Remove any old values from the keychain
OSStatus osStatus = SecItemDelete((__bridge CFDictionaryRef) dict);
if (osStatus != noErr && osStatus != errSecItemNotFound) { if (osStatus != noErr && osStatus != errSecItemNotFound) {
NSError *error = [NSError errorWithDomain:NSOSStatusErrorDomain code:osStatus userInfo:nil]; NSError *error = [NSError errorWithDomain:NSOSStatusErrorDomain code:osStatus userInfo:nil];
return rejectWithError(reject, error); return rejectWithError(reject, error);

View File

@ -41,8 +41,8 @@ type SecAccessible =
| 'AccessibleAlwaysThisDeviceOnly'; | 'AccessibleAlwaysThisDeviceOnly';
type Options = { type Options = {
accessible?: SecAccessible,
accessGroup?: string, accessGroup?: string,
accessible?: SecAccessible,
service?: string, service?: string,
}; };
@ -57,10 +57,11 @@ type SecAccessControl =
type LAPolicy = 'Authentication' | 'AuthenticationWithBiometrics'; type LAPolicy = 'Authentication' | 'AuthenticationWithBiometrics';
type SecureOptions = { type SecureOptions = {
service?: string, accessControl?: SecAccessControl,
accessGroup?: string,
authenticationPrompt?: string, authenticationPrompt?: string,
authenticationType?: LAPolicy, authenticationType?: LAPolicy,
accessControl?: SecAccessControl, service?: string,
}; };
/** /**

View File

@ -12,10 +12,11 @@ declare module 'react-native-keychain' {
} }
export interface SecureOptions { export interface SecureOptions {
service?: string; accessControl?: string;
accessGroup?: string;
authenticationPrompt?: string; authenticationPrompt?: string;
authenticationType?: string; authenticationType?: string;
accessControl?: string; service?: string;
} }
function canImplyAuthentication( function canImplyAuthentication(