retrieving data from an api call - reactjs

I have successfully retrieved data from a login API call and I return the data variable which logs user information eg. id, token, email and this is successfully printed to the console.
async function login(email: string, password: string, rememberMe: boolean) {
const requestOptions = {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({ email, password, rememberMe }),
};
await fetch(`${API_URL}/auth/login`, requestOptions).then((response) => {
if (response.ok === true) {
response.json().then((data) => {
console.log(data);
if (data.success === true) {
localStorage.setItem("USER_ID", data.id);
localStorage.setItem("EMAIL", data.email);
localStorage.setItem("ACCESS_TOKEN_KEY", data.token);
return data;
} else {
return Promise.reject(new Error("toast.user.general_error"));
}
});
} else {
return Promise.reject(new Error(response.statusText));
}
});
}
however I get user = undefined when logging to the console suggesting that my data variable is undefined
function login(email: string, password: string, rememberMe: boolean) {
return (dispatch: ThunkDispatch<{}, void, AnyAction>) => {
authService.login(email, password, rememberMe).then(
(user) => {
history.push("/student/dashboard");
console.log("user = ", user);
},
(error) => {
dispatch(failure(error.toString()));
}
);
};
}
why am I not retrieving the user variable from my fetch request? Should I be wrapping the data variable with a promise before returning it?

Login must return something at the top-level, not from within a then block.
Since you're already using async/await, try it like this.
async function login(email: string, password: string, rememberMe: boolean) {
const requestOptions = {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({ email, password, rememberMe }),
};
const response = await fetch(`${API_URL}/auth/login`, requestOptions);
if (!response.ok) return new Error(response.statusText);
const data = await response.json();
console.log(data);
if (data.success !== true) return new Error("toast.user.general_error");
return data;
}

Related

when i tried to store mongodb document _id in cookie then i show an error

this is error
POST http://localhost:3000/login 422 (Unprocessable Entity)
this is backend code
if (user) {
const isMatch = await bcrypt.compare(password, user.password)
const token = await user.generateTokens();
const _id = user._id.ObjectId.toString();
console.log(_id);
res.cookie('jwtoken', token, { expires: new Date(Date.now() + 25892000000), httpOnly: true });
res.cookie('id', _id, { expires: new Date(Date.now() + 25892000000), httpOnly: true });
if (isMatch) {
res.status(200).json({ message: "user loged in" })
} else {
res.status(400).json({ error: "invalid details" })
}
} else {
res.json({ error: "user not found" })
}
this is frontend code
const handleClick = async (e) => {
e.preventDefault();
const { email, password } = user;
const res = await fetch("/login", {
method: "POST",
headers: {
"Content-Type": "application/json",
},
body: JSON.stringify({ email, password })
})
const data = await res.json()
const message = data.message || data.error;
toast(message, { position: 'top-center' })
if(data.message){
history('/home')
}
}
i want to store mongodb document id in cookie

react-admin useGetIdentity return only the fullname, id is undefined avatar is undefined

