React Native - Fetch data from API with Authorization Bearer Token - react-native

I am trying to fetch data of user profile and I got this error
"Unexpected token U in JSON at position 0" in my Android Simulator
I checked with in my console.log and I manage to get my API properly so I think that is not the issue
This is my Home index.js (the file trying to fetch data)
import AsyncStorage from '#react-native-async-storage/async-storage';
import React, {useEffect, useState} from 'react';
import {StyleSheet, Text, View} from 'react-native';
import {getData} from '../../utils';
export default function Home() {
const [data, setData] = useState([]);
const [token, setToken] = useState('');
useEffect(() => {
getData('token').then(res => {
const res_token = res;
console.log('token response: ', res_token);
setToken(res_token);
});
}, []);
useEffect(() => {
fetch('https://emaillead.aturtoko.id/api/v1/profile', {
method: 'GET',
headers: {
Accept: 'application/json',
'Content-Type': 'application/json',
Authorization: token,
},
})
.then(response => response.json())
.then(json => {
console.log('token auth: ' + token);
setData(json);
console.log(json);
})
.catch(error => console.error(error));
}, [token]);
return (
<View>
<Text>Nama: {data.name}</Text>
</View>
);
}
const styles = StyleSheet.create({});
So I got the token from my login page. I tried to make 2 useEffect, one to get the token and the other to get the data from API. I also tried the only 1 useEffect before as well with the same result.
This is the code when I use only 1 useEffect
useEffect(() => {
getData('token').then(res => {
const res_token = res;
console.log('token response: ', res_token);
setToken(res_token);
});
fetch('https://emaillead.aturtoko.id/api/v1/profile', {
method: 'GET',
headers: {
Accept: 'application/json',
'Content-Type': 'application/json',
Authorization: token,
},
})
.then(response => response.json())
.then(json => {
console.log('token auth: ' + token);
setData(json);
console.log(json);
})
.catch(error => console.error(error));
}, [token]);
I don't know where is the problem with my code right now

Related

seting auth token in react native not working

i am trying to set auth token in react native but it is not working.the api call to the url is woeking and data is saved to db but the token doesnot work
axios({
method: 'POST',
url: 'http://127.0.0.1:8000/api/register',
data: Data,
})
.then(function (response) {
console.log('working');
ReactSession.setStoreType('Bearer', response.data.token);
ReactSession.set('username', 'Meon');
})
.catch(error => {
alert(JSON.stringify(error.response.data));
});
}
i get this error
console.log(response); returns the following
I use AsyncStorage together with fetch to set mine and then when i want to use it , I also call AsyncStorage from '#react-native-async-storage/async-storage';
After setting the state like this,
const [email, setEmail] = useState('');
const [password, setPassword] = useState('');
I try to simulate a Login
To login looks like this :
FunctionLogin = async () => {
let item = {email, password};
fetch('http://192.168.1.101/api/auth/sign-in', {
method: 'POST',
mode: 'cors',
headers: {
'Accept': 'application/json',
'Content-type': 'application/json',
},
body: JSON.stringify(item),
})
.then(response => response.json())
.then(async (responseJson) => {
if (responseJson.message === 'OK') {
var token = responseJson.token;
await AsyncStorage.setItem('email', email);
await AsyncStorage.setItem('token', token);
navigation.replace('Dashboard');
} else {
alert(responseJson);
}
})
.catch(error => {
console.error(error);
});
}
To use it in any page, I use it like this , later i reference the function in useEffect
showdata = async () => {
let token = await AsyncStorage.getItem('token');
alert(token);
};
Suppose I want to get transaction list from my endpoint to display data I do it like this
getTransactionsList = async () => {
let token = await AsyncStorage.getItem('token');
let email = await AsyncStorage.getItem('email');
var url = 'https://192.168.1.101/api/user-data/get-transactionby-email/';
fetch(url + email, {
method: 'GET',
headers: {
'Content-type': 'application/json',
'Authorization': `Bearer ${token}`,
},
})
.then(response => response.json())
.then(responseJson => {
setTransaction_details(responseJson);
setLoading(false);
});
};
Then suppose i want to call it inside useEffect, I do like this
useEffect(() => {
getTransactionsList();
});
Thats what and how i do it and it works fine. If you also know how to use Redux, its still a good one as well.

Infinite loop React Native Screen due to fetch and useState

