From fa43f378f84a296771ac49cbe952e2dd75bea28a Mon Sep 17 00:00:00 2001 From: Oleksandr Date: Fri, 24 Jan 2020 09:14:34 +0100 Subject: [PATCH] Update README.md, API for iOS & Android added strings for copy/paste fixed anchors cleanup the code iOS fix, documentation become more accurate now improved ios part last minute changes updated list of ignored files Sync api method changes cleanup --- .editorconfig | 1 + .gitignore | 5 +- KeychainExample/.gitignore | 1 + KeychainExample/App.js | 34 ++++ README.md | 45 ++--- RNKeychainManager/RNKeychainManager.m | 80 +++++++-- android/README.md | 5 + android/build.gradle | 19 +- .../com/oblador/keychain/PrefsStorage.java | 2 +- .../keychain/cipherStorage/CipherStorage.java | 15 +- index.js | 162 ++++++++---------- typings/react-native-keychain.d.ts | 5 +- 12 files changed, 217 insertions(+), 157 deletions(-) diff --git a/.editorconfig b/.editorconfig index 5e19e7c0..b6dc064b 100644 --- a/.editorconfig +++ b/.editorconfig @@ -11,6 +11,7 @@ insert_final_newline = true [*.md] trim_trailing_whitespace = false +max_line_length = 100 [*.java] diff --git a/.gitignore b/.gitignore index 6e4eca2e..8a71fec8 100644 --- a/.gitignore +++ b/.gitignore @@ -62,9 +62,8 @@ buck-out/ *.jsbundle # CocoaPods -/ios/Pods/ -/KeychainExample/ios/Pods/ - +ios/Pods/ +KeychainExample/ios/Pods/ # Logs *.log diff --git a/KeychainExample/.gitignore b/KeychainExample/.gitignore index 5d647565..f6888a53 100644 --- a/KeychainExample/.gitignore +++ b/KeychainExample/.gitignore @@ -21,6 +21,7 @@ DerivedData *.ipa *.xcuserstate project.xcworkspace +ios/Pods/ # Android/IntelliJ # diff --git a/KeychainExample/App.js b/KeychainExample/App.js index 3b95c1e4..8442c1bd 100644 --- a/KeychainExample/App.js +++ b/KeychainExample/App.js @@ -109,6 +109,27 @@ export default class KeychainExample extends Component { } } + async ios_specifics() { + try { + const reply = await Keychain.setSharedWebCredentials( + 'server', + 'username', + 'password', + {} + ); + console.log(`setSharedWebCredentials: ${JSON.stringify(reply)}`); + } catch (err) { + alert(`setSharedWebCredentials: ${err}`); + } + + try { + const reply = await Keychain.requestSharedWebCredentials({}); + console.log(`requestSharedWebCredentials: ${JSON.stringify(reply)}`) + } catch (err) { + alert(`requestSharedWebCredentials: ${err}`); + } + } + render() { const VALUES = Platform.OS === 'ios' @@ -255,6 +276,19 @@ export default class KeychainExample extends Component { )} + + {Platform.OS === 'ios' && ( + + this.ios_specifics()} + style={styles.button} + > + + Test Other APIs + + + + )} ); diff --git a/README.md b/README.md index 397be3a5..0347f20f 100644 --- a/README.md +++ b/README.md @@ -8,31 +8,32 @@ [![Travis](https://img.shields.io/travis/oblador/react-native-keychain.svg)](https://travis-ci.org/oblador/react-native-keychain) [![npm](https://img.shields.io/npm/v/react-native-keychain.svg)](https://npmjs.com/package/react-native-keychain) [![npm](https://img.shields.io/npm/dm/react-native-keychain.svg)](https://npmjs.com/package/react-native-keychain) -# Keychain/Keystore Access for React Native. +# Keychain/Keystore Access for React Native -- [Keychain/Keystore Access for React Native.](#keychainkeystore-access-for-react-native) +- [Keychain/Keystore Access for React Native](#keychainkeystore-access-for-react-native) - [Installation](#installation) - [Usage](#usage) - [API](#api) - - [`setGenericPassword(username, password, [{ accessControl, accessible, accessGroup, service, securityLevel }])`](#setgenericpasswordusername-password--accesscontrol-accessible-accessgroup-service-securitylevel) - - [`getGenericPassword([{ authenticationPrompt, service }])`](#getgenericpassword-authenticationprompt-service) - - [`resetGenericPassword([{ service }])`](#resetgenericpassword-service) - - [`setInternetCredentials(server, username, password, [{ accessControl, accessible, accessGroup, securityLevel }])`](#setinternetcredentialsserver-username-password--accesscontrol-accessible-accessgroup-securitylevel) - - [`hasInternetCredentials(server, [{ authenticationPrompt }])`](#hasinternetcredentialsserver--authenticationprompt) - - [`getInternetCredentials(server, [{ authenticationPrompt }])`](#getinternetcredentialsserver--authenticationprompt) - - [`resetInternetCredentials(server, [{}])`](#resetinternetcredentialsserver) + - [`setGenericPassword(username, password, [{ accessControl, accessible, accessGroup, service, securityLevel }])`](#setgenericpasswordusername-password--accesscontrol-accessible-accessgroup-service-securitylevel-) + - [`getGenericPassword([{ authenticationPrompt, service }])`](#getgenericpassword-authenticationprompt-service-) + - [`resetGenericPassword([{ service }])`](#resetgenericpassword-service-) + - [`setInternetCredentials(server, username, password, [{ accessControl, accessible, accessGroup, securityLevel }])`](#setinternetcredentialsserver-username-password--accesscontrol-accessible-accessgroup-securitylevel-) + - [`hasInternetCredentials(server, [{ authenticationPrompt }])`](#hasinternetcredentialsserver--authenticationprompt-) + - [`getInternetCredentials(server, [{ authenticationPrompt }])`](#getinternetcredentialsserver--authenticationprompt-) + - [`resetInternetCredentials(server, [{}])`](#resetinternetcredentialsserver-) - [`requestSharedWebCredentials()` (iOS only)](#requestsharedwebcredentials-ios-only) - [`setSharedWebCredentials(server, username, password)` (iOS only)](#setsharedwebcredentialsserver-username-password-ios-only) - [`canImplyAuthentication([{ authenticationType }])` (iOS only)](#canimplyauthentication-authenticationtype--ios-only) - [`getSupportedBiometryType([{}])`](#getsupportedbiometrytype) - - [`getSecurityLevel([{}])` (Android only)](#getsecuritylevel-android-only) + - [`getSecurityLevel([{ accessControl }])` (Android only)](#getsecuritylevel-accesscontrol--android-only) - [Options](#options) - - [`Keychain.ACCESS_CONTROL` enum](#keychainaccesscontrol-enum) + - [Data Structure Properties/Fields](#data-structure-propertiesfields) + - [`Keychain.ACCESS_CONTROL` enum](#keychainaccess_control-enum) - [`Keychain.ACCESSIBLE` enum](#keychainaccessible-enum) - - [`Keychain.AUTHENTICATION_TYPE` enum](#keychainauthenticationtype-enum) - - [`Keychain.BIOMETRY_TYPE` enum](#keychainbiometrytype-enum) - - [`Keychain.SECURITY_LEVEL` enum (Android only)](#keychainsecuritylevel-enum-android-only) - - [`Keychain.STORAGE_TYPE` enum (Android only)](#keychainstoragetype-enum-android-only) + - [`Keychain.AUTHENTICATION_TYPE` enum](#keychainauthentication_type-enum) + - [`Keychain.BIOMETRY_TYPE` enum](#keychainbiometry_type-enum) + - [`Keychain.SECURITY_LEVEL` enum (Android only)](#keychainsecurity_level-enum-android-only) + - [`Keychain.STORAGE_TYPE` enum (Android only)](#keychainstorage_type-enum-android-only) - [`Keychain.RULES` enum (Android only)](#keychainrules-enum-android-only) - [Important Behavior](#important-behavior) - [Rule 1: Automatic Security Level Upgrade](#rule-1-automatic-security-level-upgrade) @@ -108,11 +109,11 @@ Will retrieve the username/password combination from the secure storage. Resolve ### `resetGenericPassword([{ service }])` -Will remove the username/password combination from the secure storage. Returns `true` in case of success. +Will remove the username/password combination from the secure storage. Resolves to `true` in case of success. ### `setInternetCredentials(server, username, password, [{ accessControl, accessible, accessGroup, securityLevel }])` -Will store the server/username/password combination in the secure storage. Returns `{ username, password, service, storage }`; +Will store the server/username/password combination in the secure storage. Resolves to `{ username, password, service, storage }`; ### `hasInternetCredentials(server, [{ authenticationPrompt }])` @@ -124,7 +125,7 @@ Will retrieve the server/username/password combination from the secure storage. ### `resetInternetCredentials(server, [{}])` -Will remove the server/username/password combination from the secure storage. +Will remove the server/username/password combination from the secure storage. Method accept `options` object instance but ignores all it values for now. Its reserved for future functionality. ### `requestSharedWebCredentials()` (iOS only) @@ -140,16 +141,18 @@ Inquire if the type of local authentication policy is supported on this device w ### `getSupportedBiometryType([{}])` -Get what type of hardware biometry support the device has. Resolves to a `Keychain.BIOMETRY_TYPE` value when supported, otherwise `null`. +Get what type of hardware biometry support the device has. Resolves to a `Keychain.BIOMETRY_TYPE` value when supported, otherwise `null`. Method accept `options` object instance but ignores all it values for now. Its reserved for future functionality. > This method returns `null`, if the device haven't enrolled into fingerprint/FaceId. Even though it has hardware for it. -### `getSecurityLevel([{}])` (Android only) +### `getSecurityLevel([{ accessControl }])` (Android only) -Get security level that is supported on the current device with the current OS. +Get security level that is supported on the current device with the current OS. Resolves to `Keychain.SECURITY_LEVEL` enum value. ### Options +#### Data Structure Properties/Fields + | Key | Platform | Description | Default | | -------------------------- | ------------ | ------------------------------------------------------------------------------------------------ | ------------------------------------------------------------ | | **`accessControl`** | All | This dictates how a keychain item may be used, see possible values in `Keychain.ACCESS_CONTROL`. | *None* (iOS), `BIOMETRY_ANY` default for Android. | diff --git a/RNKeychainManager/RNKeychainManager.m b/RNKeychainManager/RNKeychainManager.m index c4ad7e59..e6a1a131 100644 --- a/RNKeychainManager/RNKeychainManager.m +++ b/RNKeychainManager/RNKeychainManager.m @@ -174,7 +174,10 @@ SecAccessControlCreateFlags accessControlValue(NSDictionary *options) return 0; } -- (void)insertKeychainEntry:(NSDictionary *)attributes withOptions:(NSDictionary * __nullable)options resolver:(RCTPromiseResolveBlock)resolve rejecter:(RCTPromiseRejectBlock)reject +- (void)insertKeychainEntry:(NSDictionary *)attributes + withOptions:(NSDictionary * __nullable)options + resolver:(RCTPromiseResolveBlock)resolve + rejecter:(RCTPromiseRejectBlock)reject { NSString *accessGroup = accessGroupValue(options); CFStringRef accessible = accessibleValue(options); @@ -217,7 +220,11 @@ - (void)insertKeychainEntry:(NSDictionary *)attributes withOptions:(NSDictionary NSError *error = [NSError errorWithDomain:NSOSStatusErrorDomain code:osStatus userInfo:nil]; return rejectWithError(reject, error); } else { - return resolve(@(YES)); + NSString *service = serviceValue(options); + return resolve(@{ + @"server": service, + @"storage": @"keychain" + }); } }); }); @@ -250,7 +257,9 @@ - (OSStatus)deleteCredentialsForServer:(NSString *)server #pragma mark - RNKeychain #if TARGET_OS_IOS -RCT_EXPORT_METHOD(canCheckAuthentication:(NSDictionary *)options resolver:(RCTPromiseResolveBlock)resolve rejecter:(RCTPromiseRejectBlock)reject) +RCT_EXPORT_METHOD(canCheckAuthentication:(NSDictionary * __nullable)options + resolver:(RCTPromiseResolveBlock)resolve + rejecter:(RCTPromiseRejectBlock)reject) { LAPolicy policyToEvaluate = authPolicy(options); @@ -266,7 +275,8 @@ - (OSStatus)deleteCredentialsForServer:(NSString *)server #endif #if TARGET_OS_IOS -RCT_EXPORT_METHOD(getSupportedBiometryType:(RCTPromiseResolveBlock)resolve rejecter:(RCTPromiseRejectBlock)reject) +RCT_EXPORT_METHOD(getSupportedBiometryType:(RCTPromiseResolveBlock)resolve + rejecter:(RCTPromiseRejectBlock)reject) { NSError *aerr = nil; LAContext *context = [LAContext new]; @@ -285,9 +295,14 @@ - (OSStatus)deleteCredentialsForServer:(NSString *)server } #endif -RCT_EXPORT_METHOD(setGenericPasswordForOptions:(NSDictionary *)options withUsername:(NSString *)username withPassword:(NSString *)password withSecurityLevel:(__unused NSString *)level resolver:(RCTPromiseResolveBlock)resolve rejecter:(RCTPromiseRejectBlock)reject) +RCT_EXPORT_METHOD(setGenericPasswordForOptions:(NSString *)service + withUsername:(NSString *)username + withPassword:(NSString *)password + withOptions:(NSDictionary * __nullable)options + resolver:(RCTPromiseResolveBlock)resolve + rejecter:(RCTPromiseRejectBlock)reject) { - NSString *service = serviceValue(options); + if(service == NULL) service = serviceValue(options); NSDictionary *attributes = attributes = @{ (__bridge NSString *)kSecClass: (__bridge id)(kSecClassGenericPassword), (__bridge NSString *)kSecAttrService: service, @@ -300,9 +315,12 @@ - (OSStatus)deleteCredentialsForServer:(NSString *)server [self insertKeychainEntry:attributes withOptions:options resolver:resolve rejecter:reject]; } -RCT_EXPORT_METHOD(getGenericPasswordForOptions:(NSDictionary *)options resolver:(RCTPromiseResolveBlock)resolve rejecter:(RCTPromiseRejectBlock)reject) +RCT_EXPORT_METHOD(getGenericPasswordForOptions:(NSString *)service + withOptions:(NSDictionary * __nullable)options + resolver:(RCTPromiseResolveBlock)resolve + rejecter:(RCTPromiseRejectBlock)reject) { - NSString *service = serviceValue(options); + if(service == NULL) service = serviceValue(options); NSString *authenticationPrompt = @"Authenticate to retrieve secret"; if (options && options[kAuthenticationPromptMessage]) { authenticationPrompt = options[kAuthenticationPromptMessage]; @@ -340,12 +358,15 @@ - (OSStatus)deleteCredentialsForServer:(NSString *)server return resolve(@{ @"service": service, @"username": username, - @"password": password + @"password": password, + @"storage": @"keychain" }); } -RCT_EXPORT_METHOD(resetGenericPasswordForOptions:(NSDictionary *)options resolver:(RCTPromiseResolveBlock)resolve rejecter:(RCTPromiseRejectBlock)reject) +RCT_EXPORT_METHOD(resetGenericPasswordForOptions:(NSDictionary * __nullable)options + resolver:(RCTPromiseResolveBlock)resolve + rejecter:(RCTPromiseRejectBlock)reject) { NSString *service = serviceValue(options); @@ -359,7 +380,12 @@ - (OSStatus)deleteCredentialsForServer:(NSString *)server return resolve(@(YES)); } -RCT_EXPORT_METHOD(setInternetCredentialsForServer:(NSString *)server withUsername:(NSString*)username withPassword:(NSString*)password withSecurityLevel:(__unused NSString *)level withOptions:(NSDictionary *)options resolver:(RCTPromiseResolveBlock)resolve rejecter:(RCTPromiseRejectBlock)reject) +RCT_EXPORT_METHOD(setInternetCredentialsForServer:(NSString *)server + withUsername:(NSString*)username + withPassword:(NSString*)password + withOptions:(NSDictionary * __nullable)options + resolver:(RCTPromiseResolveBlock)resolve + rejecter:(RCTPromiseRejectBlock)reject) { [self deleteCredentialsForServer:server]; @@ -373,7 +399,9 @@ - (OSStatus)deleteCredentialsForServer:(NSString *)server [self insertKeychainEntry:attributes withOptions:options resolver:resolve rejecter:reject]; } -RCT_EXPORT_METHOD(hasInternetCredentialsForServer:(NSString *)server resolver:(RCTPromiseResolveBlock)resolve rejecter:(RCTPromiseRejectBlock)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); @@ -402,7 +430,10 @@ - (OSStatus)deleteCredentialsForServer:(NSString *)server return rejectWithError(reject, error); } -RCT_EXPORT_METHOD(getInternetCredentialsForServer:(NSString *)server withOptions:(NSDictionary *)options resolver:(RCTPromiseResolveBlock)resolve rejecter:(RCTPromiseRejectBlock)reject) +RCT_EXPORT_METHOD(getInternetCredentialsForServer:(NSString *)server + withOptions:(NSDictionary * __nullable)options + resolver:(RCTPromiseResolveBlock)resolve + rejecter:(RCTPromiseRejectBlock)reject) { NSDictionary *query = @{ (__bridge NSString *)kSecClass: (__bridge id)(kSecClassInternetPassword), @@ -435,12 +466,16 @@ - (OSStatus)deleteCredentialsForServer:(NSString *)server return resolve(@{ @"server": server, @"username": username, - @"password": password + @"password": password, + @"storage": @"keychain" }); } -RCT_EXPORT_METHOD(resetInternetCredentialsForServer:(NSString *)server withOptions:(NSDictionary *)options resolver:(RCTPromiseResolveBlock)resolve rejecter:(RCTPromiseRejectBlock)reject) +RCT_EXPORT_METHOD(resetInternetCredentialsForServer:(NSString *)server + withOptions:(NSDictionary * __nullable)options + resolver:(RCTPromiseResolveBlock)resolve + rejecter:(RCTPromiseRejectBlock)reject) { OSStatus osStatus = [self deleteCredentialsForServer:server]; @@ -453,8 +488,11 @@ - (OSStatus)deleteCredentialsForServer:(NSString *)server } #if TARGET_OS_IOS && !TARGET_OS_UIKITFORMAC -RCT_EXPORT_METHOD(requestSharedWebCredentials:(RCTPromiseResolveBlock)resolve rejecter:(RCTPromiseRejectBlock)reject) +RCT_EXPORT_METHOD(requestSharedWebCredentials:(NSDictionary * __nullable)options + resolver:(RCTPromiseResolveBlock)resolve + rejecter:(RCTPromiseRejectBlock)reject) { + /* Options can be used for specifying the [domain & account == server & username] */ SecRequestSharedWebCredential(NULL, NULL, ^(CFArrayRef credentials, CFErrorRef error) { if (error != NULL) { NSError *nsError = (__bridge NSError *)error; @@ -470,7 +508,8 @@ - (OSStatus)deleteCredentialsForServer:(NSString *)server return resolve(@{ @"server": server, @"username": username, - @"password": password + @"password": password, + @"storage": @"keychain" }); } return resolve(@(NO)); @@ -478,7 +517,12 @@ - (OSStatus)deleteCredentialsForServer:(NSString *)server } -RCT_EXPORT_METHOD(setSharedWebCredentialsForServer:(NSString *)server withUsername:(NSString *)username withPassword:(NSString *)password resolver:(RCTPromiseResolveBlock)resolve rejecter:(RCTPromiseRejectBlock)reject) +RCT_EXPORT_METHOD(setSharedWebCredentialsForServer:(NSString *)server + withUsername:(NSString *)username + withPassword:(NSString *)password + withOptions:(NSDictionary * __nullable)options + resolver:(RCTPromiseResolveBlock)resolve + rejecter:(RCTPromiseRejectBlock)reject) { SecAddSharedWebCredential( (__bridge CFStringRef)server, diff --git a/android/README.md b/android/README.md index e7b04c66..267b8307 100644 --- a/android/README.md +++ b/android/README.md @@ -33,6 +33,11 @@ react-native start --reset-cache ``` +```bash +# set working dir to: 'react-native-keychain/KeychainExample' +/usr/bin/env node node_modules/.bin/react-native start --reset-cache +``` + > Important! to set checkbox `Allow parallel run`. ### Create Automatic self-refreshed TCP ports binding diff --git a/android/build.gradle b/android/build.gradle index 3af89e3f..46b074ef 100755 --- a/android/build.gradle +++ b/android/build.gradle @@ -25,22 +25,17 @@ android { targetSdkVersion safeExtGet('targetSdkVersion', 28) versionCode 1 versionName "1.0" - -// testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner" } + lintOptions { abortOnError false } + compileOptions { sourceCompatibility JavaVersion.VERSION_1_8 targetCompatibility JavaVersion.VERSION_1_8 } - // Gradle automatically adds 'android.test.runner' as a dependency. -// useLibrary 'android.test.runner' -// useLibrary 'android.test.base' -// useLibrary 'android.test.mock' - testOptions { unitTests { includeAndroidResources = true @@ -89,13 +84,3 @@ dependencies { // Uncomment for including JRE implementation of crypto api (that is used in Robolectric tests) // testImplementation fileTree(dir: "${System.properties.get("java.home")}/lib", include: ["jce.jar"]) } -// -//task updateLibrarySourceInExample(type: Copy) { -// from "${project.projectDir}/src/" -// // react-native-keychain/KeychainExample/node_modules/react-native-keychain/android/src -// into "${rootProject.projectDir}/KeychainExample/node_modules/react-native-keychain/android/src" -//} -// -//gradle.projectsEvaluated { -// preBuild.dependsOn(updateLibrarySourceInExample) -//} diff --git a/android/src/main/java/com/oblador/keychain/PrefsStorage.java b/android/src/main/java/com/oblador/keychain/PrefsStorage.java index d653b085..e5768c9b 100644 --- a/android/src/main/java/com/oblador/keychain/PrefsStorage.java +++ b/android/src/main/java/com/oblador/keychain/PrefsStorage.java @@ -75,7 +75,7 @@ public void storeEncryptedEntry(@NonNull final String service, @NonNull final En prefs.edit() .putString(keyForUsername, Base64.encodeToString(encryptionResult.username, Base64.DEFAULT)) .putString(keyForPassword, Base64.encodeToString(encryptionResult.password, Base64.DEFAULT)) - .putString(keyForCipherStorage, encryptionResult.cipherStorage.getCipherStorageName()) + .putString(keyForCipherStorage, encryptionResult.cipherName) .apply(); } diff --git a/android/src/main/java/com/oblador/keychain/cipherStorage/CipherStorage.java b/android/src/main/java/com/oblador/keychain/cipherStorage/CipherStorage.java index eaeacd23..cce079ae 100644 --- a/android/src/main/java/com/oblador/keychain/cipherStorage/CipherStorage.java +++ b/android/src/main/java/com/oblador/keychain/cipherStorage/CipherStorage.java @@ -18,7 +18,7 @@ abstract class CipherResult { public final T username; public final T password; - public CipherResult(T username, T password) { + public CipherResult(final T username, final T password) { this.username = username; this.password = password; } @@ -26,11 +26,18 @@ public CipherResult(T username, T password) { /** Credentials in bytes array, often a result of encryption. */ class EncryptionResult extends CipherResult { - public final CipherStorage cipherStorage; + /** Name of used for encryption cipher storage. */ + public final String cipherName; - public EncryptionResult(final byte[] username, final byte[] password, final CipherStorage cipherStorage) { + /** Main constructor. */ + public EncryptionResult(final byte[] username, final byte[] password, final String cipherName) { super(username, password); - this.cipherStorage = cipherStorage; + this.cipherName = cipherName; + } + + /** Helper constructor. Simplifies cipher name extraction. */ + public EncryptionResult(final byte[] username, final byte[] password, @NonNull final CipherStorage cipherStorage) { + this(username, password, cipherStorage.getCipherStorageName()); } } diff --git a/index.js b/index.js index a4d2e40b..a6856f53 100644 --- a/index.js +++ b/index.js @@ -1,6 +1,7 @@ // @flow -import { NativeModules, Platform } from 'react-native'; -const { RNKeychainManager } = NativeModules; +import {NativeModules, Platform} from 'react-native'; + +const {RNKeychainManager} = NativeModules; export const SECURITY_LEVEL = Object.freeze({ ANY: RNKeychainManager.SECURITY_LEVEL_ANY, @@ -44,6 +45,7 @@ export const STORAGE_TYPE = Object.freeze({ FB: 'FacebookConceal', AES: 'KeystoreAESCBC', RSA: 'KeystoreRSAECB', + KC: 'keychain', // <~ iOS only }); export const SECURITY_RULES = Object.freeze({ @@ -63,6 +65,8 @@ export type SecStorageType = $Values; export type SecSecurityRules = $Values; +export type SecBiometryType = $Values; + export type Options = { accessControl?: SecAccessControl, accessGroup?: string, @@ -91,68 +95,39 @@ export type SharedWebCredentials = {| ...UserCredentials, |}; -function getOptionsArgument(serviceOrOptions?: string | Options) { - if (Platform.OS !== 'ios') { - return typeof serviceOrOptions === 'object' - ? serviceOrOptions.service - : serviceOrOptions; - } - return typeof serviceOrOptions === 'string' - ? { service: serviceOrOptions } - : serviceOrOptions; -} - -function getAccessControl(serviceOrOptions?: string | Options) { - var accessControl = null; - - if (typeof serviceOrOptions === 'object') { - accessControl = serviceOrOptions.accessControl; - } - - return accessControl; -} - -function getMinimumSecurityLevel(serviceOrOptions?: string | Options) { - var specifiedLevel = undefined; - - if (typeof serviceOrOptions === 'object') { - specifiedLevel = serviceOrOptions.securityLevel; - } - - return specifiedLevel || SECURITY_LEVEL.ANY; -} +//* EXPORTS */ /** * Saves the `username` and `password` combination for `service`. * @param {string} username Associated username or e-mail to be saved. * @param {string} password Associated password to be saved. - * @param {object} options An Keychain options object. + * @param {object} options A keychain options object. * @return {Promise} Resolves to `{ service, storage }` when successful */ export function setGenericPassword( username: string, password: string, - options?: Options + options?: Options, ): Promise { - var obj: { service?: string } = options || {}; + const obj: { service?: string } = options || {}; return RNKeychainManager.setGenericPasswordForOptions( obj.service, username, password, - options + options, ); } /** * Fetches login combination for `service`. - * @param {object} options An Keychain options object. + * @param {object} options A keychain options object. * @return {Promise} Resolves to `{ service, username, password, storage }` when successful */ export function getGenericPassword( - options?: Options + options?: Options, ): Promise { - var obj: { service?: string } = options || {}; + const obj: { service?: string } = options || {}; return RNKeychainManager.getGenericPasswordForOptions(obj.service, options); } @@ -163,7 +138,7 @@ export function getGenericPassword( * @return {Promise} Resolves to `true` when successful */ export function resetGenericPassword(options?: Options): Promise { - var obj: { service?: string } = options || {}; + const obj: { service?: string } = options || {}; return RNKeychainManager.resetGenericPasswordForOptions(obj.service, options); } @@ -176,7 +151,7 @@ export function resetGenericPassword(options?: Options): Promise { */ export function hasInternetCredentials( server: string, - options?: Options + options?: Options, ): Promise { return RNKeychainManager.hasInternetCredentialsForServer(server, options); } @@ -193,13 +168,13 @@ export function setInternetCredentials( server: string, username: string, password: string, - options?: Options + options?: Options, ): Promise { return RNKeychainManager.setInternetCredentialsForServer( server, username, password, - options + options, ); } @@ -211,7 +186,7 @@ export function setInternetCredentials( */ export function getInternetCredentials( server: string, - options?: Options + options?: Options, ): Promise { return RNKeychainManager.getInternetCredentialsForServer(server, options); } @@ -224,12 +199,31 @@ export function getInternetCredentials( */ export function resetInternetCredentials( server: string, - options?: Options + options?: Options, ): Promise { return RNKeychainManager.resetInternetCredentialsForServer(server, options); } -/** IOS ONLY */ +/** + * Get what type of hardware biometry support the device has. + * @param {object} options An Keychain options object. + * @return {Promise} Resolves to a `BIOMETRY_TYPE` when supported, otherwise `null` + */ +export function getSupportedBiometryType( + options?: Options, +): Promise { + if (!RNKeychainManager.getSupportedBiometryType) { + return Promise.resolve(null); + } + + if (Platform.OS === 'ios') { + return RNKeychainManager.getSupportedBiometryType(); + } + + return RNKeychainManager.getSupportedBiometryType(options); +} + +//* IOS ONLY */ /** * Asks the user for a shared web credential. @@ -237,13 +231,13 @@ export function resetInternetCredentials( * `false` if denied and throws an error if not supported on platform or there's no shared credentials */ export function requestSharedWebCredentials( - options: ?Options + options?: Options, ): Promise { if (Platform.OS !== 'ios') { return Promise.reject( new Error( - `requestSharedWebCredentials() is not supported on ${Platform.OS} yet` - ) + `requestSharedWebCredentials() is not supported on ${Platform.OS} yet`, + ), ); } return RNKeychainManager.requestSharedWebCredentials(options); @@ -254,27 +248,27 @@ export function requestSharedWebCredentials( * @param {string} server URL to server. * @param {string} username Associated username or e-mail to be saved. * @param {string} password Associated password to be saved. - * @param {object} options An Keychain options object. + * @param {object} options a keychain options object. * @return {Promise} Resolves to `true` when successful */ export function setSharedWebCredentials( server: string, username: string, - password: ?string, - options: ?Options + password?: string, + options?: Options, ): Promise { if (Platform.OS !== 'ios') { return Promise.reject( new Error( - `setSharedWebCredentials() is not supported on ${Platform.OS} yet` - ) + `setSharedWebCredentials() is not supported on ${Platform.OS} yet`, + ), ); } return RNKeychainManager.setSharedWebCredentialsForServer( server, username, password, - options + options, ); } @@ -291,7 +285,7 @@ export function canImplyAuthentication(options?: Options): Promise { return RNKeychainManager.canCheckAuthentication(options); } -/** ANDROID ONLY */ +//* ANDROID ONLY */ /** * (Android only) Returns guaranteed security level supported by this library @@ -300,47 +294,33 @@ export function canImplyAuthentication(options?: Options): Promise { * @return {Promise} Resolves to `SECURITY_LEVEL` when supported, otherwise `null`. */ export function getSecurityLevel( - options?: Options -): Promise> { + options?: Options, +): Promise { if (!RNKeychainManager.getSecurityLevel) { return Promise.resolve(null); } return RNKeychainManager.getSecurityLevel(options); } -/** - * Get what type of hardware biometry support the device has. - * @param {object} options An Keychain options object. - * @return {Promise} Resolves to a `BIOMETRY_TYPE` when supported, otherwise `null` - */ -export function getSupportedBiometryType( - options?: Options -): Promise> { - if (!RNKeychainManager.getSupportedBiometryType) { - return Promise.resolve(null); - } - return RNKeychainManager.getSupportedBiometryType(options); -} - /** Refs: https://www.saltycrane.com/cheat-sheets/flow-type/latest/ */ export default { - SECURITY_LEVEL, - ACCESSIBLE, - ACCESS_CONTROL, - AUTHENTICATION_TYPE, - BIOMETRY_TYPE, - getSecurityLevel, - canImplyAuthentication, - getSupportedBiometryType, - setInternetCredentials, - getInternetCredentials, - resetInternetCredentials, - getOptionsArgument, - getMinimumSecurityLevel, - setGenericPassword, - getGenericPassword, - resetGenericPassword, - requestSharedWebCredentials, - setSharedWebCredentials -} + SECURITY_LEVEL, + ACCESSIBLE, + ACCESS_CONTROL, + AUTHENTICATION_TYPE, + BIOMETRY_TYPE, + STORAGE_TYPE, + SECURITY_RULES, + getSecurityLevel, + canImplyAuthentication, + getSupportedBiometryType, + setInternetCredentials, + getInternetCredentials, + resetInternetCredentials, + setGenericPassword, + getGenericPassword, + resetGenericPassword, + requestSharedWebCredentials, + setSharedWebCredentials, +}; diff --git a/typings/react-native-keychain.d.ts b/typings/react-native-keychain.d.ts index b9c52c80..8f19dbf2 100644 --- a/typings/react-native-keychain.d.ts +++ b/typings/react-native-keychain.d.ts @@ -54,6 +54,7 @@ declare module 'react-native-keychain' { FB = 'FacebookConceal', AES = 'KeystoreAESCBC', RSA = 'KeystoreRSAECB', + KC = 'keychain', } export enum SECURITY_RULES { @@ -109,7 +110,7 @@ declare module 'react-native-keychain' { function getSupportedBiometryType( options?: Options - ): Promise; + ): Promise; /** IOS ONLY */ @@ -128,5 +129,5 @@ declare module 'react-native-keychain' { /** ANDROID ONLY */ - function getSecurityLevel(options?: Options): Promise; + function getSecurityLevel(options?: Options): Promise; }