@@ -144,63 +144,95 @@ - (NSString *)messageForError:(NSError *)error
144144}
145145
146146RCT_EXPORT_METHOD (getItem:(NSString *)key options:(NSDictionary *)options resolver:(RCTPromiseResolveBlock)resolve rejecter:(RCTPromiseRejectBlock)reject){
147-
148-
147+
148+
149149 NSString * keychainService = [RCTConvert NSString: options[@" keychainService" ]];
150150 if (keychainService == NULL ) {
151151 keychainService = @" app" ;
152152 }
153+
154+ // Create dictionary of search parameters
155+ NSMutableDictionary * query = [NSMutableDictionary dictionaryWithObjectsAndKeys: (__bridge id )(kSecClassGenericPassword ), kSecClass ,
156+ keychainService, kSecAttrService ,
157+ key, kSecAttrAccount ,
158+ kCFBooleanTrue , kSecReturnAttributes ,
159+ kCFBooleanTrue , kSecReturnData ,
160+ nil ];
161+
162+ if ([RCTConvert NSString: options[@" kSecUseOperationPrompt" ]] != NULL ){
163+ [query setValue: [RCTConvert NSString: options[@" kSecUseOperationPrompt" ]] forKey: (NSString *)kSecUseOperationPrompt ];
164+ }
165+
166+ if ([RCTConvert BOOL: options[@" touchID" ]]){
167+ LAContext *context = [[LAContext alloc ] init ];
168+ context.localizedFallbackTitle = @" " ;
169+ context.touchIDAuthenticationAllowableReuseDuration = 1 ;
170+
171+ [query setValue: context forKey: (NSString *)kSecUseAuthenticationContext ];
172+
173+ NSString *prompt = @" " ;
174+ if ([RCTConvert NSString: options[@" kSecUseOperationPrompt" ]] != NULL ) {
175+ prompt = [RCTConvert NSString: options[@" kSecUseOperationPrompt" ]];
176+ }
177+
178+ [context evaluatePolicy: LAPolicyDeviceOwnerAuthenticationWithBiometrics
179+ localizedReason: prompt
180+ reply: ^(BOOL success, NSError * _Nullable error) {
181+ if (!success) {
182+ if (error) {
183+ reject (nil , error.localizedDescription , error);
184+ } else {
185+ reject (nil , @" The user name or passphrase you entered is not correct." , nil );
186+ }
187+ return ;
188+ }
189+
190+ [self getItemWithQuery: query resolver: resolve rejecter: reject];
191+ }];
192+ return ;
193+ }
194+
195+ [self getItemWithQuery: query resolver: resolve rejecter: reject];
196+ }
153197
154- // Create dictionary of search parameters
155- NSMutableDictionary * query = [NSMutableDictionary dictionaryWithObjectsAndKeys: (__bridge id )(kSecClassGenericPassword ), kSecClass ,
156- keychainService, kSecAttrService ,
157- key, kSecAttrAccount ,
158- kCFBooleanTrue , kSecReturnAttributes ,
159- kCFBooleanTrue , kSecReturnData ,
160- nil ];
161-
162- if ([RCTConvert NSString: options[@" kSecUseOperationPrompt" ]] != NULL ){
163- [query setValue: [RCTConvert NSString: options[@" kSecUseOperationPrompt" ]] forKey: (NSString *)kSecUseOperationPrompt ];
164- }
165-
166- // Look up server in the keychain
167- NSDictionary * found = nil ;
168- CFTypeRef foundTypeRef = NULL ;
169- OSStatus osStatus = SecItemCopyMatching ((__bridge CFDictionaryRef) query, (CFTypeRef*)&foundTypeRef);
170-
171- if (osStatus != noErr && osStatus != errSecItemNotFound) {
172- NSError *error = [NSError errorWithDomain: NSOSStatusErrorDomain code: osStatus userInfo: nil ];
173- reject ([NSString stringWithFormat: @" %ld " ,(long )error.code], [self messageForError: error], nil );
174- return ;
175- }
176-
177- found = (__bridge NSDictionary *)(foundTypeRef);
178- if (!found) {
179- resolve (nil );
180- } else {
181- // Found
182- NSString * value = [[NSString alloc ] initWithData: [found objectForKey: (__bridge id )(kSecValueData )] encoding: NSUTF8StringEncoding];
183- resolve (value);
184-
185- }
186-
198+ - (void )getItemWithQuery : (NSDictionary *)query resolver : (RCTPromiseResolveBlock)resolve rejecter : (RCTPromiseRejectBlock)reject {
199+ // Look up server in the keychain
200+ NSDictionary * found = nil ;
201+ CFTypeRef foundTypeRef = NULL ;
202+ OSStatus osStatus = SecItemCopyMatching ((__bridge CFDictionaryRef) query, (CFTypeRef*)&foundTypeRef);
203+
204+ if (osStatus != noErr && osStatus != errSecItemNotFound) {
205+ NSError *error = [NSError errorWithDomain: NSOSStatusErrorDomain code: osStatus userInfo: nil ];
206+ reject ([NSString stringWithFormat: @" %ld " ,(long )error.code], [self messageForError: error], nil );
207+ return ;
208+ }
209+
210+ found = (__bridge NSDictionary *)(foundTypeRef);
211+ if (!found) {
212+ resolve (nil );
213+ } else {
214+ // Found
215+ NSString * value = [[NSString alloc ] initWithData: [found objectForKey: (__bridge id )(kSecValueData )] encoding: NSUTF8StringEncoding];
216+ resolve (value);
217+
218+ }
187219}
188220
189221RCT_EXPORT_METHOD (getAllItems:(NSDictionary *)options resolver:(RCTPromiseResolveBlock)resolve rejecter:(RCTPromiseRejectBlock)reject){
190-
222+
191223 NSString * keychainService = [RCTConvert NSString: options[@" keychainService" ]];
192-
224+
193225 NSMutableArray * finalResult = [[NSMutableArray alloc ] init ];
194226 NSMutableDictionary *query = [NSMutableDictionary dictionaryWithObjectsAndKeys:
195227 (__bridge id )kCFBooleanTrue , (__bridge id )kSecReturnAttributes ,
196228 (__bridge id )kSecMatchLimitAll , (__bridge id )kSecMatchLimit ,
197229 (__bridge id )kCFBooleanTrue , (__bridge id )kSecReturnData ,
198230 nil ];
199-
231+
200232 if (keychainService) {
201233 [query setObject: keychainService forKey: (NSString *)kSecAttrService ];
202234 }
203-
235+
204236 NSArray *secItemClasses = [NSArray arrayWithObjects:
205237 (__bridge id )kSecClassGenericPassword ,
206238 (__bridge id )kSecClassInternetPassword ,
@@ -272,15 +304,19 @@ - (NSString *)messageForError:(NSError *)error
272304{
273305#if !TARGET_OS_TV
274306 LAContext *context = [[LAContext alloc ] init ];
275-
276- if ([context canEvaluatePolicy: LAPolicyDeviceOwnerAuthenticationWithBiometrics error: NULL ]) {
307+
308+ NSError *evaluationError = nil ;
309+ if ([context canEvaluatePolicy: LAPolicyDeviceOwnerAuthenticationWithBiometrics error: &evaluationError]) {
277310 if (@available (iOS 11 , macOS 10.13.2 , *)) {
278311 if (context.biometryType == LABiometryTypeFaceID) {
279312 return resolve (@" Face ID" );
280313 }
281314 }
282315 resolve (@" Touch ID" );
283316 } else {
317+ if (evaluationError && evaluationError.code == LAErrorBiometryLockout) {
318+ return reject (nil , @" Biometry is locked" , nil );
319+ }
284320 resolve (@(NO ));
285321 }
286322#else
0 commit comments