2016-11-02 20:42:55 +00:00
|
|
|
import React, { Component } from 'react';
|
|
|
|
import {
|
|
|
|
KeyboardAvoidingView,
|
2018-02-25 15:16:22 +00:00
|
|
|
Platform,
|
2018-02-26 11:31:06 +00:00
|
|
|
SegmentedControlIOS,
|
2016-11-02 20:42:55 +00:00
|
|
|
StyleSheet,
|
|
|
|
Text,
|
|
|
|
TextInput,
|
|
|
|
TouchableHighlight,
|
|
|
|
View,
|
|
|
|
} from 'react-native';
|
|
|
|
|
2017-01-10 21:27:47 +00:00
|
|
|
import * as Keychain from 'react-native-keychain';
|
2016-11-02 20:42:55 +00:00
|
|
|
|
2018-02-26 11:31:06 +00:00
|
|
|
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]
|
|
|
|
|
2016-11-02 20:42:55 +00:00
|
|
|
export default class KeychainExample extends Component {
|
|
|
|
state = {
|
|
|
|
username: '',
|
|
|
|
password: '',
|
|
|
|
status: '',
|
2018-02-25 16:05:33 +00:00
|
|
|
biometryType: null,
|
2018-02-26 11:31:06 +00:00
|
|
|
accessControl: null,
|
2016-11-02 20:42:55 +00:00
|
|
|
};
|
|
|
|
|
2018-02-25 16:05:33 +00:00
|
|
|
componentDidMount() {
|
|
|
|
Keychain.getSupportedBiometryType().then(biometryType => {
|
|
|
|
this.setState({ biometryType });
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
2018-02-26 11:31:06 +00:00
|
|
|
async save(accessControl) {
|
2018-02-25 18:24:35 +00:00
|
|
|
try {
|
2018-02-26 11:31:06 +00:00
|
|
|
await Keychain.setGenericPassword(
|
|
|
|
this.state.username,
|
|
|
|
this.state.password,
|
|
|
|
{ accessControl: this.state.accessControl }
|
|
|
|
);
|
2018-03-29 11:26:00 +00:00
|
|
|
this.setState({ username: '', password: '', status: 'Credentials saved!' });
|
2018-02-25 18:24:35 +00:00
|
|
|
} catch (err) {
|
|
|
|
this.setState({ status: 'Could not save credentials, ' + err });
|
|
|
|
}
|
2016-11-02 20:42:55 +00:00
|
|
|
}
|
|
|
|
|
2018-02-25 18:24:35 +00:00
|
|
|
async load() {
|
|
|
|
try {
|
2018-02-26 11:31:06 +00:00
|
|
|
const credentials = await Keychain.getGenericPassword();
|
2018-02-25 18:24:35 +00:00
|
|
|
if (credentials) {
|
|
|
|
this.setState({ ...credentials, status: 'Credentials loaded!' });
|
|
|
|
} else {
|
|
|
|
this.setState({ status: 'No credentials stored.' });
|
|
|
|
}
|
|
|
|
} catch (err) {
|
|
|
|
this.setState({ status: 'Could not load credentials. ' + err });
|
|
|
|
}
|
2016-11-02 20:42:55 +00:00
|
|
|
}
|
|
|
|
|
2018-02-25 18:24:35 +00:00
|
|
|
async reset() {
|
|
|
|
try {
|
|
|
|
await Keychain.resetGenericPassword();
|
|
|
|
this.setState({
|
|
|
|
status: 'Credentials Reset!',
|
|
|
|
username: '',
|
|
|
|
password: '',
|
2016-11-02 20:42:55 +00:00
|
|
|
});
|
2018-02-25 18:24:35 +00:00
|
|
|
} catch (err) {
|
|
|
|
this.setState({ status: 'Could not reset credentials, ' + err });
|
|
|
|
}
|
2016-11-02 20:42:55 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
render() {
|
|
|
|
return (
|
2018-02-25 15:16:22 +00:00
|
|
|
<KeyboardAvoidingView
|
|
|
|
behavior={Platform.OS === 'ios' ? 'padding' : undefined}
|
|
|
|
style={styles.container}
|
|
|
|
>
|
2016-11-02 20:42:55 +00:00
|
|
|
<View style={styles.content}>
|
2018-02-25 15:14:17 +00:00
|
|
|
<Text style={styles.title}>Keychain Example</Text>
|
2016-11-02 20:42:55 +00:00
|
|
|
<View style={styles.field}>
|
|
|
|
<Text style={styles.label}>Username</Text>
|
|
|
|
<TextInput
|
|
|
|
style={styles.input}
|
|
|
|
autoFocus={true}
|
|
|
|
autoCapitalize="none"
|
|
|
|
value={this.state.username}
|
2018-02-25 15:14:17 +00:00
|
|
|
onChange={event =>
|
|
|
|
this.setState({ username: event.nativeEvent.text })}
|
2018-02-25 15:16:22 +00:00
|
|
|
underlineColorAndroid="transparent"
|
2018-02-25 15:14:17 +00:00
|
|
|
/>
|
2016-11-02 20:42:55 +00:00
|
|
|
</View>
|
|
|
|
<View style={styles.field}>
|
|
|
|
<Text style={styles.label}>Password</Text>
|
|
|
|
<TextInput
|
|
|
|
style={styles.input}
|
|
|
|
password={true}
|
|
|
|
autoCapitalize="none"
|
|
|
|
value={this.state.password}
|
2018-02-25 15:14:17 +00:00
|
|
|
onChange={event =>
|
|
|
|
this.setState({ password: event.nativeEvent.text })}
|
2018-02-25 15:16:22 +00:00
|
|
|
underlineColorAndroid="transparent"
|
2018-02-25 15:14:17 +00:00
|
|
|
/>
|
2016-11-02 20:42:55 +00:00
|
|
|
</View>
|
2018-02-26 11:31:06 +00:00
|
|
|
{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>
|
|
|
|
)}
|
2018-02-25 15:14:17 +00:00
|
|
|
{!!this.state.status && (
|
|
|
|
<Text style={styles.status}>{this.state.status}</Text>
|
|
|
|
)}
|
2018-02-26 11:31:06 +00:00
|
|
|
|
2016-11-02 20:42:55 +00:00
|
|
|
<View style={styles.buttons}>
|
2018-02-25 15:14:17 +00:00
|
|
|
<TouchableHighlight
|
|
|
|
onPress={() => this.save()}
|
|
|
|
style={styles.button}
|
|
|
|
>
|
2016-11-02 20:42:55 +00:00
|
|
|
<View style={styles.save}>
|
|
|
|
<Text style={styles.buttonText}>Save</Text>
|
|
|
|
</View>
|
|
|
|
</TouchableHighlight>
|
2018-02-25 15:14:17 +00:00
|
|
|
<TouchableHighlight
|
|
|
|
onPress={() => this.load()}
|
|
|
|
style={styles.button}
|
|
|
|
>
|
2016-11-02 20:42:55 +00:00
|
|
|
<View style={styles.load}>
|
|
|
|
<Text style={styles.buttonText}>Load</Text>
|
|
|
|
</View>
|
|
|
|
</TouchableHighlight>
|
2018-02-25 15:14:17 +00:00
|
|
|
<TouchableHighlight
|
|
|
|
onPress={() => this.reset()}
|
|
|
|
style={styles.button}
|
|
|
|
>
|
2016-11-02 20:42:55 +00:00
|
|
|
<View style={styles.reset}>
|
|
|
|
<Text style={styles.buttonText}>Reset</Text>
|
|
|
|
</View>
|
|
|
|
</TouchableHighlight>
|
|
|
|
</View>
|
|
|
|
</View>
|
|
|
|
</KeyboardAvoidingView>
|
|
|
|
);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
const styles = StyleSheet.create({
|
|
|
|
container: {
|
|
|
|
flex: 1,
|
|
|
|
justifyContent: 'center',
|
2018-02-25 15:14:17 +00:00
|
|
|
backgroundColor: '#F5FCFF',
|
2016-11-02 20:42:55 +00:00
|
|
|
},
|
|
|
|
content: {
|
2018-02-26 11:31:06 +00:00
|
|
|
marginHorizontal: 20,
|
2016-11-02 20:42:55 +00:00
|
|
|
},
|
|
|
|
title: {
|
|
|
|
fontSize: 28,
|
|
|
|
fontWeight: '200',
|
|
|
|
textAlign: 'center',
|
|
|
|
marginBottom: 20,
|
|
|
|
},
|
|
|
|
field: {
|
|
|
|
marginVertical: 5,
|
|
|
|
},
|
|
|
|
label: {
|
|
|
|
fontWeight: '500',
|
|
|
|
fontSize: 15,
|
|
|
|
marginBottom: 5,
|
|
|
|
},
|
|
|
|
input: {
|
|
|
|
borderWidth: StyleSheet.hairlineWidth,
|
|
|
|
borderColor: '#ccc',
|
|
|
|
backgroundColor: 'white',
|
|
|
|
height: 32,
|
|
|
|
fontSize: 14,
|
|
|
|
padding: 8,
|
|
|
|
},
|
|
|
|
status: {
|
|
|
|
color: '#333',
|
|
|
|
fontSize: 12,
|
|
|
|
marginTop: 15,
|
|
|
|
},
|
2018-02-25 16:05:33 +00:00
|
|
|
biometryType: {
|
|
|
|
color: '#333',
|
|
|
|
fontSize: 12,
|
|
|
|
marginTop: 15,
|
|
|
|
},
|
2016-11-02 20:42:55 +00:00
|
|
|
buttons: {
|
|
|
|
flexDirection: 'row',
|
|
|
|
justifyContent: 'space-between',
|
|
|
|
marginTop: 20,
|
|
|
|
},
|
|
|
|
button: {
|
|
|
|
borderRadius: 3,
|
|
|
|
overflow: 'hidden',
|
|
|
|
},
|
|
|
|
save: {
|
|
|
|
backgroundColor: '#0c0',
|
|
|
|
},
|
|
|
|
load: {
|
|
|
|
backgroundColor: '#333',
|
|
|
|
},
|
|
|
|
reset: {
|
|
|
|
backgroundColor: '#c00',
|
|
|
|
},
|
|
|
|
buttonText: {
|
|
|
|
color: 'white',
|
|
|
|
fontSize: 14,
|
|
|
|
paddingHorizontal: 16,
|
|
|
|
paddingVertical: 8,
|
2018-02-25 15:14:17 +00:00
|
|
|
},
|
2016-11-02 20:42:55 +00:00
|
|
|
});
|