Basic Authentication is when raw(or with basic encoding) username and password is sent to the server, typically in its body
Sending credential pair in 'Authorization' header of a request can be also considered as basic authentication request:
fetch('http://192.168.43.34:4500/users/getInfo', {
headers: {
'Authorization': 'Basic ' + base64.encode(username + ":" + password);
}
});
It consists of a prefix Basic
(or some other word) and base64 encoding of username and password separated by colon (:
)
Provided application requires a backend server which performs the authentication
Check Nodejs Basic 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 NcBasicAuthApp)
react-native init NcBasicAuthApp
This creates a directory named NcBasicAuthApp 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 NcBasicAuthApp)
cd NcBasicAuthApp
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
Example provided here implements two similar kinds of basic authentication
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
If a response with status code 200 is received then the associated content of response is sent to ViewUser
page after adding password which was stored, since server is typically not expected to send password back after login
The username and password is then used for further communication with server
ViewUser page
To ger user info, authorization string is created with username and password
username and password is retrieved from state
attribute of navigation object, which is passed as props to ViewUser component
const {username, password} = props.navigation.state.params.userData;
The string for basic authentication is stored by encoding username:password
and appending it to the prefix 'Basic '
this.auth = 'Basic ' + base64.encode(username + ":" + password);
This is kind of convention, and can be customised by a programmer while corresponding to how it is parsed in server side
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;
data.password = this.state.password;
navigate('User', {
userData: data
})
};
login() {
const {username, password} = this.state;
const encoded = 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:4500/users/authenticate', {
method: 'POST',
headers: {
'Accept': 'application/json',
'Content-Type': 'application/json'
},
body: JSON.stringify({username: username, password: encoded})
})
.then(response => {
if (!response.ok) throw response;
return response.json();
})
.then(responseJson => {
this.navigateToPage(responseJson);
})
.catch(error => {
if(error instanceof Error) {
console.error(error);
}
else {
error.json().then((body) => {
if(body.message) {
alert(body.message);
}
console.log(body);
});
}
});
}
};
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 App = createStackNavigator({
HomeScreen: {
screen: LoginPage,
navigationOptions: {
title: 'Basic 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';
import base64 from 'react-native-base64'
export default class ViewUser extends React.Component {
constructor(props) {
super(props);
const {username, password} = props.navigation.state.params.userData;
this.auth = 'Basic ' + base64.encode(username + ":" + password);
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:4500/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 NcBasicAuthApp)
cd NcBasicAuthApp
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 NcBasicAuthApp)
cd NcBasicAuthApp
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