diff --git a/README.md b/README.md index aeb55fe1..cbb4e076 100644 --- a/README.md +++ b/README.md @@ -109,7 +109,7 @@ Both `setGenericPassword` and `setInternetCredentials` are limited to strings on Will store the username/password combination in the secure storage. Resolves to `{service, storage}` or rejects in case of an error. `storage` - is a name of used internal cipher for saving secret; `service` - name used for storing secret in internal storage (empty string resolved to valid default name). -### `getGenericPassword([{ authenticationPrompt, service, accessControl }])` +### `getGenericPassword([{ accessControl, account, authenticationPrompt, service }])` Will retrieve the username/password combination from the secure storage. Resolves to `{ username, password, service, storage }` if an entry exists or `false` if it doesn't. It will reject only if an unexpected error is encountered like lacking entitlements or permission. @@ -171,7 +171,8 @@ Get security level that is supported on the current device with the current OS. | -------------------------- |---------------| ------------------------------------------------------------------------------------------------ | ------------------------------------------------------------------------- | | **`accessControl`** | All | This dictates how a keychain item may be used, see possible values in `Keychain.ACCESS_CONTROL`. | _None_ | | **`accessible`** | iOS, visionOS | This dictates when a keychain item is accessible, see possible values in `Keychain.ACCESSIBLE`. | _`Keychain.ACCESSIBLE.WHEN_UNLOCKED`_ | -| **`accessGroup`** | iOS, visionOS | In which App Group to share the keychain. Requires additional setup with entitlements. | _None_ | +| **`accessGroup`** | iOS, visionOS | In which App Group to share the keychain. Requires additional setup with entitlements. | _None_ +| **`account`** | iOS, visionOS | Account / username of keychain accounts. Maps to kSecAttrAccount on iOS. | _None_ | | **`authenticationPrompt`** | All | What to prompt the user when unlocking the keychain with biometry or device password. | See [`authenticationPrompt` Properties](#authenticationprompt-properties) | | **`authenticationType`** | iOS, visionOS | Policies specifying which forms of authentication are acceptable. | `Keychain.AUTHENTICATION_TYPE.DEVICE_PASSCODE_OR_BIOMETRICS` | | **`service`** | All | Reverse domain name qualifier for the service associated with password. | _App bundle ID_ | diff --git a/RNKeychainManager/RNKeychainManager.m b/RNKeychainManager/RNKeychainManager.m index dc7a1060..70a73f16 100644 --- a/RNKeychainManager/RNKeychainManager.m +++ b/RNKeychainManager/RNKeychainManager.m @@ -111,6 +111,14 @@ CFStringRef accessibleValue(NSDictionary *options) return kSecAttrAccessibleAfterFirstUnlock; } +id accountValue(NSDictionary *options) +{ + if (options && options[@"account"] != nil) { + return options[@"account"]; + } + return nil; +} + NSString *serviceValue(NSDictionary *options) { if (options && options[@"service"] != nil) { @@ -375,18 +383,32 @@ - (OSStatus)deleteCredentialsForServer:(NSString *)server resolver:(RCTPromiseResolveBlock)resolve rejecter:(RCTPromiseRejectBlock)reject) { + NSString *account = accountValue(options); NSString *service = serviceValue(options); NSString *authenticationPrompt = authenticationPromptValue(options); - 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: authenticationPrompt - }; - + NSDictionary *query; + if (account) { + query = @{ + (__bridge NSString *)kSecClass: (__bridge id)(kSecClassGenericPassword), + (__bridge NSString *)kSecAttrAccount: account, + (__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: authenticationPrompt + }; + } else { + 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: authenticationPrompt + }; + } + // Look up service in the keychain NSDictionary *found = nil; CFTypeRef foundTypeRef = NULL; diff --git a/typings/react-native-keychain.d.ts b/typings/react-native-keychain.d.ts index 6846468e..205222d8 100644 --- a/typings/react-native-keychain.d.ts +++ b/typings/react-native-keychain.d.ts @@ -75,6 +75,7 @@ declare module 'react-native-keychain' { accessControl?: ACCESS_CONTROL; accessGroup?: string; accessible?: ACCESSIBLE; + account?: string; authenticationPrompt?: string | AuthenticationPrompt; authenticationType?: AUTHENTICATION_TYPE; service?: string;