2016-11-02 20:42:55 +00:00
|
|
|
import React, { Component } from 'react';
|
|
|
|
import {
|
|
|
|
KeyboardAvoidingView,
|
2018-02-25 15:16:22 +00:00
|
|
|
Platform,
|
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
|
|
|
|
|
|
|
export default class KeychainExample extends Component {
|
|
|
|
state = {
|
|
|
|
username: '',
|
|
|
|
password: '',
|
|
|
|
status: '',
|
2018-02-25 16:05:33 +00:00
|
|
|
biometryType: 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-25 18:24:35 +00:00
|
|
|
async save() {
|
|
|
|
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(
|
|
|
|
this.state.username,
|
|
|
|
this.state.password
|
|
|
|
);
|
|
|
|
}
|
|
|
|
this.setState({ status: 'Credentials saved!' });
|
|
|
|
} 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 {
|
|
|
|
const credentials = await (this.state.biometryType
|
|
|
|
? Keychain.getPasswordWithAuthentication({
|
|
|
|
accessControl:
|
|
|
|
Keychain.ACCESS_CONTROL.TOUCH_ID_ANY_OR_DEVICE_PASSCODE,
|
|
|
|
authenticationType: Keychain.AUTHENTICATION_TYPE.BIOMETRICS,
|
|
|
|
})
|
|
|
|
: Keychain.getGenericPassword());
|
|
|
|
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-25 15:14:17 +00:00
|
|
|
{!!this.state.status && (
|
|
|
|
<Text style={styles.status}>{this.state.status}</Text>
|
|
|
|
)}
|
2018-02-25 16:05:33 +00:00
|
|
|
{!!this.state.biometryType && (
|
|
|
|
<Text style={styles.biometryType}>
|
|
|
|
Supported biometry: {this.state.biometryType}
|
|
|
|
</Text>
|
|
|
|
)}
|
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,
|
|
|
|
alignItems: 'center',
|
|
|
|
justifyContent: 'center',
|
2018-02-25 15:14:17 +00:00
|
|
|
backgroundColor: '#F5FCFF',
|
2016-11-02 20:42:55 +00:00
|
|
|
},
|
|
|
|
content: {
|
|
|
|
width: 250,
|
|
|
|
},
|
|
|
|
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
|
|
|
});
|