Remove *PasswordWithAuthentication and replace with accessControl option
This commit is contained in:
parent
37ea15ae5e
commit
add90c5769
|
@ -2,6 +2,7 @@ import React, { Component } from 'react';
|
||||||
import {
|
import {
|
||||||
KeyboardAvoidingView,
|
KeyboardAvoidingView,
|
||||||
Platform,
|
Platform,
|
||||||
|
SegmentedControlIOS,
|
||||||
StyleSheet,
|
StyleSheet,
|
||||||
Text,
|
Text,
|
||||||
TextInput,
|
TextInput,
|
||||||
|
@ -11,12 +12,16 @@ import {
|
||||||
|
|
||||||
import * as Keychain from 'react-native-keychain';
|
import * as Keychain from 'react-native-keychain';
|
||||||
|
|
||||||
|
const ACCESS_CONTROL_OPTIONS = ['None', 'Passcode', 'Password'];
|
||||||
|
const ACCESS_CONTROL_MAP = [null, Keychain.ACCESS_CONTROL.DEVICE_PASSCODE, Keychain.ACCESS_CONTROL.APPLICATION_PASSWORD, Keychain.ACCESS_CONTROL.BIOMETRY_CURRENT_SET]
|
||||||
|
|
||||||
export default class KeychainExample extends Component {
|
export default class KeychainExample extends Component {
|
||||||
state = {
|
state = {
|
||||||
username: '',
|
username: '',
|
||||||
password: '',
|
password: '',
|
||||||
status: '',
|
status: '',
|
||||||
biometryType: null,
|
biometryType: null,
|
||||||
|
accessControl: null,
|
||||||
};
|
};
|
||||||
|
|
||||||
componentDidMount() {
|
componentDidMount() {
|
||||||
|
@ -25,24 +30,13 @@ export default class KeychainExample extends Component {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
async save() {
|
async save(accessControl) {
|
||||||
try {
|
try {
|
||||||
if (this.state.biometryType) {
|
|
||||||
await Keychain.setPasswordWithAuthentication(
|
|
||||||
this.state.username,
|
|
||||||
this.state.password,
|
|
||||||
{
|
|
||||||
accessControl:
|
|
||||||
Keychain.ACCESS_CONTROL.TOUCH_ID_ANY_OR_DEVICE_PASSCODE,
|
|
||||||
authenticationType: Keychain.AUTHENTICATION_TYPE.BIOMETRICS,
|
|
||||||
}
|
|
||||||
);
|
|
||||||
} else {
|
|
||||||
await Keychain.setGenericPassword(
|
await Keychain.setGenericPassword(
|
||||||
this.state.username,
|
this.state.username,
|
||||||
this.state.password
|
this.state.password,
|
||||||
|
{ accessControl: this.state.accessControl }
|
||||||
);
|
);
|
||||||
}
|
|
||||||
this.setState({ status: 'Credentials saved!' });
|
this.setState({ status: 'Credentials saved!' });
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
this.setState({ status: 'Could not save credentials, ' + err });
|
this.setState({ status: 'Could not save credentials, ' + err });
|
||||||
|
@ -51,13 +45,7 @@ export default class KeychainExample extends Component {
|
||||||
|
|
||||||
async load() {
|
async load() {
|
||||||
try {
|
try {
|
||||||
const credentials = await (this.state.biometryType
|
const credentials = await Keychain.getGenericPassword();
|
||||||
? Keychain.getPasswordWithAuthentication({
|
|
||||||
accessControl:
|
|
||||||
Keychain.ACCESS_CONTROL.TOUCH_ID_ANY_OR_DEVICE_PASSCODE,
|
|
||||||
authenticationType: Keychain.AUTHENTICATION_TYPE.BIOMETRICS,
|
|
||||||
})
|
|
||||||
: Keychain.getGenericPassword());
|
|
||||||
if (credentials) {
|
if (credentials) {
|
||||||
this.setState({ ...credentials, status: 'Credentials loaded!' });
|
this.setState({ ...credentials, status: 'Credentials loaded!' });
|
||||||
} else {
|
} else {
|
||||||
|
@ -113,14 +101,24 @@ export default class KeychainExample extends Component {
|
||||||
underlineColorAndroid="transparent"
|
underlineColorAndroid="transparent"
|
||||||
/>
|
/>
|
||||||
</View>
|
</View>
|
||||||
|
{Platform.OS === 'ios' && (
|
||||||
|
<View style={styles.field}>
|
||||||
|
<Text style={styles.label}>Access Control</Text>
|
||||||
|
<SegmentedControlIOS
|
||||||
|
selectedIndex={0}
|
||||||
|
values={this.state.biometryType ? [...ACCESS_CONTROL_OPTIONS, this.state.biometryType] : ACCESS_CONTROL_OPTIONS}
|
||||||
|
onChange={({ nativeEvent }) => {
|
||||||
|
this.setState({
|
||||||
|
accessControl: ACCESS_CONTROL_MAP[nativeEvent.selectedSegmentIndex],
|
||||||
|
});
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
</View>
|
||||||
|
)}
|
||||||
{!!this.state.status && (
|
{!!this.state.status && (
|
||||||
<Text style={styles.status}>{this.state.status}</Text>
|
<Text style={styles.status}>{this.state.status}</Text>
|
||||||
)}
|
)}
|
||||||
{!!this.state.biometryType && (
|
|
||||||
<Text style={styles.biometryType}>
|
|
||||||
Supported biometry: {this.state.biometryType}
|
|
||||||
</Text>
|
|
||||||
)}
|
|
||||||
<View style={styles.buttons}>
|
<View style={styles.buttons}>
|
||||||
<TouchableHighlight
|
<TouchableHighlight
|
||||||
onPress={() => this.save()}
|
onPress={() => this.save()}
|
||||||
|
@ -156,12 +154,11 @@ export default class KeychainExample extends Component {
|
||||||
const styles = StyleSheet.create({
|
const styles = StyleSheet.create({
|
||||||
container: {
|
container: {
|
||||||
flex: 1,
|
flex: 1,
|
||||||
alignItems: 'center',
|
|
||||||
justifyContent: 'center',
|
justifyContent: 'center',
|
||||||
backgroundColor: '#F5FCFF',
|
backgroundColor: '#F5FCFF',
|
||||||
},
|
},
|
||||||
content: {
|
content: {
|
||||||
width: 250,
|
marginHorizontal: 20,
|
||||||
},
|
},
|
||||||
title: {
|
title: {
|
||||||
fontSize: 28,
|
fontSize: 28,
|
||||||
|
|
|
@ -11,26 +11,9 @@
|
||||||
|
|
||||||
#import <React/RCTBundleURLProvider.h>
|
#import <React/RCTBundleURLProvider.h>
|
||||||
#import <React/RCTRootView.h>
|
#import <React/RCTRootView.h>
|
||||||
#import <RNKeychain/RNKeychainAuthenticationListener.h>
|
|
||||||
|
|
||||||
@interface AppDelegate() <RNKeychainAuthenticationListener>
|
|
||||||
|
|
||||||
@end
|
|
||||||
|
|
||||||
@implementation AppDelegate
|
@implementation AppDelegate
|
||||||
|
|
||||||
@synthesize willPromptForAuthentication = _willPromptForAuthentication;
|
|
||||||
|
|
||||||
- (void)setWillPromptForAuthentication:(BOOL)willPromptForAuthentication {
|
|
||||||
_willPromptForAuthentication = willPromptForAuthentication;
|
|
||||||
|
|
||||||
if (willPromptForAuthentication) {
|
|
||||||
NSLog(@"APPDELEGATE::: will prompt TouchId");
|
|
||||||
} else {
|
|
||||||
NSLog(@"APPDELEGATE::: ended prompt TouchId");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
|
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
|
||||||
{
|
{
|
||||||
NSURL *jsCodeLocation;
|
NSURL *jsCodeLocation;
|
||||||
|
|
10
README.md
10
README.md
|
@ -127,12 +127,12 @@ Keychain
|
||||||
|
|
||||||
| Key | Applies to | Description | Default |
|
| Key | Applies to | Description | Default |
|
||||||
|---|---|---|---|
|
|---|---|---|---|
|
||||||
|**`accessControl`**|`PasswordWithAuthentication`|This dictates how a keychain item may be used, see possible values in `Keychain.ACCESS_CONTROL`. |*`Keychain.ACCESS_CONTROL.TOUCH_ID_CURRENT_SET_OR_DEVICE_PASSCODE`*|
|
|**`accessControl`**|`setGenericPassword`, `setInternetCredentials`|This dictates how a keychain item may be used, see possible values in `Keychain.ACCESS_CONTROL`. |*None*|
|
||||||
|**`accessible`**|`GenericPassword`, `InternetCredentials`|This dictates when a keychain item is accessible, see possible values in `Keychain.ACCESSIBLE`. |*`Keychain.ACCESSIBLE.WHEN_UNLOCKED`*|
|
|**`accessible`**|`setGenericPassword`, `setInternetCredentials`|This dictates when a keychain item is accessible, see possible values in `Keychain.ACCESSIBLE`. |*`Keychain.ACCESSIBLE.WHEN_UNLOCKED`*|
|
||||||
|**`accessGroup`**|`GenericPassword`, `InternetCredentials`, `PasswordWithAuthentication`|In which App Group to share the keychain. Requires additional setup with entitlements. |*None*|
|
|**`accessGroup`**|`setGenericPassword`, `setInternetCredentials`|In which App Group to share the keychain. Requires additional setup with entitlements. |*None*|
|
||||||
|**`authenticationPrompt`**|`PasswordWithAuthentication`|What to prompt the user when unlocking the keychain with biometry or device password. |`Authenticate to retrieve secret!`|
|
|**`authenticationPrompt`**|`getGenericPassword`, `getInternetCredentials`|What to prompt the user when unlocking the keychain with biometry or device password. |`Authenticate to retrieve secret`|
|
||||||
|**`authenticationType`**|`canImplyAuthentication`|Policies specifying which forms of authentication are acceptable. |`Keychain.AUTHENTICATION_TYPE.DEVICE_PASSCODE_OR_BIOMETRICS`|
|
|**`authenticationType`**|`canImplyAuthentication`|Policies specifying which forms of authentication are acceptable. |`Keychain.AUTHENTICATION_TYPE.DEVICE_PASSCODE_OR_BIOMETRICS`|
|
||||||
|**`service`**|`GenericPassword`, `PasswordWithAuthentication`|Reverse domain name qualifier for the service associated with password. |*App bundle ID*|
|
|**`service`**|`setGenericPassword`, `getGenericPassword`|Reverse domain name qualifier for the service associated with password. |*App bundle ID*|
|
||||||
|
|
||||||
#### `Keychain.ACCESS_CONTROL` enum
|
#### `Keychain.ACCESS_CONTROL` enum
|
||||||
|
|
||||||
|
|
|
@ -10,11 +10,7 @@
|
||||||
5D82368F1B0CE3CB005A9EF3 /* RNKeychainManager.m in Sources */ = {isa = PBXBuildFile; fileRef = 5D82368C1B0CE2A6005A9EF3 /* RNKeychainManager.m */; };
|
5D82368F1B0CE3CB005A9EF3 /* RNKeychainManager.m in Sources */ = {isa = PBXBuildFile; fileRef = 5D82368C1B0CE2A6005A9EF3 /* RNKeychainManager.m */; };
|
||||||
5D8236911B0CE3D6005A9EF3 /* Security.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 5D8236901B0CE3D6005A9EF3 /* Security.framework */; };
|
5D8236911B0CE3D6005A9EF3 /* Security.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 5D8236901B0CE3D6005A9EF3 /* Security.framework */; };
|
||||||
5DE632D52043423E004F9598 /* LocalAuthentication.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 5DE632D42043423E004F9598 /* LocalAuthentication.framework */; };
|
5DE632D52043423E004F9598 /* LocalAuthentication.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 5DE632D42043423E004F9598 /* LocalAuthentication.framework */; };
|
||||||
5DE632D720434276004F9598 /* RNKeychainAuthenticationListener.h in Copy Headers */ = {isa = PBXBuildFile; fileRef = 5DE632D62043426A004F9598 /* RNKeychainAuthenticationListener.h */; };
|
|
||||||
5DE632D920434291004F9598 /* RNKeychainAuthenticationListener.h in Headers */ = {isa = PBXBuildFile; fileRef = 5DE632D62043426A004F9598 /* RNKeychainAuthenticationListener.h */; };
|
|
||||||
5DE632DB204342AE004F9598 /* Security.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 5DE632DA204342AE004F9598 /* Security.framework */; };
|
5DE632DB204342AE004F9598 /* Security.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 5DE632DA204342AE004F9598 /* Security.framework */; };
|
||||||
5DE632DC204342B5004F9598 /* RNKeychainAuthenticationListener.h in Copy Headers */ = {isa = PBXBuildFile; fileRef = 5DE632D62043426A004F9598 /* RNKeychainAuthenticationListener.h */; };
|
|
||||||
5DE632DE204342C3004F9598 /* RNKeychainAuthenticationListener.h in Headers */ = {isa = PBXBuildFile; fileRef = 5DE632D62043426A004F9598 /* RNKeychainAuthenticationListener.h */; };
|
|
||||||
6478986B1F38BFA100DA1C12 /* RNKeychainManager.m in Sources */ = {isa = PBXBuildFile; fileRef = 5D82368C1B0CE2A6005A9EF3 /* RNKeychainManager.m */; };
|
6478986B1F38BFA100DA1C12 /* RNKeychainManager.m in Sources */ = {isa = PBXBuildFile; fileRef = 5D82368C1B0CE2A6005A9EF3 /* RNKeychainManager.m */; };
|
||||||
/* End PBXBuildFile section */
|
/* End PBXBuildFile section */
|
||||||
|
|
||||||
|
@ -25,7 +21,6 @@
|
||||||
dstPath = "include/$(PRODUCT_NAME)";
|
dstPath = "include/$(PRODUCT_NAME)";
|
||||||
dstSubfolderSpec = 16;
|
dstSubfolderSpec = 16;
|
||||||
files = (
|
files = (
|
||||||
5DE632D720434276004F9598 /* RNKeychainAuthenticationListener.h in Copy Headers */,
|
|
||||||
);
|
);
|
||||||
name = "Copy Headers";
|
name = "Copy Headers";
|
||||||
runOnlyForDeploymentPostprocessing = 0;
|
runOnlyForDeploymentPostprocessing = 0;
|
||||||
|
@ -36,7 +31,6 @@
|
||||||
dstPath = "include/$(PRODUCT_NAME)";
|
dstPath = "include/$(PRODUCT_NAME)";
|
||||||
dstSubfolderSpec = 16;
|
dstSubfolderSpec = 16;
|
||||||
files = (
|
files = (
|
||||||
5DE632DC204342B5004F9598 /* RNKeychainAuthenticationListener.h in Copy Headers */,
|
|
||||||
);
|
);
|
||||||
name = "Copy Headers";
|
name = "Copy Headers";
|
||||||
runOnlyForDeploymentPostprocessing = 0;
|
runOnlyForDeploymentPostprocessing = 0;
|
||||||
|
@ -46,10 +40,9 @@
|
||||||
/* Begin PBXFileReference section */
|
/* Begin PBXFileReference section */
|
||||||
5D82366F1B0CE05B005A9EF3 /* libRNKeychain.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = libRNKeychain.a; sourceTree = BUILT_PRODUCTS_DIR; };
|
5D82366F1B0CE05B005A9EF3 /* libRNKeychain.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = libRNKeychain.a; sourceTree = BUILT_PRODUCTS_DIR; };
|
||||||
5D82368B1B0CE2A6005A9EF3 /* RNKeychainManager.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RNKeychainManager.h; sourceTree = "<group>"; };
|
5D82368B1B0CE2A6005A9EF3 /* RNKeychainManager.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RNKeychainManager.h; sourceTree = "<group>"; };
|
||||||
5D82368C1B0CE2A6005A9EF3 /* RNKeychainManager.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RNKeychainManager.m; sourceTree = "<group>"; };
|
5D82368C1B0CE2A6005A9EF3 /* RNKeychainManager.m */ = {isa = PBXFileReference; fileEncoding = 4; indentWidth = 2; lastKnownFileType = sourcecode.c.objc; path = RNKeychainManager.m; sourceTree = "<group>"; tabWidth = 2; };
|
||||||
5D8236901B0CE3D6005A9EF3 /* Security.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Security.framework; path = System/Library/Frameworks/Security.framework; sourceTree = SDKROOT; };
|
5D8236901B0CE3D6005A9EF3 /* Security.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Security.framework; path = System/Library/Frameworks/Security.framework; sourceTree = SDKROOT; };
|
||||||
5DE632D42043423E004F9598 /* LocalAuthentication.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = LocalAuthentication.framework; path = System/Library/Frameworks/LocalAuthentication.framework; sourceTree = SDKROOT; };
|
5DE632D42043423E004F9598 /* LocalAuthentication.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = LocalAuthentication.framework; path = System/Library/Frameworks/LocalAuthentication.framework; sourceTree = SDKROOT; };
|
||||||
5DE632D62043426A004F9598 /* RNKeychainAuthenticationListener.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RNKeychainAuthenticationListener.h; sourceTree = "<group>"; };
|
|
||||||
5DE632DA204342AE004F9598 /* Security.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Security.framework; path = Platforms/AppleTVOS.platform/Developer/SDKs/AppleTVOS11.2.sdk/System/Library/Frameworks/Security.framework; sourceTree = DEVELOPER_DIR; };
|
5DE632DA204342AE004F9598 /* Security.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Security.framework; path = Platforms/AppleTVOS.platform/Developer/SDKs/AppleTVOS11.2.sdk/System/Library/Frameworks/Security.framework; sourceTree = DEVELOPER_DIR; };
|
||||||
6478985F1F38BF9100DA1C12 /* libRNKeychain.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = libRNKeychain.a; sourceTree = BUILT_PRODUCTS_DIR; };
|
6478985F1F38BF9100DA1C12 /* libRNKeychain.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = libRNKeychain.a; sourceTree = BUILT_PRODUCTS_DIR; };
|
||||||
/* End PBXFileReference section */
|
/* End PBXFileReference section */
|
||||||
|
@ -97,7 +90,6 @@
|
||||||
5D82368A1B0CE2A6005A9EF3 /* RNKeychainManager */ = {
|
5D82368A1B0CE2A6005A9EF3 /* RNKeychainManager */ = {
|
||||||
isa = PBXGroup;
|
isa = PBXGroup;
|
||||||
children = (
|
children = (
|
||||||
5DE632D62043426A004F9598 /* RNKeychainAuthenticationListener.h */,
|
|
||||||
5D82368B1B0CE2A6005A9EF3 /* RNKeychainManager.h */,
|
5D82368B1B0CE2A6005A9EF3 /* RNKeychainManager.h */,
|
||||||
5D82368C1B0CE2A6005A9EF3 /* RNKeychainManager.m */,
|
5D82368C1B0CE2A6005A9EF3 /* RNKeychainManager.m */,
|
||||||
);
|
);
|
||||||
|
@ -121,7 +113,6 @@
|
||||||
isa = PBXHeadersBuildPhase;
|
isa = PBXHeadersBuildPhase;
|
||||||
buildActionMask = 2147483647;
|
buildActionMask = 2147483647;
|
||||||
files = (
|
files = (
|
||||||
5DE632D920434291004F9598 /* RNKeychainAuthenticationListener.h in Headers */,
|
|
||||||
);
|
);
|
||||||
runOnlyForDeploymentPostprocessing = 0;
|
runOnlyForDeploymentPostprocessing = 0;
|
||||||
};
|
};
|
||||||
|
@ -129,7 +120,6 @@
|
||||||
isa = PBXHeadersBuildPhase;
|
isa = PBXHeadersBuildPhase;
|
||||||
buildActionMask = 2147483647;
|
buildActionMask = 2147483647;
|
||||||
files = (
|
files = (
|
||||||
5DE632DE204342C3004F9598 /* RNKeychainAuthenticationListener.h in Headers */,
|
|
||||||
);
|
);
|
||||||
runOnlyForDeploymentPostprocessing = 0;
|
runOnlyForDeploymentPostprocessing = 0;
|
||||||
};
|
};
|
||||||
|
|
|
@ -1,22 +0,0 @@
|
||||||
//
|
|
||||||
// TouchIdPromptListener.h
|
|
||||||
// RNKeychain
|
|
||||||
//
|
|
||||||
// Created by Steffen Blümm on 05/04/17.
|
|
||||||
// Copyright © 2017 Joel Arvidsson. All rights reserved.
|
|
||||||
//
|
|
||||||
|
|
||||||
#import <Foundation/Foundation.h>
|
|
||||||
|
|
||||||
/**
|
|
||||||
This is a protocol to be implemented by the AppDelegate in case
|
|
||||||
the AppDelegate takes precautions to obfuscate the screen when
|
|
||||||
the app resigns active state.
|
|
||||||
Thus the AppDelegate can avoid to obfuscating the screen when
|
|
||||||
the TouchId-prompt is brought up by the OS
|
|
||||||
*/
|
|
||||||
@protocol RNKeychainAuthenticationListener <NSObject>
|
|
||||||
|
|
||||||
@property (nonatomic, assign) BOOL willPromptForAuthentication;
|
|
||||||
|
|
||||||
@end
|
|
|
@ -15,8 +15,6 @@
|
||||||
#import <LocalAuthentication/LAContext.h>
|
#import <LocalAuthentication/LAContext.h>
|
||||||
#import <UIKit/UIKit.h>
|
#import <UIKit/UIKit.h>
|
||||||
|
|
||||||
#import "RNKeychainAuthenticationListener.h"
|
|
||||||
|
|
||||||
@implementation RNKeychainManager
|
@implementation RNKeychainManager
|
||||||
|
|
||||||
@synthesize bridge = _bridge;
|
@synthesize bridge = _bridge;
|
||||||
|
@ -148,43 +146,69 @@ LAPolicy authPolicy(NSDictionary *options)
|
||||||
return LAPolicyDeviceOwnerAuthentication;
|
return LAPolicyDeviceOwnerAuthentication;
|
||||||
}
|
}
|
||||||
|
|
||||||
SecAccessControlCreateFlags secureAccessControl(NSDictionary *options)
|
SecAccessControlCreateFlags accessControlValue(NSDictionary *options)
|
||||||
{
|
{
|
||||||
if (options && options[kAccessControlType]) {
|
if (options && options[kAccessControlType] && [options[kAccessControlType] isKindOfClass:[NSString class]]) {
|
||||||
if ([ options[kAccessControlType] isEqualToString: kAccessControlUserPresence ]) {
|
if ([options[kAccessControlType] isEqualToString: kAccessControlUserPresence]) {
|
||||||
return kSecAccessControlUserPresence;
|
return kSecAccessControlUserPresence;
|
||||||
}
|
}
|
||||||
else if ([ options[kAccessControlType] isEqualToString: kAccessControlBiometryAny ]) {
|
else if ([options[kAccessControlType] isEqualToString: kAccessControlBiometryAny]) {
|
||||||
return kSecAccessControlTouchIDAny;
|
return kSecAccessControlTouchIDAny;
|
||||||
}
|
}
|
||||||
else if ([ options[kAccessControlType] isEqualToString: kAccessControlBiometryCurrentSet ]) {
|
else if ([options[kAccessControlType] isEqualToString: kAccessControlBiometryCurrentSet]) {
|
||||||
return kSecAccessControlTouchIDCurrentSet;
|
return kSecAccessControlTouchIDCurrentSet;
|
||||||
}
|
}
|
||||||
else if ([ options[kAccessControlType] isEqualToString: kAccessControlDevicePasscode ]) {
|
else if ([options[kAccessControlType] isEqualToString: kAccessControlDevicePasscode]) {
|
||||||
return kSecAccessControlDevicePasscode;
|
return kSecAccessControlDevicePasscode;
|
||||||
}
|
}
|
||||||
else if ([ options[kAccessControlType] isEqualToString: kAccessControlBiometryAnyOrDevicePasscode ]) {
|
else if ([options[kAccessControlType] isEqualToString: kAccessControlBiometryAnyOrDevicePasscode]) {
|
||||||
return kSecAccessControlTouchIDAny|kSecAccessControlOr|kSecAccessControlDevicePasscode;
|
return kSecAccessControlTouchIDAny|kSecAccessControlOr|kSecAccessControlDevicePasscode;
|
||||||
}
|
}
|
||||||
else if ([ options[kAccessControlType] isEqualToString: kAccessControlBiometryCurrentSetOrDevicePasscode ]) {
|
else if ([options[kAccessControlType] isEqualToString: kAccessControlBiometryCurrentSetOrDevicePasscode]) {
|
||||||
return kSecAccessControlTouchIDCurrentSet|kSecAccessControlOr|kSecAccessControlDevicePasscode;
|
return kSecAccessControlTouchIDCurrentSet|kSecAccessControlOr|kSecAccessControlDevicePasscode;
|
||||||
}
|
}
|
||||||
else if ([ options[kAccessControlType] isEqualToString: kAccessControlApplicationPassword ]) {
|
else if ([options[kAccessControlType] isEqualToString: kAccessControlApplicationPassword]) {
|
||||||
return kSecAccessControlApplicationPassword;
|
return kSecAccessControlApplicationPassword;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return kSecAccessControlTouchIDCurrentSet|kSecAccessControlOr|kSecAccessControlDevicePasscode;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
//LAPolicyDeviceOwnerAuthenticationWithBiometrics | LAPolicyDeviceOwnerAuthentication
|
- (void)insertKeychainEntry:(NSDictionary *)attributes withOptions:(NSDictionary * __nullable)options resolver:(RCTPromiseResolveBlock)resolve rejecter:(RCTPromiseRejectBlock)reject
|
||||||
|
|
||||||
- (void)insertKeychainEntry:(NSDictionary *)attributes withAccessGroup:(NSString * __nullable)accessGroup resolver:(RCTPromiseResolveBlock)resolve rejecter:(RCTPromiseRejectBlock)reject
|
|
||||||
{
|
{
|
||||||
if (accessGroup != nil) {
|
NSString *accessGroup = accessGroupValue(options);
|
||||||
|
CFStringRef accessible = accessibleValue(options);
|
||||||
|
SecAccessControlCreateFlags accessControl = accessControlValue(options);
|
||||||
|
|
||||||
NSMutableDictionary *mAttributes = attributes.mutableCopy;
|
NSMutableDictionary *mAttributes = attributes.mutableCopy;
|
||||||
mAttributes[(__bridge NSString *)kSecAttrAccessGroup] = accessGroup;
|
|
||||||
attributes = [NSDictionary dictionaryWithDictionary:mAttributes];
|
if (accessControl) {
|
||||||
|
NSError *aerr = nil;
|
||||||
|
BOOL canAuthenticate = [[LAContext new] canEvaluatePolicy:LAPolicyDeviceOwnerAuthentication error:&aerr];
|
||||||
|
if (aerr || !canAuthenticate) {
|
||||||
|
return rejectWithError(reject, aerr);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
CFErrorRef error = NULL;
|
||||||
|
SecAccessControlRef sacRef = SecAccessControlCreateWithFlags(kCFAllocatorDefault,
|
||||||
|
accessible,
|
||||||
|
accessControl,
|
||||||
|
&error);
|
||||||
|
|
||||||
|
if (error) {
|
||||||
|
return rejectWithError(reject, aerr);
|
||||||
|
}
|
||||||
|
mAttributes[(__bridge NSString *)kSecAttrAccessControl] = (__bridge id)sacRef;
|
||||||
|
} else {
|
||||||
|
mAttributes[(__bridge NSString *)kSecAttrAccessible] = (__bridge id)accessible;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (accessGroup != nil) {
|
||||||
|
mAttributes[(__bridge NSString *)kSecAttrAccessGroup] = accessGroup;
|
||||||
|
}
|
||||||
|
|
||||||
|
attributes = [NSDictionary dictionaryWithDictionary:mAttributes];
|
||||||
|
|
||||||
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
|
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
|
||||||
OSStatus osStatus = SecItemAdd((__bridge CFDictionaryRef) attributes, NULL);
|
OSStatus osStatus = SecItemAdd((__bridge CFDictionaryRef) attributes, NULL);
|
||||||
|
|
||||||
|
@ -223,7 +247,7 @@ SecAccessControlCreateFlags secureAccessControl(NSDictionary *options)
|
||||||
return SecItemDelete((__bridge CFDictionaryRef) query);
|
return SecItemDelete((__bridge CFDictionaryRef) query);
|
||||||
}
|
}
|
||||||
|
|
||||||
#pragma mark - Proposed functionality - RCT_EXPORT_METHOD
|
#pragma mark - RNKeychain
|
||||||
|
|
||||||
#if TARGET_OS_IOS
|
#if TARGET_OS_IOS
|
||||||
RCT_EXPORT_METHOD(canCheckAuthentication:(NSDictionary *)options resolver:(RCTPromiseResolveBlock)resolve rejecter:(RCTPromiseRejectBlock)reject)
|
RCT_EXPORT_METHOD(canCheckAuthentication:(NSDictionary *)options resolver:(RCTPromiseResolveBlock)resolve rejecter:(RCTPromiseRejectBlock)reject)
|
||||||
|
@ -261,129 +285,28 @@ RCT_EXPORT_METHOD(getSupportedBiometryType:(RCTPromiseResolveBlock)resolve rejec
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
|
||||||
RCT_EXPORT_METHOD(setPasswordWithAuthentication:(NSDictionary *)options withUsername:(NSString *)username withPassword:(NSString *)password resolver:(RCTPromiseResolveBlock)resolve rejecter:(RCTPromiseRejectBlock)reject)
|
|
||||||
{
|
|
||||||
NSString *service = serviceValue(options);
|
|
||||||
NSError *aerr = nil;
|
|
||||||
BOOL canAuthenticate = [[LAContext new] canEvaluatePolicy:LAPolicyDeviceOwnerAuthentication error:&aerr];
|
|
||||||
if (aerr || !canAuthenticate) {
|
|
||||||
return rejectWithError(reject, aerr);
|
|
||||||
}
|
|
||||||
|
|
||||||
[self deletePasswordsForService:service];
|
|
||||||
|
|
||||||
CFErrorRef error = NULL;
|
|
||||||
SecAccessControlRef sacRef = SecAccessControlCreateWithFlags(kCFAllocatorDefault,
|
|
||||||
kSecAttrAccessibleWhenUnlockedThisDeviceOnly, //kSecAttrAccessibleWhenPasscodeSetThisDeviceOnly,
|
|
||||||
secureAccessControl(options),
|
|
||||||
&error);
|
|
||||||
|
|
||||||
if (error) {
|
|
||||||
// ok: failed
|
|
||||||
return rejectWithError(reject, aerr);
|
|
||||||
}
|
|
||||||
|
|
||||||
NSDictionary *attributes = @{
|
|
||||||
(__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
|
|
||||||
};
|
|
||||||
|
|
||||||
[self insertKeychainEntry:attributes withAccessGroup:accessGroupValue(options) resolver:resolve rejecter:reject];
|
|
||||||
}
|
|
||||||
|
|
||||||
RCT_EXPORT_METHOD(getPasswordWithAuthentication:(NSDictionary *)options resolver:(RCTPromiseResolveBlock)resolve rejecter:(RCTPromiseRejectBlock)reject)
|
|
||||||
{
|
|
||||||
NSString *service = serviceValue(options);
|
|
||||||
NSString *promptMessage = @"Authenticate to retrieve secret!";
|
|
||||||
if (options && options[kAuthenticationPromptMessage]) {
|
|
||||||
promptMessage = options[kAuthenticationPromptMessage];
|
|
||||||
}
|
|
||||||
|
|
||||||
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: promptMessage
|
|
||||||
};
|
|
||||||
|
|
||||||
// Notify AppDelegate
|
|
||||||
dispatch_async(dispatch_get_main_queue(), ^{
|
|
||||||
[self notifyAuthenticationListener: YES];
|
|
||||||
});
|
|
||||||
|
|
||||||
// Look up password for service in the keychain
|
|
||||||
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
|
|
||||||
__block NSDictionary* found = nil;
|
|
||||||
CFTypeRef foundTypeRef = NULL;
|
|
||||||
OSStatus osStatus = SecItemCopyMatching((__bridge CFDictionaryRef) query, (CFTypeRef*)&foundTypeRef);
|
|
||||||
|
|
||||||
dispatch_async(dispatch_get_main_queue(), ^{
|
|
||||||
[self notifyAuthenticationListener: NO];
|
|
||||||
|
|
||||||
if (osStatus != noErr && osStatus != errSecItemNotFound) {
|
|
||||||
NSError *error = [NSError errorWithDomain:NSOSStatusErrorDomain code:osStatus userInfo:nil];
|
|
||||||
return rejectWithError(reject, error);
|
|
||||||
}
|
|
||||||
|
|
||||||
found = (__bridge NSDictionary*)(foundTypeRef);
|
|
||||||
if (!found) {
|
|
||||||
return resolve(@(NO));
|
|
||||||
}
|
|
||||||
|
|
||||||
// Found
|
|
||||||
NSString *username = (NSString *) [found objectForKey:(__bridge id)(kSecAttrAccount)];
|
|
||||||
NSString *password = [[NSString alloc] initWithData:[found objectForKey:(__bridge id)(kSecValueData)] encoding:NSUTF8StringEncoding];
|
|
||||||
|
|
||||||
return resolve(@{
|
|
||||||
@"service": service,
|
|
||||||
@"username": username,
|
|
||||||
@"password": password
|
|
||||||
});
|
|
||||||
});
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
- (void) notifyAuthenticationListener:(BOOL)willPresent {
|
|
||||||
id<UIApplicationDelegate> appDelegate = [ UIApplication sharedApplication ].delegate;
|
|
||||||
|
|
||||||
if ([ appDelegate conformsToProtocol:@protocol(RNKeychainAuthenticationListener) ]) {
|
|
||||||
((id<RNKeychainAuthenticationListener>)appDelegate).willPromptForAuthentication = willPresent;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#pragma mark - RNKeychain
|
|
||||||
|
|
||||||
RCT_EXPORT_METHOD(setGenericPasswordForOptions:(NSDictionary *)options withUsername:(NSString *)username withPassword:(NSString *)password resolver:(RCTPromiseResolveBlock)resolve rejecter:(RCTPromiseRejectBlock)reject)
|
RCT_EXPORT_METHOD(setGenericPasswordForOptions:(NSDictionary *)options withUsername:(NSString *)username withPassword:(NSString *)password resolver:(RCTPromiseResolveBlock)resolve rejecter:(RCTPromiseRejectBlock)reject)
|
||||||
{
|
{
|
||||||
NSString *service = serviceValue(options);
|
NSString *service = serviceValue(options);
|
||||||
|
NSDictionary *attributes = attributes = @{
|
||||||
[self deletePasswordsForService:service];
|
|
||||||
|
|
||||||
// Create dictionary of parameters to add
|
|
||||||
NSDictionary *attributes = @{
|
|
||||||
(__bridge NSString *)kSecClass: (__bridge id)(kSecClassGenericPassword),
|
(__bridge NSString *)kSecClass: (__bridge id)(kSecClassGenericPassword),
|
||||||
(__bridge NSString *)kSecAttrService: service,
|
(__bridge NSString *)kSecAttrService: service,
|
||||||
(__bridge NSString *)kSecAttrAccount: username,
|
(__bridge NSString *)kSecAttrAccount: username,
|
||||||
(__bridge NSString *)kSecValueData: [password dataUsingEncoding:NSUTF8StringEncoding],
|
(__bridge NSString *)kSecValueData: [password dataUsingEncoding:NSUTF8StringEncoding]
|
||||||
(__bridge NSString *)kSecAttrAccessible: (__bridge id)accessibleValue(options)
|
|
||||||
};
|
};
|
||||||
|
|
||||||
[self insertKeychainEntry:attributes withAccessGroup:accessGroupValue(options) resolver:resolve rejecter:reject];
|
[self deletePasswordsForService:service];
|
||||||
|
|
||||||
|
[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:(NSDictionary *)options resolver:(RCTPromiseResolveBlock)resolve rejecter:(RCTPromiseRejectBlock)reject)
|
||||||
{
|
{
|
||||||
NSString *service = serviceValue(options);
|
NSString *service = serviceValue(options);
|
||||||
|
NSString *authenticationPrompt = @"Authenticate to retrieve secret";
|
||||||
// secure compatibility with TouchId / Passcode secured stored items
|
if (options && options[kAuthenticationPromptMessage]) {
|
||||||
// http://stackoverflow.com/questions/42339000/ksecuseauthenticationuiskip-how-to-use-it
|
authenticationPrompt = options[kAuthenticationPromptMessage];
|
||||||
// Silently skip any items that require user authentication. Only use this value with the SecItemCopyMatching function.
|
}
|
||||||
|
|
||||||
NSDictionary *query = @{
|
NSDictionary *query = @{
|
||||||
(__bridge NSString *)kSecClass: (__bridge id)(kSecClassGenericPassword),
|
(__bridge NSString *)kSecClass: (__bridge id)(kSecClassGenericPassword),
|
||||||
|
@ -391,7 +314,7 @@ RCT_EXPORT_METHOD(getGenericPasswordForOptions:(NSDictionary *)options resolver:
|
||||||
(__bridge NSString *)kSecReturnAttributes: (__bridge id)kCFBooleanTrue,
|
(__bridge NSString *)kSecReturnAttributes: (__bridge id)kCFBooleanTrue,
|
||||||
(__bridge NSString *)kSecReturnData: (__bridge id)kCFBooleanTrue,
|
(__bridge NSString *)kSecReturnData: (__bridge id)kCFBooleanTrue,
|
||||||
(__bridge NSString *)kSecMatchLimit: (__bridge NSString *)kSecMatchLimitOne,
|
(__bridge NSString *)kSecMatchLimit: (__bridge NSString *)kSecMatchLimitOne,
|
||||||
(__bridge NSString *)kSecUseAuthenticationUI: (__bridge id)kSecUseAuthenticationUISkip
|
(__bridge NSString *)kSecUseOperationPrompt: authenticationPrompt
|
||||||
};
|
};
|
||||||
|
|
||||||
// Look up service in the keychain
|
// Look up service in the keychain
|
||||||
|
@ -443,26 +366,20 @@ RCT_EXPORT_METHOD(setInternetCredentialsForServer:(NSString *)server withUsernam
|
||||||
(__bridge NSString *)kSecClass: (__bridge id)(kSecClassInternetPassword),
|
(__bridge NSString *)kSecClass: (__bridge id)(kSecClassInternetPassword),
|
||||||
(__bridge NSString *)kSecAttrServer: server,
|
(__bridge NSString *)kSecAttrServer: server,
|
||||||
(__bridge NSString *)kSecAttrAccount: username,
|
(__bridge NSString *)kSecAttrAccount: username,
|
||||||
(__bridge NSString *)kSecValueData: [password dataUsingEncoding:NSUTF8StringEncoding],
|
(__bridge NSString *)kSecValueData: [password dataUsingEncoding:NSUTF8StringEncoding]
|
||||||
(__bridge NSString *)kSecAttrAccessible: (__bridge id)accessibleValue(options)
|
|
||||||
};
|
};
|
||||||
|
|
||||||
[self insertKeychainEntry:attributes withAccessGroup:accessGroupValue(options) resolver:resolve rejecter:reject];
|
[self insertKeychainEntry:attributes withOptions:options resolver:resolve rejecter:reject];
|
||||||
}
|
}
|
||||||
|
|
||||||
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)
|
||||||
{
|
{
|
||||||
// secure compatibility with TouchId / Passcode secured stored items
|
|
||||||
// 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.
|
|
||||||
|
|
||||||
NSDictionary *query = @{
|
NSDictionary *query = @{
|
||||||
(__bridge NSString *)kSecClass: (__bridge id)(kSecClassInternetPassword),
|
(__bridge NSString *)kSecClass: (__bridge id)(kSecClassInternetPassword),
|
||||||
(__bridge NSString *)kSecAttrServer: server,
|
(__bridge NSString *)kSecAttrServer: server,
|
||||||
(__bridge NSString *)kSecReturnAttributes: (__bridge id)kCFBooleanTrue,
|
(__bridge NSString *)kSecReturnAttributes: (__bridge id)kCFBooleanTrue,
|
||||||
(__bridge NSString *)kSecReturnData: (__bridge id)kCFBooleanTrue,
|
(__bridge NSString *)kSecReturnData: (__bridge id)kCFBooleanTrue,
|
||||||
(__bridge NSString *)kSecMatchLimit: (__bridge NSString *)kSecMatchLimitOne,
|
(__bridge NSString *)kSecMatchLimit: (__bridge NSString *)kSecMatchLimitOne
|
||||||
(__bridge NSString *)kSecUseAuthenticationUI: (__bridge id)kSecUseAuthenticationUISkip
|
|
||||||
};
|
};
|
||||||
|
|
||||||
// Look up server in the keychain
|
// Look up server in the keychain
|
||||||
|
@ -502,7 +419,6 @@ RCT_EXPORT_METHOD(resetInternetCredentialsForServer:(NSString *)server withOptio
|
||||||
}
|
}
|
||||||
|
|
||||||
return resolve(@(YES));
|
return resolve(@(YES));
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#if TARGET_OS_IOS
|
#if TARGET_OS_IOS
|
||||||
|
|
63
index.js
63
index.js
|
@ -41,12 +41,6 @@ type SecAccessible =
|
||||||
| 'AccessibleAfterFirstUnlockThisDeviceOnly'
|
| 'AccessibleAfterFirstUnlockThisDeviceOnly'
|
||||||
| 'AccessibleAlwaysThisDeviceOnly';
|
| 'AccessibleAlwaysThisDeviceOnly';
|
||||||
|
|
||||||
type Options = {
|
|
||||||
accessGroup?: string,
|
|
||||||
accessible?: SecAccessible,
|
|
||||||
service?: string,
|
|
||||||
};
|
|
||||||
|
|
||||||
type SecAccessControl =
|
type SecAccessControl =
|
||||||
| 'UserPresence'
|
| 'UserPresence'
|
||||||
| 'BiometryAny'
|
| 'BiometryAny'
|
||||||
|
@ -58,9 +52,10 @@ type SecAccessControl =
|
||||||
|
|
||||||
type LAPolicy = 'Authentication' | 'AuthenticationWithBiometrics';
|
type LAPolicy = 'Authentication' | 'AuthenticationWithBiometrics';
|
||||||
|
|
||||||
type SecureOptions = {
|
type Options = {
|
||||||
accessControl?: SecAccessControl,
|
accessControl?: SecAccessControl,
|
||||||
accessGroup?: string,
|
accessGroup?: string,
|
||||||
|
accessible?: SecAccessible,
|
||||||
authenticationPrompt?: string,
|
authenticationPrompt?: string,
|
||||||
authenticationType?: LAPolicy,
|
authenticationType?: LAPolicy,
|
||||||
service?: string,
|
service?: string,
|
||||||
|
@ -72,7 +67,7 @@ type SecureOptions = {
|
||||||
* @param {object} options LAPolicy option, iOS only
|
* @param {object} options LAPolicy option, iOS only
|
||||||
* @return {Promise} Resolves to `true` when supported, otherwise `false`
|
* @return {Promise} Resolves to `true` when supported, otherwise `false`
|
||||||
*/
|
*/
|
||||||
export function canImplyAuthentication(options?: SecureOptions): Promise {
|
export function canImplyAuthentication(options?: Options): Promise {
|
||||||
if (RNKeychainManager.canCheckAuthentication) {
|
if (RNKeychainManager.canCheckAuthentication) {
|
||||||
return Promise.reject(
|
return Promise.reject(
|
||||||
new Error(`canImplyAuthentication() is not supported on this platform`)
|
new Error(`canImplyAuthentication() is not supported on this platform`)
|
||||||
|
@ -93,50 +88,6 @@ export function getSupportedBiometryType(): Promise {
|
||||||
return RNKeychainManager.getSupportedBiometryType();
|
return RNKeychainManager.getSupportedBiometryType();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Saves the `username` and `password` combination securely - needs authentication to retrieve it.
|
|
||||||
* @param {string} username Associated username or e-mail to be saved.
|
|
||||||
* @param {string} password Associated password to be saved.
|
|
||||||
* @param {object} options Keychain options, iOS only
|
|
||||||
* @return {Promise} Resolves to `true` when successful
|
|
||||||
*/
|
|
||||||
export function setPasswordWithAuthentication(
|
|
||||||
username: string,
|
|
||||||
password: string,
|
|
||||||
options?: SecureOptions
|
|
||||||
): Promise {
|
|
||||||
if (Platform.OS !== 'ios') {
|
|
||||||
return Promise.reject(
|
|
||||||
new Error(
|
|
||||||
`setPasswordWithAuthentication() is not supported on ${Platform.OS} yet`
|
|
||||||
)
|
|
||||||
);
|
|
||||||
}
|
|
||||||
return RNKeychainManager.setPasswordWithAuthentication(
|
|
||||||
options,
|
|
||||||
username,
|
|
||||||
password
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Fetches login combination for `service` - demands for authentication if necessary.
|
|
||||||
* @param {string|object} serviceOrOptions Reverse domain name qualifier for the service, defaults to `bundleId` or an options object.
|
|
||||||
* @return {Promise} Resolves to `{ service, username, password }` when successful
|
|
||||||
*/
|
|
||||||
export function getPasswordWithAuthentication(
|
|
||||||
options?: SecureOptions
|
|
||||||
): Promise {
|
|
||||||
if (Platform.OS !== 'ios') {
|
|
||||||
return Promise.reject(
|
|
||||||
new Error(
|
|
||||||
`getPasswordWithAuthentication() is not supported on ${Platform.OS} yet`
|
|
||||||
)
|
|
||||||
);
|
|
||||||
}
|
|
||||||
return RNKeychainManager.getPasswordWithAuthentication(options);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Saves the `username` and `password` combination for `server`.
|
* Saves the `username` and `password` combination for `server`.
|
||||||
* @param {string} server URL to server.
|
* @param {string} server URL to server.
|
||||||
|
@ -185,7 +136,7 @@ export function resetInternetCredentials(
|
||||||
return RNKeychainManager.resetInternetCredentialsForServer(server, options);
|
return RNKeychainManager.resetInternetCredentialsForServer(server, options);
|
||||||
}
|
}
|
||||||
|
|
||||||
function getOptionsArgument(serviceOrOptions?: string | KeychainOptions) {
|
function getOptionsArgument(serviceOrOptions?: string | Options) {
|
||||||
if (Platform.OS !== 'ios') {
|
if (Platform.OS !== 'ios') {
|
||||||
return typeof serviceOrOptions === 'object'
|
return typeof serviceOrOptions === 'object'
|
||||||
? serviceOrOptions.service
|
? serviceOrOptions.service
|
||||||
|
@ -206,7 +157,7 @@ function getOptionsArgument(serviceOrOptions?: string | KeychainOptions) {
|
||||||
export function setGenericPassword(
|
export function setGenericPassword(
|
||||||
username: string,
|
username: string,
|
||||||
password: string,
|
password: string,
|
||||||
serviceOrOptions?: string | KeychainOptions
|
serviceOrOptions?: string | Options
|
||||||
): Promise {
|
): Promise {
|
||||||
return RNKeychainManager.setGenericPasswordForOptions(
|
return RNKeychainManager.setGenericPasswordForOptions(
|
||||||
getOptionsArgument(serviceOrOptions),
|
getOptionsArgument(serviceOrOptions),
|
||||||
|
@ -221,7 +172,7 @@ export function setGenericPassword(
|
||||||
* @return {Promise} Resolves to `{ service, username, password }` when successful
|
* @return {Promise} Resolves to `{ service, username, password }` when successful
|
||||||
*/
|
*/
|
||||||
export function getGenericPassword(
|
export function getGenericPassword(
|
||||||
serviceOrOptions?: string | KeychainOptions
|
serviceOrOptions?: string | Options
|
||||||
): Promise {
|
): Promise {
|
||||||
return RNKeychainManager.getGenericPasswordForOptions(
|
return RNKeychainManager.getGenericPasswordForOptions(
|
||||||
getOptionsArgument(serviceOrOptions)
|
getOptionsArgument(serviceOrOptions)
|
||||||
|
@ -234,7 +185,7 @@ export function getGenericPassword(
|
||||||
* @return {Promise} Resolves to `true` when successful
|
* @return {Promise} Resolves to `true` when successful
|
||||||
*/
|
*/
|
||||||
export function resetGenericPassword(
|
export function resetGenericPassword(
|
||||||
serviceOrOptions?: string | KeychainOptions
|
serviceOrOptions?: string | Options
|
||||||
): Promise {
|
): Promise {
|
||||||
return RNKeychainManager.resetGenericPasswordForOptions(
|
return RNKeychainManager.resetGenericPasswordForOptions(
|
||||||
getOptionsArgument(serviceOrOptions)
|
getOptionsArgument(serviceOrOptions)
|
||||||
|
|
|
@ -11,7 +11,7 @@ declare module 'react-native-keychain' {
|
||||||
password: string;
|
password: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface SecureOptions {
|
export interface Options {
|
||||||
accessControl?: string;
|
accessControl?: string;
|
||||||
accessGroup?: string;
|
accessGroup?: string;
|
||||||
authenticationPrompt?: string;
|
authenticationPrompt?: string;
|
||||||
|
@ -20,26 +20,17 @@ declare module 'react-native-keychain' {
|
||||||
}
|
}
|
||||||
|
|
||||||
function canImplyAuthentication(
|
function canImplyAuthentication(
|
||||||
options?: SecureOptions
|
options?: Options
|
||||||
): Promise<boolean>;
|
): Promise<boolean>;
|
||||||
|
|
||||||
function getSupportedBiometryType(
|
function getSupportedBiometryType(
|
||||||
): Promise<string>;
|
): Promise<string>;
|
||||||
|
|
||||||
function setSecurePassword(
|
|
||||||
username: string,
|
|
||||||
password: string,
|
|
||||||
options?: SecureOptions
|
|
||||||
): Promise<boolean>;
|
|
||||||
|
|
||||||
function getSecurePassword(
|
|
||||||
options?: SecureOptions
|
|
||||||
): Promise<boolean | {service: string, username: string, password: string}>;
|
|
||||||
|
|
||||||
function setInternetCredentials(
|
function setInternetCredentials(
|
||||||
server: string,
|
server: string,
|
||||||
username: string,
|
username: string,
|
||||||
password: string
|
password: string,
|
||||||
|
options?: Options
|
||||||
): Promise<void>;
|
): Promise<void>;
|
||||||
|
|
||||||
function getInternetCredentials(
|
function getInternetCredentials(
|
||||||
|
@ -53,15 +44,15 @@ declare module 'react-native-keychain' {
|
||||||
function setGenericPassword(
|
function setGenericPassword(
|
||||||
username: string,
|
username: string,
|
||||||
password: string,
|
password: string,
|
||||||
service?: string
|
options?: Options
|
||||||
): Promise<boolean>;
|
): Promise<boolean>;
|
||||||
|
|
||||||
function getGenericPassword(
|
function getGenericPassword(
|
||||||
service?: string
|
options?: Options
|
||||||
): Promise<boolean | {service: string, username: string, password: string}>;
|
): Promise<boolean | {service: string, username: string, password: string}>;
|
||||||
|
|
||||||
function resetGenericPassword(
|
function resetGenericPassword(
|
||||||
service?: string
|
options?: Options
|
||||||
): Promise<boolean>
|
): Promise<boolean>
|
||||||
|
|
||||||
function requestSharedWebCredentials (
|
function requestSharedWebCredentials (
|
||||||
|
|
Loading…
Reference in New Issue