xxxxxxxxxx
// src/components/Login.js
import {
View,
Text,
StyleSheet,
SafeAreaView,
TextInput,
Button,
Alert,
} from 'react-native';
import React, {useContext, useState} from 'react';
import {AuthContext} from '../context/AuthContext';
import * as Keychain from 'react-native-keychain';
import {AxiosContext} from '../context/AxiosContext';
const Login = () => {
const [email, setEmail] = useState('');
const [password, setPassword] = useState('');
const authContext = useContext(AuthContext);
const {publicAxios} = useContext(AxiosContext);
const onLogin = async () => {
try {
const response = await publicAxios.post('/login', {
email,
password,
});
const {accessToken, refreshToken} = response.data;
authContext.setAuthState({
accessToken,
refreshToken,
authenticated: true,
});
await Keychain.setGenericPassword(
'token',
JSON.stringify({
accessToken,
refreshToken,
}),
);
} catch (error) {
Alert.alert('Login Failed', error.response.data.message);
}
};
return (
<SafeAreaView style={styles.container}>
<Text style={styles.logo}>Cats</Text>
<View style={styles.form}>
<TextInput
style={styles.input}
placeholder="Email"
placeholderTextColor="#fefefe"
keyboardType="email-address"
autoCapitalize="none"
onChangeText={text => setEmail(text)}
value={email}
/>
<TextInput
style={styles.input}
placeholder="Password"
placeholderTextColor="#fefefe"
secureTextEntry
onChangeText={text => setPassword(text)}
value={password}
/>
</View>
<Button title="Login" style={styles.button} onPress={() => onLogin()} />
</SafeAreaView>
);
};
const styles = StyleSheet.create({
container: {
flex: 1,
backgroundColor: '#000',
alignItems: 'center',
justifyContent: 'flex-start',
width: '100%',
},
logo: {
fontSize: 60,
color: '#fff',
margin: '20%',
},
form: {
width: '80%',
margin: '10%',
},
input: {
fontSize: 20,
color: '#fff',
paddingBottom: 10,
borderBottomColor: '#fff',
borderBottomWidth: 1,
marginVertical: 20,
},
button: {},
});
export default Login;
xxxxxxxxxx
//AuthContext.js
import React, {createContext, useState} from 'react';
import * as Keychain from 'react-native-keychain';
const AuthContext = createContext(null);
const {Provider} = AuthContext;
const AuthProvider = ({children}) => {
const [authState, setAuthState] = useState({
accessToken: null,
refreshToken: null,
authenticated: null,
});
const logout = async () => {
await Keychain.resetGenericPassword();
setAuthState({
accessToken: null,
refreshToken: null,
authenticated: false,
});
};
const getAccessToken = () => {
return authState.accessToken;
};
return (
<Provider
value={{
authState,
getAccessToken,
setAuthState,
logout,
}}>
{children}
</Provider>
);
};
export {AuthContext, AuthProvider};
xxxxxxxxxx
import React, {createContext, useContext} from 'react';
import axios from 'axios';
import {AuthContext} from './AuthContext';
import createAuthRefreshInterceptor from 'axios-auth-refresh';
import * as Keychain from 'react-native-keychain';
const AxiosContext = createContext();
const {Provider} = AxiosContext;
const AxiosProvider = ({children}) => {
const authContext = useContext(AuthContext);
const authAxios = axios.create({
baseURL: 'http://localhost:3000/api',
});
const publicAxios = axios.create({
baseURL: 'http://localhost:3000/api',
});
authAxios.interceptors.request.use(
config => {
if (!config.headers.Authorization) {
config.headers.Authorization = `Bearer ${authContext.getAccessToken()}`;
}
return config;
},
error => {
return Promise.reject(error);
},
);
const refreshAuthLogic = failedRequest => {
const data = {
refreshToken: authContext.authState.refreshToken,
};
const options = {
method: 'POST',
data,
url: 'http://localhost:3001/api/refreshToken',
};
return axios(options)
.then(async tokenRefreshResponse => {
failedRequest.response.config.headers.Authorization =
'Bearer ' + tokenRefreshResponse.data.accessToken;
authContext.setAuthState({
authContext.authState,
accessToken: tokenRefreshResponse.data.accessToken,
});
await Keychain.setGenericPassword(
'token',
JSON.stringify({
accessToken: tokenRefreshResponse.data.accessToken,
refreshToken: authContext.authState.refreshToken,
}),
);
return Promise.resolve();
})
.catch(e => {
authContext.setAuthState({
accessToken: null,
refreshToken: null,
});
});
};
createAuthRefreshInterceptor(authAxios, refreshAuthLogic, {});
return (
<Provider
value={{
authAxios,
publicAxios,
}}>
{children}
</Provider>
);
};
export {AxiosContext, AxiosProvider};
xxxxxxxxxx
//index.js
import {AppRegistry} from 'react-native';
import App from './App';
import {name as appName} from './app.json';
import {AuthProvider} from './src/context/AuthContext';
import {AxiosProvider} from './src/context/AxiosContext';
import React from 'react';
const Root = () => {
return (
<AuthProvider>
<AxiosProvider>
<App />
</AxiosProvider>
</AuthProvider>
);
};
AppRegistry.registerComponent(appName, () => Root);
xxxxxxxxxx
//index.js
import {AppRegistry} from 'react-native';
import App from './App';
import {name as appName} from './app.json';
import {AuthProvider} from './src/context/AuthContext';
import {AxiosProvider} from './src/context/AxiosContext';
import React from 'react';
const Root = () => {
return (
<AuthProvider>
<AxiosProvider>
<App />
</AxiosProvider>
</AuthProvider>
);
};
AppRegistry.registerComponent(appName, () => Root);
xxxxxxxxxx
// App.js
import React, {useCallback, useContext, useEffect, useState} from 'react';
import Login from './src/components/Login';
import {AuthContext} from './src/context/AuthContext';
import * as Keychain from 'react-native-keychain';
import Dashboard from './src/components/Dashboard';
import Spinner from './src/components/Spinner';
const App = () => {
const authContext = useContext(AuthContext);
const [status, setStatus] = useState('loading');
const loadJWT = useCallback(async () => {
try {
const value = await Keychain.getGenericPassword();
const jwt = JSON.parse(value.password);
authContext.setAuthState({
accessToken: jwt.accessToken || null,
refreshToken: jwt.refreshToken || null,
authenticated: jwt.accessToken !== null,
});
setStatus('success');
} catch (error) {
setStatus('error');
console.log(`Keychain Error: ${error.message}`);
authContext.setAuthState({
accessToken: null,
refreshToken: null,
authenticated: false,
});
}
}, []);
useEffect(() => {
loadJWT();
}, [loadJWT]);
if (status === 'loading') {
return <Spinner />;
}
if (authContext?.authState?.authenticated === false) {
return <Login />;
} else {
return <Dashboard />;
}
};
export default App;
xxxxxxxxxx
// App.js
import React, {useCallback, useContext, useEffect, useState} from 'react';
import Login from './src/components/Login';
import {AuthContext} from './src/context/AuthContext';
import * as Keychain from 'react-native-keychain';
import Dashboard from './src/components/Dashboard';
import Spinner from './src/components/Spinner';
const App = () => {
const authContext = useContext(AuthContext);
const [status, setStatus] = useState('loading');
const loadJWT = useCallback(async () => {
try {
const value = await Keychain.getGenericPassword();
const jwt = JSON.parse(value.password);
authContext.setAuthState({
accessToken: jwt.accessToken || null,
refreshToken: jwt.refreshToken || null,
authenticated: jwt.accessToken !== null,
});
setStatus('success');
} catch (error) {
setStatus('error');
console.log(`Keychain Error: ${error.message}`);
authContext.setAuthState({
accessToken: null,
refreshToken: null,
authenticated: false,
});
}
}, []);
useEffect(() => {
loadJWT();
}, [loadJWT]);
if (status === 'loading') {
return <Spinner />;
}
if (authContext?.authState?.authenticated === false) {
return <Login />;
} else {
return <Dashboard />;
}
};
export default App;
xxxxxxxxxx
// src/components/Spinner.js
import React from 'react';
import {ActivityIndicator, StyleSheet, View} from 'react-native';
const Spinner = () => (
<View style={styles.container}>
<ActivityIndicator size="large" color="#007aff" />
</View>
);
const styles = StyleSheet.create({
container: {
flex: 1,
justifyContent: 'center',
alignItems: 'center',
},
});
export default Spinner;
xxxxxxxxxx
// src/components/Spinner.js
import React from 'react';
import {ActivityIndicator, StyleSheet, View} from 'react-native';
const Spinner = () => (
<View style={styles.container}>
<ActivityIndicator size="large" color="#007aff" />
</View>
);
const styles = StyleSheet.create({
container: {
flex: 1,
justifyContent: 'center',
alignItems: 'center',
},
});
export default Spinner;
xxxxxxxxxx
// src/components/Dashboard.js
import React, {useContext, useState} from 'react';
import {Button, Image, StyleSheet, View} from 'react-native';
import {AuthContext} from '../context/AuthContext';
import {AxiosContext} from '../context/AxiosContext';
import Spinner from './Spinner';
const Dashboard = () => {
const axiosContext = useContext(AxiosContext);
const authContext = useContext(AuthContext);
const [image, setImage] = useState(null);
const [status, setStatus] = useState('idle');
const loadImage = async () => {
setStatus('loading');
try {
const response = await axiosContext.authAxios.get('/cat');
setImage(response.data);
setStatus('success');
} catch (error) {
setStatus('error');
}
};
if (status === 'loading') {
return <Spinner />;
}
return (
<View style={styles.container}>
<Image
source={{uri: image}}
width={300}
height={500}
style={styles.image}
/>
<View style={styles.buttonGroup}>
<Button title="Get Image" onPress={loadImage} />
<Button title="Logout" onPress={() => authContext.logout()} />
</View>
</View>
);
};
const styles = StyleSheet.create({
container: {
justifyContent: 'center',
alignItems: 'center',
flex: 1,
},
image: {
width: '90%',
height: '50%',
resizeMode: 'contain',
},
buttonGroup: {
marginTop: 20,
flexDirection: 'row',
justifyContent: 'space-between',
width: '90%',
},
});
export default Dashboard;