I'm having an infinite loop due to the change of state on react native, the problem is that I tried to fix it with Use Effect Hook and was impossible.
That is my code
import React from 'react';
import { Alert } from 'react-native';
import AsyncStorage from '#react-native-async-storage/async-storage'
import Constants from 'expo-constants';
import Leaderboard from 'react-native-leaderboard';
import { useEffect } from 'react/cjs/react.production.min';
const LeaderBoardScreen = ({ navigation }) => {
const baseUrl = Constants.manifest.extra.backend_url;
const [token, setToken] = React.useState(null);
const [users, setUsers] = React.useState(null);
_retrieveToken = async () => {
try {
const token = await AsyncStorage.getItem('userToken');
setToken(token);
} catch (error) {
Alert.alert("SESIÓN FINALIZADA");
}
};
_retrieveToken();
const requestOptions = {
method: 'GET',
headers: {
'Content-Type': 'application/json',
'Authorization': 'Bearer ' + token,
},
};
fetch(`${baseUrl}/user/top`, requestOptions)
.then((response) => response.json())
.then(data => {
if (data == undefined) {
Alert.alert(
"Usuario inválido",
"el usuario no existe, o la contraseña o usuario son incorrectas ¿Estás seguro de que tienes cuenta?"
);
}
console.log(data);
setUsers(data);
});
return (
<Leaderboard
data={users}
sortBy='highScore'
labelBy='userName'
icon='icon' />
);
}
export default LeaderBoardScreen;
It starts sending GET to my back end in a infinite loop
Try importing useEffect from React perhaps instead of where you have imported it from.
That's because fetch and get token are being called on every render, you should move the function call inside a useEffect, (Or React Query useQuery if you want a 3rd party library)
The following code gets the token after the first render and then runs the fetches whenever the token changes
const requestOptions = (token) => ({
method: 'GET',
headers: {
'Content-Type': 'application/json',
'Authorization': 'Bearer ' + token,
},
});
const LeaderBoardScreen = ({ navigation }) => {
const baseUrl = Constants.manifest.extra.backend_url;
const [token, setToken] = React.useState(null);
const [users, setUsers] = React.useState(null);
_retrieveToken = useCallback(async () => {
try {
const token = await AsyncStorage.getItem('userToken');
setToken(token)
} catch (error) {
Alert.alert("SESIÓN FINALIZADA");
}
},[])
useEffect(()=>{
_retrieveToken()
},[_retrieveToken])
useEffect(()=>{
if(!!token){
fetch(`${baseUrl}/user/top`, requestOptions(token))
.then((response) => response.json())
.then(data => {
if (data == undefined) {
Alert.alert(
"Usuario inválido",
"el usuario no existe, o la contraseña o usuario son incorrectas ¿Estás seguro de que tienes cuenta?"
);
}
console.log(data);
setUsers(data);
})
}
},[token])
return (
<Leaderboard
data={users}
sortBy='highScore'
labelBy='userName'
icon='icon' />
);
}
export default LeaderBoardScreen;

React Router: Check that JWT is valid before rendering a Private Route

