could someone help me to resolve this problem?
I don't know how to implement expired session to my react app.
I have json data with expires_in: 86400 what I need to do to implement this to my app, when expired to logout user.
I using react.JS and redux.
Action:
export const signin = obj => {
return dispatch => {
let data = {
method: "post",
url: "/oauth/token",
data: {
grant_type: "password",
client_id: CLIENT_ID,
client_secret: CLIENT_SECRET,
username: obj.email,
password: obj.password
}
};
return API(data)
.then(res => {
window.localStorage.setItem("access_token", res.data.access_token);
console.log('uuuuuuuuuu', res)
dispatch({
type: AUTH.LOGGED_IN,
payload: {
access_token: res.data.access_token,
expired_in: res.data.expires_in
}
});
})
.catch(err => {
throw err;
});
};
};
You can simply achieve this using setTimeout function to trigger your logout functionality. The below code is on the assumption that expires_in is the relative time not the absolute time.
window.localStorage.setItem("access_token", res.data.access_token);
setTimeout(logoutFunction, response.data.expires_in)
console.log('uuuuuuuuuu', res)
dispatch({
type: AUTH.LOGGED_IN,
payload: {
access_token: res.data.access_token,
expired_in: res.data.expires_in
}
});
and your logoutFunction will look something like this:
function logOutFunction() {
window.localStorage.removeItem("access_token");
// your redux dispatch and probably a redirection logic to go to a proper UX page
}
I mistakenly said in comments to use setInterval. I assume this will be a one time execution, setTimeout is better to use.
From your code I found. you have used window.localStorage.setItem("access_token", res.data.access_token);
But you actually need a cookie there and set the expire time from the response. Then you have to check is that the cookie existence in the parent component. if it, not exist (expired) you can redirect to the login page.
For force logout, you can simply make the cookie expire. ;)
You shouldn’t store the time left to expire, but the current time + the time left. Otherwise you can’t know when this happened.
Related
I am working with React hooks and created login logout functionality, but now I want user to logout when token is expired.
I am on a welcome page and using it, after two minutes if I do something else and the token is expired, I want to logout the user
I have created a context, Auth context where I have login logout context
I just want to call the logout function whenever the token expires
My authContext
const initialstate = {
user: null,
};
if (localStorage.getItem("JWT_Token")) {
const jwt_Token_decoded = Jwt_Decode(localStorage.getItem("JWT_Token"));
console.log(jwt_Token_decoded.exp * 1000);
console.log(Date.now());
if (jwt_Token_decoded.exp * 1000 < Date.now()) {
localStorage.clear(); // this runs only when I refresh the page or reload on route change it dosent work
} else {
initialstate.user = jwt_Token_decoded;
}
}
const AuthContext = createContext({
user: null,
login: (userData) => {},
logout: () => {},
});
const AuthReducer = (state, action) => {
switch (action.type) {
case "LOGIN":
return {
...state,
user: action.payload,
};
case "LOGOUT":
return {
...state,
user: null,
};
default:
return state;
}
};
const AuthProvider = (props) => {
const [state, dispatch] = useReducer(AuthReducer, initialstate);
const login = (userData) => {
localStorage.setItem("JWT_Token", userData.token);
dispatch({
type: "LOGIN",
payload: userData,
});
};
const logout = () => {
localStorage.clear();
dispatch({ action: "LOGOUT" });
};
return (
<AuthContext.Provider
value={{ user: state.user, login, logout }}
{...props}
/>
);
};
export { AuthContext, AuthProvider };
In the above code I am checking for expiry of token, but that only runs when page reloads, here I want to run it in every route change so that I can check for the token expiry.
I don't know how to do that and where to do that.
To logout I just need to call my logout context function, but I don't understand how to make the call.
I don't know If I have to do something in my Axios instance which is a separate file like below. Here I am creating one instance so that I can define my headers and other stuff at one place.
//global axios instance
import axios, { AxiosHeaders } from "axios"; // import axios from axios
const BASE_URL = "https://jsonplaceholder.typicode.com"; // server api
export default axios.create({
baseURL: BASE_URL,
headers: {
"Content-Type": "Application/json",
Access-token: "token here",
},
});
How can I approach this problem? I checked this question but in this example GraphQL has been used, so there is a function to set context where I can pass and use the store to dispatch my logout.
I have shared my axiosInstance code I think something needs to be added there. I am ready to use any approach that will generate some middleware, so that I can check for token in one place.
There are some points to consider in your approach
You can update the log out function to send the user to the login page, this way you guarantee this behavior whenever this user has been logged out;
Use the API response to validate when your token has expired, this way you don't need to keep watching the time of the token expiration and logout the user after any unauthorized API call;
Pay attention to this localStorage.clear() call, sometimes it can clear more than you want, and it is always a good idea to declare what you really want to clear. Eg.: sometimes you want to keep your user language, theme, or some UI configs that shouldn't be cleared after a logout;
Axios has an API to intercept your API call, this is a good way to do something before/after an API call (use it to logout after any unauthorized response)
If you really need to take care of this logout by the client and don't need to await the API response, you can try the setTimeout method (not recommended)
I'm practicing login using react and node.js. but have a problem. My jwt token is not receivable from backend.
I watched the video on YouTube and followed it. link is here (sadly the youtuber doesnt have code in github:()
I think nodejs code doesn't have any problem. because I checked token by console.log no matter client receive token or not.
export const loginUser = (email, password) => async dispatch => {
try {
const config = {
headers: {
'Content-Type' : 'application/json'
}
}
const body = JSON.stringify({email, password})
const response = await axios.post('http://localhost:4000/app/signin', body, config)
dispatch({
type: LOGIN_SUCCESS,
payload: response.data
})
dispatch(loadUser());
} catch (error) {
dispatch({ type: LOGIN_FAIL, payload: error })
}
}
I receive token using this code, it sometimes bring token or not. I also use async and await, but the problem always happen. When I use console.log to check response data it says undefined.
I checked code but think there is no mispelled.
I'm also using redux to login. weirdly, when I try to login, it appears new tab exatly same login page. The problem also happening irregularly. And I use redux extensional devtools with chrome. When I try to login, redux states always initialized.
I have no idea why this happening. And when I failed to login, beginning append with '?' after url. like this.
I know it's hard to guess why this happening.
To see my code detail
Thank you for read this.
In my react web app, login-logout functionality is implemented using context-API and hooks. Everything seems to work fine except for the fact that I have to click the 'Login' button twice to push the user to the dashboard.
The statement, return <Redirect to="/dashboard /> does not work, instead I have to use history.push('/dashboard'), which does not seems to be a better option to me while login.
Here is the working snippet :
https://codesandbox.io/s/wizardly-worker-mqkex?file=/src/AuthContext.js
Also, I need some suggestions for the best practise to fetch the logged user details in other components. Using localstorage or global context API state, which of them serves as the best option for the same ?
Any help to resolve this, appreciated :)
Well, it boils down to the simple fact that your context is not updated when you do your check. The simplest solution would be to remove the check for isLoggedIn and push the user to the Dashboard:
const postLogin = async (e) => {
e.preventDefault()
try {
await login()
props.history.push('/dashboard')
} catch (error) {
dispatch({
type: 'LOGIN_FAILURE',
payload: 'Unable to login'
})
}
}
Then throw an error in your login function when you don't get a 200 back:
const login = async () => {
const loginUser = { status: 200, id: '3654634565659abhdgh' }
if (loginUser.status === 200) {
dispatch({
type: 'LOGIN_SUCCESS',
payload: loginUser.id
})
} else {
throw new Error("Invalid Credentials")
}
}
As your login code is in a try catch block, the user won't get pushed to the Dashboard when the login fails.
I have successfully created an API that uses passport-google-oauth to return a JWT. Currently when I go through the process using my API routes it returns a json object with a JWT Bearer token.
I am attempting to use Reactjs on the front end however am running into a couple issues.
In my signin button component I am just trying to retrieve the result with the bearer token to pass it into a reducer
When using Axios -> I am running into a CORS issue when using exios and cant return a result, when adding CORS into my build and a proxy to my react project I recieve the following error No 'Access-Control-Allow-Origin' header is present on the requested resource.
When I use a anchor tag with href link the authentication successfully works however it redirects to the /api/auth/google/callback link itself instead of allowing me to catch the bearer token and then run it through my reducers to save it into local storage and update my state.
Am I missing a step? Ive looked for a few hours at various resources online and cant seem to find the solution im looking for
React
(for simplicity at the moment I am just trying to catch the response, which should be returned bearer token, however I am unable to do this)
googleAuth = (e) => {
e.preventDefault()
axios.get('/api/auth/google')
.then(res => console.log(res))
.catch(err => console.log(err))
}
render() {
return (
<button onClick={this.googleAuth}>Signin With Google</button>
)
}
API
Routes
router.get('/google', passport.authenticate('google', {
session: false,
scope: ['profile', 'email']
}))
router.get('/google/callback', passport.authenticate('google', { session: false }), generateUserToken)
Strategy
passport.use(new passportGoogle.OAuth2Strategy(googleConfig, async (request, accessToken, refreshToken, profile, done) => {
// Check for existing user
const existingUser = await User.findOne({
providers: {
$elemMatch: {
provider: 'Google',
providerId: profile.id
}
}
})
// If user exists return done
if (existingUser) return done(null, existingUser)
// If user does not exist create a new user
const newUser = await new User({
name: profile.displayName,
providers: [
{
provider: 'Google',
providerId: profile.id
}
]
}).save()
// Create profile with new user information
const newProfile = await new Profile({
userId: newUser.id
}).save()
return done(null, newUser)
}))
I've looked a bit at your code and haven't seen any serializing/deserializing going on. Usually you'd have to go through this process to be able to connect with whatever authentication strategy you are using.
Here is a snippet of code that you could use in the file you keep your strategies:
passport.serializeUser((user, done) => {
done(null, user.id);
});
passport.deserializeUser((id, done) => {
User.findById(id).then(user => {
done(null, user);
});
});
Maybe you could look it up in more detail in the documentation. Here is a link http://www.passportjs.org/docs/ go to the sessions part. Plus, make sure to look at how the app.use is put together with the .session() func.
Below an example what's goes wrong in my app that I can't figure out how to fix it and why it's actually happening.
I made a simple API call to fetch some data
dispatch(isLoadingData(true));
api({
method,
url,
headers: { Authorization: `JWT ${APP.token}` }
})
.then(async (response) => {
return resolve(dispatch(await updateData(response)));
}).catch(reject);
}).catch(async (err) => {
dispatch(isError(err));
dispatch(await isLoadingData(false));
throw err;
});
if API response has 401 status code that it initiates logout process by calling the next action.
case LOGOUT_USER: {
Actions.main({ type: 'reset' });
return {
...INITIAL_STATE,
};
}
that restores the user reducer to
{ user: '', token: '' };
(It calls before Promise returns a response in the upper function). So the sequence looks like this
1) isLoadingData(true)
2) logoutUser() (this clears user data)
3) isError(err) (this again has user and token)
4) isLoadingData(false)
As a result, the user can't be logging out.
just add another
.then(response => dispatchActionAfterActualLogoutWasMade())
so that you dispatch the action after the initial logout request was completed
that will send an action to reducer to clear the app state after you already received the response from the server.