From b2c07b9f77f493882dc7cf6ec2c198550621615d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=BCrno=20Ader?= Date: Sun, 30 Sep 2018 15:12:57 +0300 Subject: [PATCH] Add hasInternetCredentials (#150) This checks if the username/password combination for server is available in the secure storage. Resolves to `true` if an entry exists or `false` if it doesn't. --- README.md | 4 +++ RNKeychainManager/RNKeychainManager.m | 29 +++++++++++++++++++ .../com/oblador/keychain/KeychainModule.java | 13 +++++++++ index.js | 11 +++++++ typings/react-native-keychain.d.ts | 4 +++ 5 files changed, 61 insertions(+) diff --git a/README.md b/README.md index a5dd38f..2c4cd1b 100644 --- a/README.md +++ b/README.md @@ -61,6 +61,10 @@ Will remove the username/password combination from the secure storage. Will store the server/username/password combination in the secure storage. +### `hasInternetCredentials(server, [{ authenticationPrompt }])` + +Will check if the username/password combination for server is available in the secure storage. Resolves to `true` if an entry exists or `false` if it doesn't. + ### `getInternetCredentials(server, [{ authenticationPrompt }])` Will retreive the server/username/password combination from the secure storage. Resolves to `{ username, password }` 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. diff --git a/RNKeychainManager/RNKeychainManager.m b/RNKeychainManager/RNKeychainManager.m index 381981b..189b96d 100644 --- a/RNKeychainManager/RNKeychainManager.m +++ b/RNKeychainManager/RNKeychainManager.m @@ -372,6 +372,35 @@ RCT_EXPORT_METHOD(setInternetCredentialsForServer:(NSString *)server withUsernam [self insertKeychainEntry:attributes withOptions:options resolver:resolve rejecter:reject]; } +RCT_EXPORT_METHOD(hasInternetCredentialsForServer:(NSString *)server resolver:(RCTPromiseResolveBlock)resolve rejecter:(RCTPromiseRejectBlock)reject) +{ + NSMutableDictionary *queryParts = [[NSMutableDictionary alloc] init]; + queryParts[(__bridge NSString *)kSecClass] = (__bridge id)(kSecClassInternetPassword); + queryParts[(__bridge NSString *)kSecAttrServer] = server; + queryParts[(__bridge NSString *)kSecMatchLimit] = (__bridge NSString *)kSecMatchLimitOne; + + if (@available(iOS 9, *)) { + queryParts[(__bridge NSString *)kSecUseAuthenticationUI] = (__bridge NSString *)kSecUseAuthenticationUIFail; + } + + NSDictionary *query = [queryParts copy]; + + // Look up server in the keychain + OSStatus osStatus = SecItemCopyMatching((__bridge CFDictionaryRef) query, nil); + + switch (osStatus) { + case noErr: + case errSecInteractionNotAllowed: + return resolve(@(YES)); + + case errSecItemNotFound: + return resolve(@(NO)); + } + + NSError *error = [NSError errorWithDomain:NSOSStatusErrorDomain code:osStatus userInfo:nil]; + return rejectWithError(reject, error); +} + RCT_EXPORT_METHOD(getInternetCredentialsForServer:(NSString *)server withOptions:(NSDictionary *)options resolver:(RCTPromiseResolveBlock)resolve rejecter:(RCTPromiseRejectBlock)reject) { NSDictionary *query = @{ diff --git a/android/src/main/java/com/oblador/keychain/KeychainModule.java b/android/src/main/java/com/oblador/keychain/KeychainModule.java index 94b6c5e..323fbb5 100644 --- a/android/src/main/java/com/oblador/keychain/KeychainModule.java +++ b/android/src/main/java/com/oblador/keychain/KeychainModule.java @@ -149,6 +149,19 @@ public class KeychainModule extends ReactContextBaseJavaModule { } } + public void hasInternetCredentialsForServer(@NonNull String server, Promise promise) { + final String defaultService = getDefaultServiceIfNull(server); + + ResultSet resultSet = prefsStorage.getEncryptedEntry(defaultService); + if (resultSet == null) { + Log.e(KEYCHAIN_MODULE, "No entry found for service: " + defaultService); + promise.resolve(false); + return; + } + + promise.resolve(true); + } + @ReactMethod public void setInternetCredentialsForServer(@NonNull String server, String username, String password, ReadableMap unusedOptions, Promise promise) { setGenericPasswordForOptions(server, username, password, promise); diff --git a/index.js b/index.js index cadec3d..7969ed2 100644 --- a/index.js +++ b/index.js @@ -108,6 +108,17 @@ export function setInternetCredentials( ); } +/** + * Checks if we have a login combination for `server`. + * @param {string} server URL to server. + * @return {Promise} Resolves to `true` when successful + */ +export function hasInternetCredentials( + server: string, +): Promise { + return RNKeychainManager.hasInternetCredentialsForServer(server); +} + /** * Fetches login combination for `server`. * @param {string} server URL to server. diff --git a/typings/react-native-keychain.d.ts b/typings/react-native-keychain.d.ts index eeb1630..af94fef 100644 --- a/typings/react-native-keychain.d.ts +++ b/typings/react-native-keychain.d.ts @@ -63,6 +63,10 @@ declare module 'react-native-keychain' { server: string ): Promise; + function hasInternetCredentials( + server: string + ): Promise; + function resetInternetCredentials( server: string ): Promise;