I'm looking to implement a real world authentication for a React Router app. Every tutorial I've seen uses fakeAuth to simulate authentication, but doesn't actually implement real world authentication. I'm trying to actually implement authentication. Is this possible?
Right now I'm sending a jwt to the back end to check whether it is valid before returning the Component I want to render -- Redirect to Login if jwt authentication fails, or render Dashboard if it's a valid jwt. The problem is the ProtectedRoute is returning the redirect to /login before the back end is returning whether the jwt is valid or not.
How can I get real world authentication in my React-Router app? Is this even possible?
const PrivateRoute = ({ component: Component, ...rest }) => {
const [auth, setAuth] = useState(false);
useEffect(() => {}, [auth])
useEffect(() => {
// send jwt to API to see if it's valid
let token = localStorage.getItem("token");
if (token) {
fetch("/protected", {
method: "POST",
headers: {
'Accept': 'application/json',
'Content-Type': 'application/json'
},
body: JSON.stringify({ token })
})
.then((res) => {
return res.json()
})
.then((json) => {
if (json.success) {
setAuth(true);
}
})
.catch((err) => {
setAuth(false);
localStorage.removeItem("token");
});
}
}, [])
return (<Route {...rest}
render={(props) => {
return auth ? <Component {...props} /> : <Redirect to="/login" />
}} />)
}
}
I would say you need a state between authenticated/not valid jwt. I would use another state field, isTokenValidated (or isLoading):
const PrivateRoute = ({ component: Component, ...rest }) => {
const [auth, setAuth] = useState(false);
const [isTokenValidated, setIsTokenValidated] = useState(false);
useEffect(() => {
// send jwt to API to see if it's valid
let token = localStorage.getItem("token");
if (token) {
fetch("/protected", {
method: "POST",
headers: {
'Accept': 'application/json',
'Content-Type': 'application/json'
},
body: JSON.stringify({ token })
})
.then((res) => {
return res.json()
})
.then((json) => {
if (json.success) {
setAuth(true);
}
})
.catch((err) => {
setAuth(false);
localStorage.removeItem("token");
})
.then(() => setIsTokenValidated(true));
} else {
setIsTokenValidated(true); // in case there is no token
}
}, [])
if (!isTokenValidated) return <div />; // or some kind of loading animation
return (<Route {...rest}
render={(props) => {
return auth ? <Component {...props} /> : <Redirect to="/login" />
}} />)
}
}
As an alternative way, your backend server.js file can check if jwt is valid or not and send a status code accordingly. Then your frontend react component can check this status code and render (or not render) page accordingly.
For component:
import {useState, useEffect} from "react";
const Private = () => {
const [statusAuth, setStatusAuth] = useState(false);
useEffect(() => {
fetch("/privateroute")
.then((res) => {
if (res.status === 200) {
setStatusAuth(true)
} else {
setStatusAuth(false)
}
});
}, []);
return(
{statusAuth && <div>rendered page</div>}
{!statusAuth && <div>You need to login. <Link to="/login"><span>Click for login page</span></Link></div>}
)
}
For server.js file:
app.get("/privateroute", function(req, res){
const token = req.cookies.jwt;
if (token) {
jwt.verify(token, "signature-of-your-jwt-token", () => {
res.status(200).end();
})
} else {
res.status(404)
res.send("Tokens didnt match");
}
});

Handling Refresh Token in React Native

