Token is Invalid in Reactjs application - reactjs

I try to get a list from the backend in Reactjs component with JWT token but I get an error message {"status":"Token is Invalid"}, please guide me.
My backend API is working fine and my token is saved in the localstore after login.
my frontend used API code
import {API} from "../config";
/**
* to get about pages
* get a single about page
* update a about page
* delete about page
*/
export const getAboutContent = (token) =>{
return fetch(`${API}/about/?token=${token}`, {
method: "GET",
})
.then(response =>{
return response.json();
})
.catch(err =>{
console.log(err);
});
};
about/index.js
const [allAboutContent, setAllAboutContent] = useState([]);
const loadAllAboutContent = () => {
getAboutContent().then(data => {
if(data.error){
console.log(data.error)
} else{
setAllAboutContent(data.data)
}
});
};
useEffect(() =>{
loadAllAboutContent();
}, [])
Please help.

You are invoking getAboutContent in about/index.js file without JWT and hence it not defined. Just update your code to read JWT from localStorage like below
const loadAllAboutContent = () => {
// Read token from local storage
const token = localStorage.getItem('jwt');
// Pass to getAboutContent
getAboutContent(token).then(data => {
if(data.error){
console.log(data.error)
} else{
setAllAboutContent(data.data)
}
});
};
Also, I see you have stored your token as {token: ''}. Maybe, you can directly save it. Otherwise you have to read it like this JSON.parse(localStorage.getItem('jwt')).token

Related

Failing to call two APIs one after another and redirecting the user to another page

I'm using Reactjs for my frontend and Springboot and Firebase for my backend. Basically, when a user registers for an account, I will call two APIs to post the account to both Firebase and my Springboot server, the reason being that I need to make use of the user data collected inside my own Springboot server.
The problem I'm facing is that after a user registers for an account, the Springboot API is not called after Firebase API is called. (I call the Firebase API first and then the Springboot one) It seems to me that after calling the Firebase API, everything stops and the code doesn't go any further, thus calling no more API.
How do I make sure that I can call both APIs one after another and then redirect the user to the next page without any interference?
Registration on submit in Reactjs:
const handleOnSubmit=(event: React.FormEvent<HTMLFormElement>)=> {
if (password !== secondPassword) {
setPasswordsMatched(false);
console.log("passwords matched when password!==secondPassword:" + passwordsMatched);
} else if(!username){
setUsernameExists(false);
}else if(!email){
setEmailExists(false);
}else if(!password||!secondPassword){
setPasswordExists(false);
}else{
if(subscribedStatus){
let subscribed:string="subscribed";
firebaseAuthServiceSignUpWithEmailAndPassword(username,email,password,subscribed,handleSignupSuccess);
}else{
let subscribed:string="unsubscribed";
firebaseAuthServiceSignUpWithEmailAndPassword(username,email,password,subscribed,handleSignupSuccess);
}
}
}
//This is the callback function put inside the Firebase API to see if Firebase accepts the registration. If yes, the user is redirected to "/verification-email"
const handleSignupSuccess=(signupStatus:boolean)=>{
setSignupSuccess(signupStatus);
if(signupStatus){
firebaseAuthServiceEmailVerification(setEmailVerificationSent);
navigate("/verification-email");
}
}
Firebase API:
export const firebaseAuthServiceSignUpWithEmailAndPassword= (username:string,email: string, password: string, subscribed:string,callback: (isSuccess:boolean)=>void) =>{
const auth = getAuth();
createUserWithEmailAndPassword(auth, email, password)
.then(async ( userCredential) => {
// Signed in
const user = userCredential.user;
await postAccount(username, email, password, user.uid, subscribed);
callback(true);
})
.catch((error) => {
const errorCode = error.code;
const errorMessage = error.message;
callback(false);
// ..
});
}
Springboot API:
export const postAccount=(username:string,email:string,password:string,firebaseUid:string,subscribedStatus:string)=>{
axios.post(`http://localhost:8080/user/${username}/${email}/${password}/${firebaseUid}/${subscribedStatus}`
)
.then((res)=>{
}).catch((error)=>{
console.log(error);
})
}
You mostly need a return statement in postAccount function
Quick fix
export const postAccount=(username:string,email:string,password:string,firebaseUid:string,subscribedStatus:string)=>{
// return here
return axios.post(`http://localhost:8080/user/${username}/${email}/${password}/${firebaseUid}/${subscribedStatus}`
)
.then((res)=>{
return res; // may be return here too
}).catch((error)=>{
console.log(error);
})
}
// async function
const handleOnSubmit= async (event: React.FormEvent<HTMLFormElement>)=> {
if (password !== secondPassword) {
setPasswordsMatched(false);
console.log("passwords matched when password!==secondPassword:" + passwordsMatched);
} else if(!username){
setUsernameExists(false);
}else if(!email){
setEmailExists(false);
}else if(!password||!secondPassword){
setPasswordExists(false);
}else{
if(subscribedStatus){
let subscribed:string="subscribed";
// wait till completion
await firebaseAuthServiceSignUpWithEmailAndPassword(username,email,password,subscribed,handleSignupSuccess);
}else{
let subscribed:string="unsubscribed";
// wait till completion
await firebaseAuthServiceSignUpWithEmailAndPassword(username,email,password,subscribed,handleSignupSuccess);
}
}
}
Slightly better fixs:
With multiple API call its better to use async calls
export const firebaseAuthServiceSignUpWithEmailAndPassword = async (username:string, ...) => {
try {
const auth = getAuth();
const userCredentials = await createUserWithEmailAndPassword(auth, email, password)
const user = userCredential.user;
const res = await postAccount(username, email, password, user.uid, subscribed);
// may be some extra checks
//if (res.success) {
// callback(true);
//}
callback(true);
} catch(error: any) {
// handle error
callback(false);
}
}
export const postAccount = async (username: string, ...) => {
return await axios.post(`http://localhost:8080/user/${username}/${email}/${password}/${firebaseUid}/${subscribedStatus}`)
}
Hope it helps in someway

