I am fetching user data from the graphql backend through the apollo client. My goal is to keep the user logged in, so I signed jwt token with all user data, passed it to localStorage(only the token), decoded it, and passed all values to redux-store.
UserModel:
userSchema.methods.createJWT = function (payload) {
return jwt.sign({ ...payload }, process.env.JWT_SECRET, {
expiresIn: '1d',
});
};
userLogin:
await user.save();
return {
...user._doc,
id: user._id,
token: user.createJWT(user._doc),
}
reduxSlice
const userSlice = createSlice({
name: 'user',
initialState: {
userInfo: localStorage.getItem('jwtToken')
? jwtDecode(localStorage.getItem('jwtToken'))
: null,
},
reducers: {
loginUser: (state, action) => {
localStorage.setItem('jwtToken', action.payload.token);
state.userInfo = action.payload;
},
I am wondering if this is ok that the token is holding too much info like:
{
"_id": "62a9ee3878c4979fedb471c5",
"username": "***",
"email": "***",
"password": "$2a$12$hN2lfCtEbqOOFSlHpapyfuxYAHdEGUYKeHY4BMK1YvYOtSG7zHwcS",
"isAdmin": false,
"shippingAddress": [],
"createdAt": "2022-06-15T14:35:36.877Z",
"updatedAt": "2022-06-16T09:04:59.367Z",
"__v": 0,
"firstName": "***",
"lastName": "***",
"shoeSize": 4,
"iat": 1655371413,
"exp": 1655457813
}
There is another effective way to save user data and keep him logged in?
it's not recommended (actually very dangerous) to return all information with the jwt token especially password. I think userId is enough! But you can also return username, firstName, lastName, etc. But in some cases even returning email address is not a good approach for some user privacy reasons.
I mean by that you have only to get the userId once there is a user and the credentials are correct, then :
const userToken = {
userId: user._id,
username: user.username,
};
return {
user,
token: user.createJWT(userData)
};
Now after signing the jwt token , you can set whatever data from user inside some redux state or even react context , (choose your favorite) , but DON'T set any password in the localStorage.
Update: at the end you should store the user from the payload like this :
state.userInfo = action.payload.user;
Btw you should check the localStorage only to get the token and verify it , then based on userId you need to fetch the user and store it, here is an example :
const getUser = React.useCallback(async (userId) => {
try {
const res = await axios.post('/auth/login', {userId}, {
credentials: 'include',
withCredentials: true
});
const { accessToken, user } = res.data;
setState((currentState: IState) => ({
...currentState,
user,
loading: false,
isAuth: !!accessToken,
accessToken
}));
} catch (err) {
setState((currentState: IState) => ({
...currentState,
user: null,
loading: false,
isAuth: false,
accessToken: ''
}));
}
}, []);
useEffect(() => {
getUser(userId);
}, [getUser]);
useEffect(() => {
const jwtToken = localStorage.getItem('jwtToken');
if (jwtToken && jwt_decode(jwtToken)) {
const { exp } = jwt_decode(jwtToken);
const currentTime = Date.now() / 1000;
if (exp < currentTime) {
getUserById(userId);
}
}
}, [getUser]);