I have an app authenticating fine and returning the access_token and refresh_token. I store them with AsyncStorage and save/get the access_token with redux. This is the very first app I am building and I am struggling with how and where to use the refresh_token.
This is the axios call in the component loginForm.js
axios({
url: `${base}/oauth/token`,
method: 'POST',
data: formData,
headers: {
Accept: 'application/json',
'Content-Type': 'multipart/form-data',
}
})
.then(response => {
setStatus({ succeeded: true });
// console.log(response.data);
deviceStorage.saveKey("userToken", response.data.access_token);
deviceStorage.saveKey("refreshToken", response.data.refresh_token);
Actions.main();
})
.catch(error => {
if (error.response) {
console.log(error);
}
});
This is the service deviceStorage.js
import { AsyncStorage } from 'react-native';
const deviceStorage = {
async saveItem(key, value) {
try {
await AsyncStorage.setItem(key, value);
} catch (error) {
console.log('AsyncStorage Error: ' + error.message);
}
}
};
export default deviceStorage;
This is the token action file
import { AsyncStorage } from 'react-native';
import {
GET_TOKEN,
SAVE_TOKEN,
REMOVE_TOKEN,
LOADING_TOKEN,
ERROR_TOKEN
} from '../types';
export const getToken = token => ({
type: GET_TOKEN,
token,
});
export const saveToken = token => ({
type: SAVE_TOKEN,
token
});
export const removeToken = () => ({
type: REMOVE_TOKEN,
});
export const loading = bool => ({
type: LOADING_TOKEN,
isLoading: bool,
});
export const error = tokenError => ({
type: ERROR_TOKEN,
tokenError,
});
export const getUserToken = () => dispatch =>
AsyncStorage.getItem('userToken')
.then((data) => {
dispatch(loading(false));
dispatch(getToken(data));
})
.catch((err) => {
dispatch(loading(false));
dispatch(error(err.message || 'ERROR'));
});
export const saveUserToken = (data) => dispatch =>
AsyncStorage.setItem('userToken', data)
.then(() => {
dispatch(loading(false));
dispatch(saveToken('token saved'));
})
.catch((err) => {
dispatch(loading(false));
dispatch(error(err.message || 'ERROR'));
});
export const removeUserToken = () => dispatch =>
AsyncStorage.removeItem('userToken')
.then((data) => {
dispatch(loading(false));
dispatch(removeToken(data));
})
.catch((err) => {
dispatch(loading(false));
dispatch(error(err.message || 'ERROR'));
});
This is the token reducer file
import {
GET_TOKEN,
SAVE_TOKEN,
REMOVE_TOKEN,
LOADING_TOKEN,
ERROR_TOKEN
} from '../actions/types';
const INITIAL_STATE = {
token: {},
loading: true,
error: null
};
export default (state = INITIAL_STATE, action) => {
switch (action.type) {
case GET_TOKEN:
return {
...state,
token: action.token
};
case SAVE_TOKEN:
return {
...state,
token: action.token
};
case REMOVE_TOKEN:
return {
...state,
token: action.token
};
case LOADING_TOKEN:
return {
...state,
loading: action.isLoading
};
case ERROR_TOKEN:
return {
...state,
error: action.error
};
default:
return state;
}
};
And this is the authentication file
import React from 'react';
import {
StatusBar,
StyleSheet,
View,
} from 'react-native';
import { connect } from 'react-redux';
import { Actions } from 'react-native-router-flux';
import { Spinner } from '../common';
import { getUserToken } from '../../actions';
class AuthLoadingScreen extends React.Component {
componentDidMount() {
this.bootstrapAsync();
}
bootstrapAsync = () => {
this.props.getUserToken().then(() => {
if (this.props.token.token !== null) {
Actions.main();
} else {
Actions.auth();
}
})
.catch(error => {
this.setState({ error });
});
};
render() {
return (
<View style={styles.container}>
<Spinner />
<StatusBar barStyle="default" />
</View>
);
}
}
const styles = StyleSheet.create({
container: {
flex: 1,
alignItems: 'center',
justifyContent: 'center'
},
});
const mapStateToProps = state => ({
token: state.token,
});
const mapDispatchToProps = dispatch => ({
getUserToken: () => dispatch(getUserToken()),
});
export default connect(mapStateToProps, mapDispatchToProps)(AuthLoadingScreen);
I believe I need to create an action and reducer to get the refresh_token (is that correct?) but I do not know what to do with it and where to call it (perhaps in the authentication file?).
Any help with this possibly with code examples related to my code would be massively appreciated. Thanks
Below are the steps
Do Login , get accessToken , refreshToken from response and save it to AsyncStorage.
Make common function for API calling
async function makeRequest(method, url, params, type) {
const token = await AsyncStorage.getItem('access_token');
let options = {
method: method,
headers: {
Accept: 'application/json',
'Content-Type': 'application/json',
Authorization: 'Bearer ' + token,
},
};
if (!token) {
delete options['Authorization'];
}
if (['GET', 'OPTIONS'].includes(method)) {
url += (url.indexOf('?') === -1 ? '?' : '&') + queryParams(params);
} else {
Object.assign(options, {body: JSON.stringify(params)});
}
const response = fetch(ENV.API_URL+url, options);
return response;
}
Make one method in redux for getAceessTokenFromRefreshToken.
Use this method when session is expired
How do you know session is expired?
From each API calling if you get response like (440 response code) in
async componentWillReceiveProps(nextProps) {
if (nextProps.followResponse && nextProps.followResponse != this.props.followResponse) {
if (nextProps.followResponse.status) {
if (nextProps.followResponse.status == 440) {
// call here get acceesstokenfrom refresh token method and save again accesstoken in asyncstorage and continue calling to API
}
}
}
}

Getting Undefined in React native

import React, { Component } from 'react';
import { View, Text } from 'react-native';
class HttpExample extends Component {
state = {
data: ''
}
componentDidMount = () => {
fetch("https://jsonplaceholder.typicode.com/posts/1", { --this is fake url
method: 'POST',
headers: new Headers({
'Content-Type': 'application/x-www-form-urlencoded', // <-- Specifying the Content-Type
}),
body: "param1=value1&param2=value2" // <-- Post parameters
})
.then((response) =>
response.text()
)
.then((responseText) => {
alert(responseText.id);
this.setState({
data: responseText
})
})
.catch((error) => {
console.error(error);
});
}
render() {
return (
<View>
<Text>
{this.state.data.id}
</Text>
</View>
)
}
}
export default HttpExample;
if i use alert(ResponseText)
in alert i am getting o/p but as i tried to have individual value from my object it returns undefined
o/p: "id": "1",
"computeId": "USR00001" in alert
.then((response) => response.text())
to
.then((response) => response.json())
I guess you need json style response.