I am trying to make an ecommerce using react, redux toolkit and axios
the problem is that I want the user to log in and get his cart from the backend right after the login
it always fails the and says (unauthorized) when i first login because it can't find the token
then after refresh it says unauthorized one more time
after the third refresh it works
this is my get cart
export const getCart = createAsyncThunk("cart/getcart", async () => {
const response = await axios.get("http://127.0.0.1:8000/techcart/get_cart/", {
headers: {
Authorization: `Token ${token}`,
},
});
return response.data;
});
const cartSlice = createSlice({
name: "cart",
initialState: {
cart: [],
cartItemsIds :[],
},
builder.addCase(getCart.fulfilled, (state, action) => {
state.cart = action.payload;
and this is my login function
export const login = createAsyncThunk(
"auth/login",
async ({ email, password }, thunkAPI) => {
try {
const response = await axios.post(
"http://127.0.0.1:8000/techcart/login/",
{ username: email, password }
);
localStorage.setItem("user", JSON.stringify(response.data));
return response.data;
} catch (error) {}
}
);
const initialState = user
? { isLoggedIn: true, user }
: { isLoggedIn: false, user: null };
builder.addCase(login.fulfilled, (state, action) => {
state.isLoggedIn = true;
state.user = action.payload;
here is where i am doing the login
const HandleLogin = () => {
dispatch(login({ email, password }));
};
useEffect(()=> {
if(isLoggedIn){
navigate('/')
dispatch(getCart())
}
},[isLoggedIn])
Cart page
useEffect(() => {
dispatch(getCart());
}, []);
here is where im defining my token :
export let user = JSON.parse(localStorage.getItem("user")) ? JSON.parse(localStorage.getItem("user")) : null;
export let userId = user ? user.user_id : null;
export let token = user!=null ? user.token : null;
and here is where im importing it in my cart slice
import { user, token } from "../../constants";
im using redux persist to persist the state of my cart
if anyone can help me i'm so thankful
here is what happens
You're initializing your token directly when your js is executed. So when you retrieve it, it is undefined.
Ans when you do the login, you're indeed storing your token, but you're not updating it in your application.
I can see you're using redux, so store your token in your redux store, and before sending your api call to retrieve your cart, retrieve your token from redux, to always have the latest value of your token
Related
Error: authorization bearer undefined is shown in the browser on the .
Here is my
Here is my code of useEffect of OrderScreen.js Here I have tried to dispatch check if user have value or not if not redirect them to /login which is login page. I am redirecting to login page because from use state i am not getting any value.
const dispatch = useDispatch()
const navigate = useNavigate()
const { user } = useSelector((state) => state.auth)
const { orders, isLoading, isError, message } = useSelector(
(state) => state.orders,
)
useEffect(() => {
if (isError) {
toast.error(message)
}
if (!user && !user.isAdmin) {
navigate('/login')
}
dispatch(getOrder())
return () => {
dispatch(reset())
}
}, [user, isError, message, dispatch, navigate])
`
Here is my orderSlice. for the getOrder Function `
const initialState = {
orders: [],
isError: false,
isSuccess: false,
isLoading: false,
message: '',
}
export const getOrder = createAsyncThunk(
'orders/getOrder',
async (_, thunkAPI) => {
try {
const token = thunkAPI.getState().auth.user.token
return await orderService.getOrder(token)
} catch (error) {
const message =
(error.response &&
error.response.data &&
error.response.data.message) ||
error.message ||
error.toString()
return thunkAPI.rejectWithValue(message)
}
},
)
`
Here is my orderService.js `
const getOrder = async (token) => {
const config = {
headers: {
Authorization: `Bearer ${token}`,
},
}
const response = await axios.get(API_URL, config)
return response.data
}
`
I tried to solve from these stacks
Authorization Bearer Token Header in Javascript
How to send bearer token through header of axios call in react redux
You can try to pass the token as first argument of your thunk function ( currently "_") to make sure it is not undefined. Also, you can use a debugger to know the actual state of the global store (or simply log it in your component).
And lastly, try to log what returns the thunkApi.getState() function.
I am working on one MERN application and using Redux toolkit to handle asynchronus task on react side.
I noticed that whenever I tried to sign-in in app with invalid credential, createAsyncThunk is unable to dispatch rejected action.
However, in network request I am getting error response with status code 404, but I am getting payload undefined in rejected lifecycle.
Is there any solution to fix that problem with createAsyncThunk ?
controller(sign-in) :
export const signin = async (req, res) => {
const { email, password } = req.body;
try {
const oldUser = await UserModal.findOne({ email });
if (!oldUser)
return res.status(404).json({ message: "User doesn't exist" });
const isPasswordCorrect = await bcrypt.compare(password, oldUser.password);
if (!isPasswordCorrect)
return res.status(400).json({ message: "Invalid credentials" });
const token = jwt.sign({ email: oldUser.email, id: oldUser._id }, secret, {
expiresIn: "1h",
});
res.status(200).json({ result: oldUser, token });
} catch (err) {
res.status(500).json({ message: "Something went wrong" });
}
};
authSlice.js :
import { createAsyncThunk, createSlice } from "#reduxjs/toolkit";
import axios from "axios";
import * as api from "./api";
export const login = createAsyncThunk(
"auth/login",
async (formValue) => {
const response = await api.signIn(formValue);
return response.data;
}
);
const authSlice = createSlice({
name: "auth",
initialState: {
user: {},
error: "",
loading: false,
},
extraReducers: {
[login.pending]: (state, action) => {
state.loading = true;
},
[login.fulfilled]: (state, action) => {
state.loading = false;
state.user = action.payload.result;
},
[login.rejected]: (state, action) => {
state.loading = false;
state.error = action.payload.message;
},
},});
export default authSlice.reducer;
Unable to get payload in case of failure
But getting response in network tab
I have a react-redux app. I need to call API and used it in my component. The app is called with fetch in function in utills.
All functions are group and export like this:
export const sportTeam = {
getBasketballTeam,
getBasketballTeamById,
}
function getBasketballTeam() {
let token = store.getState().UserReducer.token;
fetch(
actions.GET_BASKETBALLTEAM,
{
method: "GET",
headers: { Authorization: `Bearer ${token}` },
}
)
.then((res) => {
if (res.status == 200 ) {
return res.json();
}
})
.then((response) => {
console.log(response);
})
.catch((err) => {
console.log(err);
});
}
getBasketballTeam contains an array of objects.
How can I get getBasketballTeam and used it in the component in the view to returning the list with this data?
You don't want your getBasketballTeam function to access the store directly through store.getState().
What you want is a "thunk" action creator that gets the store instance as an argument when you dispatch it.
The flow that you want is this:
Component continuously listens to the basketball team state with useSelector (or connect).
Component mounts.
Component dispatches a getBasketballTeam action.
Action fetches data from the API.
Reducer saves data from the action to the state.
State updates.
Component re-renders with the new data from state.
The easiest way to do this is with the createAsyncThunk function from Redux Toolkit. This helper handles all errors by dispatching a separate error action. Try something like this:
Action:
export const fetchBasketballTeam = createAsyncThunk(
"team/fetchBasketballTeam",
async (_, thunkAPI) => {
const token = thunkAPI.getState().user.token;
if ( ! token ) {
throw new Error("Missing access token.");
}
const res = await fetch(actions.GET_BASKETBALLTEAM, {
method: "GET",
headers: { Authorization: `Bearer ${token}` }
});
if (res.status !== 200) {
throw new Error("Invalid response");
}
// what you return is the payload of the fulfilled action
return res.json();
}
);
Reducer:
const initialState = {
status: "idle",
data: null
};
export const teamReducer = createReducer(initialState, (builder) =>
builder
.addCase(fetchBasketballTeam.pending, (state) => {
state.status = "pending";
})
.addCase(fetchBasketballTeam.fulfilled, (state, action) => {
state.status = "fulfilled";
delete state.error;
state.data = action.payload;
})
.addCase(fetchBasketballTeam.rejected, (state, action) => {
state.status = "rejected";
state.error = action.error;
})
);
Store:
export const store = configureStore({
reducer: {
team: teamReducer,
user: userReducer,
}
});
Component:
export const BasketballTeam = () => {
const { data, error, status } = useSelector((state) => state.team);
const dispatch = useDispatch();
useEffect(
() => {
dispatch(fetchBasketballTeam());
},
// run once on mount
// or better: take the token as an argument and re-run if token changes
[dispatch]
);
if (status === "pending") {
return <SomeLoadingComponent />;
}
if (!data) {
return <SomeErrorComponent />;
}
// if we are here then we definitely have data
return <div>{/* do something with data */}</div>;
};
After you get response you need to do the following things
call dispatch function to store the data received in REDUX state.
Now when you have data in redux state, you can use useSelector() to get that state and make use of it in your jsx file.
I am working on authentication using Auth0 and react. I am using loginWithPopup() for the login popup screen. But every time I end up getting misconfiguration error(like you can see in the attachment). But if I remove the response_mode = web_message from the URL it works, is there any way to remove response_mode from code. I am using the react-auth0-spa.js given my auth0 quick start
import React, { Component, createContext } from 'react';
import createAuth0Client from '#auth0/auth0-spa-js';
// create the context
export const Auth0Context = createContext();
// create a provider
export class Auth0Provider extends Component {
state = {
auth0Client: null,
isLoading: true,
isAuthenticated: false,
user: false,
};
config = {
domain: "dev-ufnn-q8r.auth0.com",
client_id: "zZh4I0PgRLQqLKSPP1BUKlnmfJfLqdoK",
redirect_uri: window.location.origin,
//audience: "https://reachpst.auth0.com/api/v2/"
};
componentDidMount() {
this.initializeAuth0();
}
// initialize the auth0 library
initializeAuth0 = async () => {
const auth0Client = await createAuth0Client(this.config);
const isAuthenticated = await auth0Client.isAuthenticated();
const user = isAuthenticated ? await auth0Client.getUser() : null;
this.setState({ auth0Client, isLoading: false, isAuthenticated, user });
};
loginWithPopup = async () => {
try {
await this.state.auth0Client.loginWithPopup();
}
catch (error) {
console.error(error);
}
this.setState({
user: await this.state.auth0Client.getUser(),
isAuthenticated: true,
});
};
render() {
const { auth0Client, isLoading, isAuthenticated, user } = this.state;
const { children } = this.props;
const configObject = {
isLoading,
isAuthenticated,
user,
loginWithPopup: this.loginWithPopup,
loginWithRedirect: (...p) => auth0Client.loginWithRedirect(...p),
getTokenSilently: (...p) => auth0Client.getTokenSilently(...p),
getIdTokenClaims: (...p) => auth0Client.getIdTokenClaims(...p),
logout: (...p) => auth0Client.logout(...p)
};
return (
<Auth0Context.Provider value={configObject}>
{children}
</Auth0Context.Provider>
);
}
}
After a bit of research, I found an answer to my own question. So if we use response_mode = web_message then we need to configure our callback URL in allowed web origin field as well. In my case, I am using loginWithPopup() so which typically adds response_mode = web_message in the login URL because loginWithPopup() from auth0 SDK is a combination of PKCE + web_message
https://auth0.com/docs/protocols/oauth2 (under how response mode works?)
https://auth0.com/blog/introducing-auth0-single-page-apps-spa-js-sdk (under behind the curtain)
OS : Window 10
IDE TOOLS : VSC
node : v12.14.1
Hi i'm not good at English. so my expressions will be little bit awkward.
I'm using Spring boot REST API and client-side is react.js
I'm trying to use refresh Token, Access Token with jwt.
What i want to do is,
Before calling rest api, If accessToken is invalid with timeout in client side,
get Requesttoken in localStorage and send it to serverside and reinssuance accessToken and refreshToken.
And store it again. Then i call rest api what i want to call it first.
Here is my question.
Is it possible that Rest api has if statement ?
api.js
const getAccessToken = () => {
const accessToken = sessionStorage.getItem('accessToken');
if (!accessToken) {
window.location.href = "http://localhost:3000";
return alert('Login first');
} else if (accessToken && !validateToken()) {
// ~~~~ Here is what i want to ask~~~~
is it possible in react.js???
const refreshToken = localStorage.getItem("refreshToken");
getAtWithRefreshToken(refreshToken);
sessionStorage.setItem('')
return accessToken;
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
} else if (accessToken && validateToken()) {
console.log("token is Valid~~~");
return accessToken;
}}
export const getBoardList = (searchType = '', searchString = '', page) =>
axios.get("http://localhost:8080/jpa/board/",
{
params: {
searchType,
searchString,
page
},
headers: {
'Authorization': getAccessToken()
}
}
)
This is my first Question in StackOverFlow... Please Let me know in the comments if there is anything I need to explain.
Sorry you for you that spend many time in my promiscuous question.
Hope you guys always in healthy.
+ api.js
getAtwithRefreshToken
export const getAtWithRefreshToken = (refreshToken) =>
axios.post("http://localhost:8080/jpa/system/getat",
{
refreshToken
}
)
and in module,
export default handleActions({
..(another pender)....
...pender({
type: GET_AT, // getAtWithRefreshToken
onPending: (state, action) => {
return state; // do something
},
onSuccess: (state, action) => {
const result = action.payload.data.data;
sessionStorage.setItem('role', result.role);// role : [ROLE_ADMIN]
sessionStorage.setItem('accessToken', result.accessToken);
sessionStorage.setItem('memberId', result.memberId); // id : admin
localStorage.setItem('refreshToken', result.refreshToken);
return state
},
onFailure: (state, action) => {
alert(action);
console.log(action);
return state; // do something
}
}),
..(another pender)....
, initialState);
and in container, i uses terrible thing like....
getBoardList = async (searchType, searchString, page, size, direction) => {
this.getAccessToken();
const { boardActions } = this.props;
try {
this.getAccessToken();
await boardActions.getBoardList(searchType, searchString, page, size);
} catch (e) {
console.log("error log :" + e);
}
this.getBoardCount(searchType, searchString);
}
and my page shows
Unhandled Rejection (InvalidTokenError):
Invalid token specified: Cannot read property 'replace' of undefined
such a mess. my brain stopped... :(