Updated February 27, 2019
Setting up an Authentication Flow in React Native
Hey - React Navigation has a new API! To read an updated article please check out the new tutorial.
Whether or not you use React Navigation you should be able to apply this framework to your authentication needs.
Ensuring proper authentication is critical and when things are critical I like to keep them as simple as possible.
When a user opens the app, here's what happens:
- They land on an entry point file. This file will check if they're a authorized user.
- If they're an authenticated user, I switch to the app state
- If they're not an authenticated user, I switch to the auth state
That means I've got three app states:
- User status unknown
- User status known, authenticated
- User status known, not authenticated
Question: How do you handle if a user becomes logged out while using the app? What if they're authenticated but they're not authorized to do something (like access a pro feature)?
Here's the final code from the video:
App.js
import React from 'react';
import {
StyleSheet,
Text,
View,
TouchableOpacity,
Button,
AsyncStorage,
} from 'react-native';
import {
createAppContainer,
createStackNavigator,
createBottomTabNavigator,
createSwitchNavigator,
} from 'react-navigation';
const setUserId = (userId) => {
if (userId) {
return AsyncStorage.setItem('userId', userId);
}
return AsyncStorage.removeItem('userId');
};
const getUserId = () => AsyncStorage.getItem('userId');
/*
* ####################
* START APP
* ####################
*/
class Home extends React.Component {
constructor(props) {
super(props);
this.state = {
backgroundColor: this.generateColor(),
};
}
generateColor = () => {
let backgroundColor = Math.floor(Math.random() * 16777215).toString(16);
return '#' + ('000000' + backgroundColor).slice(-6);
};
render() {
return (
<View
style={{
flex: 1,
alignItems: 'center',
justifyContent: 'center',
backgroundColor: this.state.backgroundColor,
}}
>
<Text
style={{
color: '#fff',
fontWeight: 'bold',
fontSize: 25,
}}
>
{this.state.backgroundColor}
</Text>
<TouchableOpacity
onPress={() => {
this.setState({ backgroundColor: this.generateColor() });
}}
style={{
backgroundColor: '#fff',
marginTop: 20,
paddingVertical: 10,
paddingHorizontal: 20,
}}
>
<Text>New Color!</Text>
</TouchableOpacity>
</View>
);
}
}
const Profile = ({ navigation }) => (
<View style={{ flex: 1, alignItems: 'center', justifyContent: 'center' }}>
<Button
title="Sign Out"
onPress={async () => {
await setUserId(null);
navigation.navigate('Auth');
}}
/>
</View>
);
/*
* ####################
* START AUTH
* ####################
*/
const SignIn = ({ navigation }) => (
<View style={{ flex: 1, alignItems: 'center', justifyContent: 'center' }}>
<Button
title="Sign In"
onPress={async () => {
// Wait on the server side sign in process...
await setUserId('123');
navigation.navigate('App');
}}
/>
<Button title="Sign Up" onPress={() => navigation.navigate('SignUp')} />
</View>
);
const SignUp = ({ navigation }) => (
<View style={{ flex: 1, alignItems: 'center', justifyContent: 'center' }}>
<Button
title="Sign Up"
onPress={async () => {
// Wait on the server side sign up process...
await setUserId('123');
navigation.navigate('App');
}}
/>
</View>
);
/*
* ####################
* START NAVIGATION
* ####################
*/
const App = createStackNavigator(
{
Tabs: createBottomTabNavigator({
Home: {
screen: Home,
},
Profile: {
screen: Profile,
},
}),
},
{
mode: 'modal',
headerMode: 'none',
}
);
const Auth = createStackNavigator({
SignIn: {
screen: SignIn,
navigationOptions: {
headerTitle: 'Sign In',
},
},
SignUp: {
screen: SignUp,
navigationOptions: {
headerTitle: 'Sign Up',
},
},
});
class Entry extends React.Component {
async componentDidMount() {
const userId = await getUserId();
if (userId) {
this.props.navigation.navigate('App', {});
} else {
this.props.navigation.navigate('Auth');
}
}
render() {
return (
<View style={{ flex: 1, alignItems: 'center', justifyContent: 'center' }}>
<Text>Loading...</Text>
</View>
);
}
}
const RootNavigation = createSwitchNavigator({
Entry: {
screen: Entry,
},
App: {
screen: App,
},
Auth: {
screen: Auth,
},
});
export default createAppContainer(RootNavigation);