my app is based on tutorial of React-admin and loopback 4 as a backend
I'm trying to get the id of the logged in user, the login mechanisms works well but when i try to access the id of the logged in user it remains undefined.
in my authProvider, my login function is
login: ({ username, password }) => {
const request = new Request(
process.env.REACT_APP_API_URL + '/users/login',
{
method: 'POST',
body: JSON.stringify({ email: username, password }),
headers: new Headers({ 'Content-Type': 'application/json' }),
},
);
return fetch(request)
.then((response) => {
if (response.status < 200 || response.status >= 300) {
throw new Error(response.statusText);
}
return response.json();
})
.then((auth) => {
localStorage.setItem(
'auth',
JSON.stringify({ ...auth, fullName: username }),
);
})
.catch(() => {
throw new Error('Network error');
});
},
and I use this in one component:
const CurrentUserId = ({ id }) => {
const { identity, isLoading: identityLoading } = useGetIdentity();
console.log(identity);
if (identityLoading) {
return <span>Loading...</span>;
} else {
// find the user_id from the identity
const user_email = identity.fullName;
const user_id = identity.id;
return <span>id: {user_id}</span>;
}
};
but the I console.log returns
{id: undefined, fullName: 'xxx#xxxxx.com', avatar: undefined}
I followed the instructions presented here
https://marmelab.com/react-admin/AuthProviderWriting.html
https://marmelab.com/react-admin/useGetIdentity.html
any ideas how to retrieve the id?
thanks a lot
If you receive a JWT token from the server, you need to decode it and store it like this:
import jwtDecode from 'jwt-decode'
...
function saveLBToken({ token } : { token: string }) {
const decoded = jwtDecode(token)
if (decoded && typeof decoded === 'object') {
sessionStorage.setItem(LB4_TOKEN, JSON.stringify({ token, ...decoded }))
} else {
console.log('Bad LB token:', decoded)
}
}
Thanks to MaxAlex answer I ended up using this in my code:
export const authProvider = {
// authentication
login: ({ username, password }) => {
const request = new Request(
process.env.REACT_APP_API_URL + '/users/login',
{
method: 'POST',
body: JSON.stringify({ email: username, password }),
headers: new Headers({ 'Content-Type': 'application/json' }),
},
);
return fetch(request)
.then((response) => {
if (response.status < 200 || response.status >= 300) {
throw new Error(response.statusText);
}
return response.json();
})
.then((auth) => {
const { id, name, email, exp, iat } = jwtDecode(auth.token);
if (!id || !name || !email || !exp || !iat) {
throw new Error('Invalid token');
}
if (exp < iat) {
throw new Error('Token expired');
}
localStorage.setItem(
'auth',
JSON.stringify({
...auth,
id,
fullName: name,
email,
exp,
iat,
}),
);
})
.catch(() => {
throw new Error('Network error');
});
},

Redirecting to a React Component - Express

I have a Login component on the front in which I make a POST to a rout on the server:
const handleSubmit = async (e) => {
e.preventDefault();
try {
fetch("http://localhost:3000/login", {
method: "POST",
mode: "cors",
headers: {
"Content-Type": "application/json",
"Access-Control-Allow-Origin": "*",
},
body: JSON.stringify({
email,
password,
}),
});
} catch (error) {
console.log(error.message);
}
};
On the server side, I validate the information sended from the front:
app.post("/login", async (req, res) => {
const user = await Users.findOne({
where: { email: req.body.email, password: req.body.password },
});
if (user) {
console.log(user);
console.log("usuario logeado");
res.status(200).send("LOGGED");
} else {
console.log("Usuario no Registrado");
}
});
I want to redirect to a component Home on the front once I validate the user and idk how to do it.
You need to read the response from the server and figure out what you want to do with it. I'd recommend not sending just a string from the server, so this solution will restructure a bit to hopefully help you understand and expand for your own needs.
// From your server - we're sending some json that we can
// read in the front end code. Maybe you also want to send
// the user object or other data, and json is great for that.
app.post("/login", async (req, res) => {
const user = await Users.findOne({
where: { email: req.body.email, password: req.body.password },
});
if (user) {
console.log(user);
console.log("usuario logeado");
res.status(200).send({
isLoggedIn: true,
});
} else {
console.log("Usuario no Registrado");
res.status(200).send({
isLoggedIn: false,
});
}
});
const handleSubmit = async (e) => {
e.preventDefault();
try {
const response = await fetch("http://localhost:3000/login", {
method: "POST",
mode: "cors",
headers: {
"Content-Type": "application/json",
"Access-Control-Allow-Origin": "*",
},
body: JSON.stringify({
email,
password,
}),
});
// Get the server response as json
const data = await response.json()
if(data.isLoggedIn) {
// Redirect to the account
window.location.href = "/account"
} else {
// Show some error message
window.alert("Invalid login")
}
} catch (error) {
console.log(error.message);
}
};

React-admin JWT authentication refresh token problem

I want to implement my own authProvider for react-admin but I'm stuck.
I use a Django-Rest-Framework backend and a JWT token authentication system.
I want to refresh the JWT token if it's almost expired before every request. According to the documentation the authProvider's checkAuth function gets called before every API call, which is true. My problem is that with my code it doesn't wait for the promise to finish and it uses the old access token which results in a 401 and I get redirected to the login page. Any guidance what am I missing?
import jwt from 'jsonwebtoken';
export default {
login: async ({ username, password }) => {
const request = new Request('http://localhost:8000/api/token/', {
method: 'POST',
body: JSON.stringify({ username, password }),
headers: new Headers({ 'Content-Type': 'application/json' }),
});
const response = await fetch(request);
if (response.status < 200 || response.status >= 300) {
throw new Error(response.statusText);
}
const { refresh, access } = await response.json();
localStorage.setItem("refreshToken", refresh);
localStorage.setItem("accessToken", access);
},
logout: params => {
console.log("logout");
localStorage.setItem('accessToken', "");
localStorage.setItem('refreshToken', "");
return Promise.resolve();
},
checkAuth: (params) => {
const accessToken = localStorage.getItem('accessToken');
const refreshToken = localStorage.getItem('refreshToken');
if (accessToken && refreshToken) {
console.log(accessToken);
const { exp } = jwt.decode(accessToken);
if (exp > (new Date().getTime() / 1000) - 10) {
return Promise.resolve();
} else {
const request = new Request('http://localhost:8000/api/token/refresh/', {
method: 'POST',
body: JSON.stringify({ "refresh": refreshToken }),
headers: new Headers({ 'Content-Type': 'application/json' }),
});
const response = fetch(request)
.then(response => {
if (response.status !== 200) {
throw new Error(response.statusText);
}
return response.json();
})
.then(({ token }) => {
localStorage.setItem('accessToken', token);
return Promise.resolve();
});
return response;
}
}
return Promise.reject();
},
checkError: error => {
if (error.status === 401 || error.status === 403) {
return Promise.reject();
}
return Promise.resolve();
},
getPermissions: params => Promise.resolve(),
}
Can you try something like that
checkAuth: async (params) =>
And
const request = new Request(...);
let data;
const response = await fetch(request);
if (response.ok) data = await response.json()
else throw new Error(response.statusText);
if (data && data.token) {
localStorage.setItem('accessToken', data.token);
console.log(data.token);
return Promise.resolve();
} else return Promise.reject();

Persistent storing not working on user_id but works on access_token

I'm trying to store my user id which I am getting from my server via a fetch function. I am able to store the access token I am getting HOWEVER, I am not able to do the same for my userId.
This is how I am fetching the userid from the backend:
async onRegisterPressed() {
try {
let response = await fetch('SERVER_URL', {
method: 'POST',
headers: {
'Accept': 'application/json',
'Content-Type': 'application/json',
},
body: JSON.stringify({
user:{
email: (this.state.email).toLowerCase(),
password: this.state.password,
password_confirmation: this.state.password_confirmation,
}
})
});
let res = await response._bodyText;
if (response.status >= 200 && response.status < 300) {
this.storeToken(JSON.parse(res).data.user.authentication_token)
this.storeID((JSON.parse(res).data.user.id).toString())
this.redirect(JSON.parse(res).data.user.authentication_token, this.state.email, (JSON.parse(res).data.user.id).toString())
} else {
let error = res;
this.setState({ error: 'Please try again' })
throw error;
}
} catch(errors) {
this.removeToken()
this.setState({ error: 'Oops, try again' })
}
}
The following functions are what I am using to persistent store the email, access token (and attempting to store the user id):
const ACCESS_TOKEN = 'authentication_token'
const USER_ID = 'id'
persistData() {
console.log('persistData function')
let email = this.state.email
let accessToken = this.state.accessToken
AsyncStorage.setItem('email', email)
AsyncStorage.setItem('accessToken', accessToken)
}
persistID() {
console.log('persistID function')
let userId = this.state.userId
AsyncStorage.setItem('userId', userId)
}
redirect(accessToken, email, id) {
console.log('redirect function')
this.props.navigation.navigate(
'Home',
{ accessToken: accessToken,
email: email,
userId: id,
onLogout: () => this.clearData()
}
)
}
async storeToken(accessToken) {
console.log('storeToken function')
try {
await AsyncStorage.setItem(ACCESS_TOKEN, accessToken)
this.getToken()
this.setState({accessToken: accessToken})
this.persistData()
} catch(error) {
console.log('ERROR STORE TOKEN')
}
}
async storeID(userId) {
console.log('storeID function')
try {
await AsyncStorage.setItem(USER_ID, userId)
this.getID()
this.setState({userId: userId})
this.persistID()
} catch(error) {
console.log('ERROR STORE TOKEN')
}
}
async getID() {
console.log('getID function')
try {
let id = await AsyncStorage.getItem(USER_ID)
} catch(error) {
console.log('GET TOKEN ERROR')
}
}
async getToken() {
console.log('getToken function')
try {
let token = await AsyncStorage.getItem(ACCESS_TOKEN)
} catch(error) {
console.log('GET TOKEN ERROR')
}
}

Resources