A Json Web Token or JWT is a token or string which is typically associated with one or more attribute of user data which can uniquely identify a user
It is typically sent from a server to a client(such as mobile app) in the response of a user authentication request
The client then uses this token for subsequent requests to the server, restricting user credentials to authentication apis
A jwt can be associated with an expiry period and additional session data, which is implemented on the server side
Provided application requires a server which performs the authentication and returns a Jwt
Check Nodejs Jwt Authentication Server to implement such a server in Nodejs
Check React Native Installation for installation related info
Use react-native init
command to create a new React Native project (here named NcJwtAuthApp)
react-native init NcJwtAuthApp
This creates a directory named NcJwtAuthApp and initializes it as a react native application directory
This can be skipped in case an existing react native project is being integrated into
Since this example uses NavigationDrawer component, related packages have to be added
Go to project directory (here NcJwtAuthApp)
cd NcJwtAuthApp
Add react-navigation
react-navigation-drawer
&& react-navigation-stack
and associated dependencies to current project using yarn
yarn add react-navigation react-navigation-drawer react-navigation-stack \
react-native-gesture-handler react-native-reanimated react-native-safe-area-context react-native-screens
or using npm
npm install react-navigation react-navigation-drawer react-navigation-stack \
react-native-gesture-handler react-native-reanimated react-native-safe-area-context react-native-screens --save
After yarn add
command, cd into ios folder inside project directory
cd ios
Use pod command to install any required CocoaPods dependencies:
pod install
Login Page
LoginPage component renders two textinputs to get username and password from user and a button which initiates a request to the server
username and password is send as body of request, where password is encoded prior to being sent
fetch('http://192.168.43.34:4500/users/authenticate', {
method: 'POST',
headers: {
'Accept': 'application/json',
'Content-Type': 'application/json'
},
body: JSON.stringify({username: username, password: base64.encode(password)})
})
With basic base64 encoding the password can be easily decoded if request is sniffed by an attacker
Something like AES encryption is recommended when sending user credentials, to enhance security
Response from server, which contains the jwt, is sent to ViewUser
page, to be used for subsequent requests to server
Check for presence of token in server response is omitted here
ViewUser page
To ger user info, authorization string is created with username and password
jwt is retrieved from state
attribute of navigation object, which is passed as props to ViewUser component
this.auth = 'Bearer ' + props.navigation.state.params.userData.token;
userData is the server response sent from Login page
Prefix Bearer
is according to server implementation, which can be omitted and/or additional encryption can be implemented on the token
Resultant authentication string is then passed as a header for requests to the server
fetch('http://192.168.43.34:4500/users/getInfo', {
method: 'GET',
headers: {
'Authorization': this.auth
}
});
Authorization
key used in header can also be changed to some other name, while the server is configured to parse that key
User info is retrieved as specific fields from the response received from server and used to set state which gets rendered
if(responseJson) {
console.log("viewuser response " + JSON.stringify(responseJson));
this.setState({
user_contact: responseJson.userdata.cell,
user_fn: responseJson.userdata.name.first + ' ' + responseJson.userdata.name.last,
user_city: responseJson.userdata.location.city,
user_country: responseJson.userdata.location.country,
user_image: responseJson.userdata.picture.medium
});
}
Following code of App.js
defines
import React from 'react';
import {
StyleSheet,
View,
Text,
StatusBar,
KeyboardAvoidingView,
TextInput,
Button, ScrollView
} from 'react-native';
import { createAppContainer } from 'react-navigation';
import { createStackNavigator} from 'react-navigation-stack';
import base64 from 'react-native-base64'
import ViewUser from './ViewUser';
class LoginPage extends React.Component {
constructor() {
super();
this.state = {
username:'',
password:''
}
}
navigateToPage(data) {
const { navigate } = this.props.navigation;
navigate('User', {
userData: data
})
};
login() {
const {username, password} = this.state;
const encrypted = base64.encode(password);
if(username.length < 1) {
alert('Enter username');
}
else if (password.length < 1) {
alert('enter password');
}
else {
fetch('http://192.168.43.34:4000/users/authenticate', {
method: 'POST',
headers: {
'Accept': 'application/json',
'Content-Type': 'application/json'
},
body: JSON.stringify({username: username, password: encrypted})
})
.then(response => response.json())
.then(responseJson => {
this.navigateToPage(responseJson);
})
.catch(error => {
console.error(error);
});
}
};
render() {
return (
<View style={{ backgroundColor: 'white', flex: 1 }}>
<ScrollView keyboardShouldPersistTaps="handled">
<KeyboardAvoidingView
behavior="padding"
style={{ flex: 1, justifyContent: 'space-between' }}>
<TextInput
placeholder="Enter username"
onChangeText={username => this.setState({ username })}
style={{ padding:10 }}
/>
<TextInput
placeholder="Enter password"
onChangeText={password => this.setState({ password })}
style={{ padding:10 }}
secureTextEntry
/>
<Button
title="Submit"
onPress={this.login.bind(this)}
/>
</KeyboardAvoidingView>
</ScrollView>
</View>
);
};
};
const styles = StyleSheet.create({
engine: {
position: 'absolute',
right: 0,
},
sectionContainer: {
marginTop: 32,
paddingHorizontal: 24,
},
});
const App = createStackNavigator({
HomeScreen: {
screen: LoginPage,
navigationOptions: {
title: 'Jwt Auth - Login Page',
},
},
User: {
screen: ViewUser,
navigationOptions: {
title: 'User Page',
},
}
});
export default createAppContainer(App);
ViewUser
class displays some info about a user
import React from 'react';
import { Text, View, Button, TextInput, Image } from 'react-native';
export default class ViewUser extends React.Component {
constructor(props) {
super(props);
this.auth = 'Bearer ' + props.navigation.state.params.userData.token;
this.state = {
user_id: props.navigation.state.params.userData.id,
user_contact: '',
user_city: '',
user_country: '',
user_image: '',
user_fn: '',
};
};
componentDidMount() {
this.getUserData();
};
async getUserData() {
var responseJson = null;
try {
responseJson = await( await fetch('http://192.168.43.34:4000/users/getInfo', {
method: 'GET',
headers: {
'Authorization': this.auth
}
})).json();
}
catch(err) {
console.log("error", err);
}
if(responseJson) {
console.log("viewuser response " + JSON.stringify(responseJson));
this.setState({
user_contact: responseJson.userdata.cell,
user_fn: responseJson.userdata.name.first + ' ' + responseJson.userdata.name.last,
user_city: responseJson.userdata.location.city,
user_country: responseJson.userdata.location.country,
user_image: responseJson.userdata.picture.medium
});
}
};
render() {
const { user_id, user_contact, user_city, user_country, user_image, user_fn } = this.state;
return (
<View style={{ flexDirection:'row', backgroundColor:'#628367'}}>
<View style={{ marginLeft: 35, marginRight: 35, margin: 10, flex: 2 }}>
<Text>Id: {user_id}</Text>
<Text>Name: {user_fn}</Text>
<Text>Phone: {user_contact}</Text>
<Text>City: {user_city}</Text>
<Text>Country: {user_country}</Text>
</View>
<View>
{ user_image.length == 0? null:
<Image source={{uri:user_image}} style={{ width:100, height:150, marginLeft: 35, marginRight: 35, margin: 10, flex:1, resizeMode:'contain' }}/> }
</View>
</View>
);
};
}
cd into project directory (here NcJwtAuthApp)
cd NcJwtAuthApp
Run metro server to serve js
react-native start
Go to project directory in another terminal tab
Enter following run command for Android:
react-native run-android
cd into project directory (here NcJwtAuthApp)
cd NcJwtAuthApp
Enter following command :
react-native run-ios
This might take some time
If the app shows error about metro server not configured (check Running an App in iOS), then run metro server in another tab:
react-native start
Reload the app
Re-run react-native run-ios
command if reloading doesn't work