how to pass access token from local storage in react redux

enter image description here
Here i have screen shot of my local storage. how can i fetch access token from there pass as headers in below action page. please provide any solution for this. how we can fetch token from local storage using react redux and display in action page.
import axios from 'axios';
export const upiAction = {
upi,
};
function upi(user) {
return (dispatch) => {
var data = {
upiId: user.upiId,
accountNumber: user.accountNumber,
};
axios
.post('http://localhost:9091/upiidcreation', data,
)
.then((res) => {
console.log("res", (res));
const { data } = res;
alert(JSON.stringify(data.responseDesc));
// window.location.pathname = "./homes";
if (data.responseCode === "00") {
window.location.pathname = "./home"
}
})
.catch(err => {
dispatch(setUserUpiError(err, true));
alert("Please Check With details");
});
};
}
export function setUserUpi(showError) {
return {
type: 'SET_UPI_SUCCESS',
showError: showError,
};
}
export function setUserUpiError(error, showError) {
return {
type: 'SET_UPI_ERROR',
error: error,
showError: showError,
};
}
if you just need to fetch the token and send it as header in the api request you can do this
let storageValue =JSON.parse(localStorage.getItem('currentUser')
storageValue object will have the whole thing that you've stored in localStorage .
axios.post('http://localhost:9091/upiidcreation', data, {
headers: {
token : storageValue?.data?.accessToken
}
})
You can get localStorage Object like this
let localStorageObject = JSON.parse(localStorage.getItem('currentUser'));
Then You can use it that object to get access token like this:
localStorageObject?.data?.accessToken

Login status in React

I created authorization in javascript. Then if success login I redirect to React project with url parameter http://localhost:3000/?phoneNum=%2B77072050399
Then in React I get userId by using the passed url parameter => phoneNumber using axios.
I realized it in App.js. Code below:
let url = window.location.href;
let params = (new URL(url)).searchParams;
const userPhoneNum = encodeURIComponent(params.get('phoneNum'));
const [userToken, setUserToken] = useState(null);
const getUserToken = async() => {
try {
const data = await axios
.get(`https://stormy-escarpment-89406.herokuapp.com/users/getToken?phone_number=${userPhoneNum}`)
.then(response => {
setUserToken(response.data);
})
.catch(function(error) {
console.log('No such user! Error in getting token!');
});
} catch (e) {
console.log(e);
}
}
useEffect(() => {
getUserToken();
console.log(userToken);
}, userToken);
So, when I go to next page localhost:3000/places, it is requesting for userToken again with parameter null, because there is no param phoneNum.
How to make it to request only one time and save the userId after it is taken in main page. So, then only when I click LogOut button reset the variable where userID is saved.
If you want to do that without using any third party libraries you can use browser's in built storage api
So, when you receive the token, you can store that in the local storage of the browser using localstorage.setItem and later when you wan to see if the token is there or not just read from there using localStorage.getItem
const getUserToken = async() => {
try {
const data = await axios
.get(`https://stormy-escarpment-89406.herokuapp.com/users/getToken?phone_number=${userPhoneNum}`)
.then(response => {
setUserToken(response.data);
Storage.setItem('token',JSON.stringify(response.data))
})
.catch(function(error) {
console.log('No such user! Error in getting token!');
});
} catch (e) {
console.log(e);
}
}
For Logout you can simply remove the token using localStorage.removeItem
You can easily achieve this by using the react-cookie library
npm i react-cookie
Can be easily implemented in your code by
cookies.set('key', value, { path: '/' });
cookies.get('key')
After getting the userNumber form the param
const userPhoneNum = encodeURIComponent(params.get('phoneNum'));
cookies.set('userphoneNum', userPhoneNum);
View the documentation for more information
https://www.npmjs.com/package/react-cookie

Property in state becomes undefined within action creators

I'm making an app where different users can add their own plants/flowers.
The flowerlist contains the users flowers and loads these items upon mounting.
class flowerList extends Component {
componentDidMount() {
this.props.getFlowers();
}
To send the correct GET request to the backend I need to have the currently logged in user's ID.
This is what the called action creator looks like:
export const getFlowers = () => (dispatch, getState) => {
dispatch(setFlowersLoading());
axios
.get(`/api/users/${getState().auth.user.id}/flowers`)
.then((res) =>
dispatch({
type : GET_FLOWERS,
payload : res.data
})
)
.catch((err) => dispatch(returnErrors(err.response.data, err.response.status)));
};
However, this doesn't work very well. It only works when coming directly from signing in. If I refresh the page, the app crashes with the error message "TypeError: Cannot read property 'id' of null". When writing the POST requests in a similar fashion it doesn't work well either, so I guess there must be a better way to access the state. I'd really appreciate any help in getting this to work.
When you login, you should set local storage to keep the users info something like these:
const setAuthorizationHeader = token => {
const Token = `Bearer ${token}`;
localStorage.setItem("Token", Token);
axios.defaults.headers.common["Authorization"] = Token;
};
you can add it to your user login action, after then(), when the login is successful, here is an example, I assume you handle the token in the backend, so after successful login, it sends the token with a respond(res.data):
export const loginUser = (userData) => dispatch => {
axios
.post("http://localhost:5000/api/users/login", userData)
.then(res => {
setAuthorizationHeader(res.data.token);
})
.catch(err => {
dispatch({
type: SET_ERRORS,
payload: err.response
});
});
};
Afterwards, put these to your app.js:
const token = localStorage.Token;
if (token) {
const decodedToken = jwtDecode(token);
if (decodedToken.exp * 1000 < Date.now()) {
store.dispatch(logoutUser());
window.location.href = "/login";
} else {
store.dispatch({ type: SET_AUTHENTICATED_USER });
axios.defaults.headers.common["Authorization"] = token;
store.dispatch(getUserData(decodedToken));
}
}
Here I used jwtDecode because I am using JWT to crypt my users' info and store it to the localStorage, these codes provide to look for Token in localStorage after refreshing the page. If the user logged in, there is the token and so the app will not crash

Axios request interceptor not working on browser refresh

I am using axios interceptor in my react app to pass the token for each request.
I initially call the setupAxiosInterceptors method after I login (See code below). This works perfectly fine until I refresh the browser.
const registerSucessfulLoginForJwt = (username, token) => {
sessionStorage.setItem(USER_NAME_SESSION_ATTRIBUTE_NAME, username)
setupAxiosInterceptors(createJwtAuth(token)) //Calling the axios interceptor at the time of login
}
See below the setupAxiosInterceptors method
const setupAxiosInterceptors = (token) => {
Axios.interceptors.request.use((config) => {
if(isUserLoggedIn()) {
config.headers.authorization = token
sessionStorage.setItem('authorization', token)
}
return config
})
}
Any thought on how to fix this so it works at all time?
I was able to find a solution to my problem. I create an ApiSetup.js file where I create a custom axios instance which could use for all requests.
const request = axios.create({
baseURL: API_PATH_BASE
})
request.interceptors.request.use(config => {
const currentUser = AuthenticationService.getLoggedInUser() //You can get the user directly from the cookie or session storage...
if(currentUser.userName) {
config.headers.Authorization = currentUser.userToken
}
return config
}, err => {
console.log(err)
return Promise.reject(err)
})
export default request

Resources