I have an app that has OAuth2 implemented. It works fine, but I'm confused with refresh_tokens here. My app is using React + Redux combination.
I know I need to check if my access_token expired and then ask for new one using refresh_token. Okay... But when should I refresh it? After the 401 happened or in time when some API request that needs authorization is ready (just before send)?
I also know how to use HTTP interceptors to get all API requests before send or detect the 401 responses.
The problem is I'm confused how to solve the problem in Redux flow. How to "freeze" a request for the time the token is being refreshed? Or how to repeat the request when I solve the problem on 401 response?
When first authenticating with server successfully, we will have {access_token, expires_in, refresh_token}. We will store access_token and expires_in in session storage and refresh_token in local storage.
Whenever getting access_token for fetching data (you should enhance/wrap the fetch with filling access_token), you'll check if the expires_in is due soon (less than 1 minute, or what you feel comfortable), then we use the refresh_token to request new access_token. And of course, we have to update new result to the storage as well.
Pseudo code:
function getToken() {
const expiresIn = storage.getItem(KEY_EXPIRES_IN);
const accessToken = storage.getItem(KEY_ACCESS_TOKEN);
if (accessToken
&& (!expiresIn || moment.unix(Number(expiresIn)).diff(moment(), 'minute') > 1)) {
return accessToken;
}
return refreshToken();
}
Above function will be called every time you make a request to server.
P/S: you may want to make it as promise to use with isomorphic-fetch
Related
I'm using React on frontend and Node on backend. How I structure the authentication is, when a user logs in, a JWT token is created and stored into the user data model. That token then get stored into cookie (instead of Localstorage).
Localstorage, on the other hand, is used to store user info such as username, name, and email.
When a user logs out, the JWT token is removed, and so is the cookie and userinfo in Localstorage. I designed my React so that if there's no userinfo in Localstorage, then the private routes would become inaccessible to users.
When a user logs out from all devices, all JWT tokens are removed (but cookie and Localstorage on other devices are not, since they are local).
However, this approach results in some problems:
since removing all JWT tokens doesn't remove the userinfo from Localstorage, the private routes can still be viewed, which is undesirable
after all JWT tokens are removed, even if a user wants to log out from another device, they can't, because now without any token, the user can't pass the authentication.
What would be a better way to structure the logOutAll component?
In order to logout the user if there isn't any JWT available, using Axios interceptor you can catch the response error and status. So in your case the API will response with status code 401 or any other status code you're sending. If the API fails with 401 status you could add your logout logic.
Here's the code snippet using Axios interceptor, hope this helps.
axios.interceptors.response.use(
(response) => response,
(error) => {
const { status } = error.response;
if (status === 401) {
// your logout logic goes here
}
return Promise.reject(error);
}
);
I'm developing website like classified ads with Django REST framework, react and redux. I have a question about authentication with JWT.
I want to use djangorestframework_simplejwt for authenticate and I've checked a few tutorial. I saw that many tutorial are checking access token on client side like below
export function isAccessTokenExpired(state) {
if (state.access && state.access.exp) {
return 1000 * state.access.exp - (new Date()).getTime() < 5000
}
return true
}
and refresh token as well.
But I don't know why. Because just request new access token with refresh token every time we got HTTP 401 Unauthorized error with expired access token.
The workflow that I thought is
Send server a request with access token to get page which only
authenticated user can see.
If access token is expired, frontend
will get HTTP 401 Unauthorized error.
Send server a request with
refresh token to get new access token, then frontend will store it
to localStorage.
Send a request again.
Is this bad way?
My apologies with my poor English...
You shouldnt be checking the JWT on the client side. A JWT is basically a token that the server has given you that is "assumed" valid. When you send the token back, the server will tell you if the token is not valid in the form of Http Status Code 401 - Unauthorized
I am implementing JWT in my project. I implemented jwt and gave it an expiration time of 1 minute. The jwt that is generated from the api side is during login and the token and expiration details are sent in the result and are stored in local storage. How can I refresh the expired token from API side and send it back again to the client so that it can be stored in local storage and sent for every call using interceptor?
this is how I created jwt and gave expiration time
// let us suppose this is my input
tokenObject = { User: { username: name, pwd: pwd } };
//creating a jwt here
jwt.sign({ tokenObject }, "secretkey", { expiresIn: "60s" }, (err, token) => {
res.json({
token
});
});
After this, I'm verifying the token in the result and sending it in result to the client.
After a minute how do I regenerate the token?
Please help and let me know the way and tell me if I am doing something wrong . Thanks!!
You need to add a function or middleware that will check that the JWT is valid or not. You can use the verify method of JWT library:
jwt.verify(token, 'secretKey', function (err, decoded) {
if (err) {
if (err.name === 'TokenExpiredError') {
//create a new token and send the same way you created initially
}
}
});
You can create an API that accepts a JWT token, validates it and then issues a new token for the same user.
Take a look at the verify method of jsonwebtoken.
While verifying the token you can use ignoreExpiration: true option to verify the expired token as well. Then then generate the new one using sign method.
So while making the request from the frontend, if you get a token expired error, then you issue a new token using that API and save that token.
With that said, I do recommend you to take a look at the note about refreshing the JWT token from the docs:
First of all, we recommend to think carefully if auto-refreshing a JWT
will not introduce any vulnerability in your system.
We are not comfortable including this as part of the library, however,
you can take a look to this example to show how this could be
accomplished. Apart from that example there are an issue and a pull
request to get more knowledge about this topic.
Here is the link of the gist that has the code.
I'm using the following combination of packages:
react
redux
react-cognito
react-router (v4)
redux-saga
(and I'll disclaim that I'm pretty new with all of these)
Currently I have a PrivateRoute component which will check (and refresh if necessary) an expired IdToken on route changes. This works well.
My problem is if the browser is open past token expiry to a PrivateRoute which is polling my API and sending along the IdToken in its 'Authorization' header. The API will start returning 401.
Options I've Thought of:
Act on API Error
I could catch the 401 error and dispatch an action to refresh the token, but
if there is any issue refreshing the token I wind up in an infinite loop hammering AWS, so need some logic to catch and prevent this. Perhaps redirect to login route if refresh fails?
I then need to add complexity to all my private API calls throughout my app to have this logic, and re-do the requested API call upon successful refresh.
Pre-empt API Error
To me it makes more sense to separate API calls and keeping the auth token valid. Considering react-cognito stores the token expiry time in cognito.user.signInUserSession.idToken.payload.exp, maybe it is possible to pre-empt the API call and expiry.
How to best do this though? At login a refresh action could be 'scheduled' using setTimeout for (currentTime - expiryTime - someBuffer) seconds in the future.
I'm assuming (haven't verified) AWS will let you refresh an IdToken before it expires. I don't want to wait until afterwards else some API calls may have already failed.
are there concerns with using setTimeout with a timeout that may be up to 1 hour long?
Alternatively I could set something up to poll cognito.user.signInUserSession.idToken.payload.exp frequently to detect and refresh an almost-expired token?
Any suggestions?
Lets say I have a method called getUsers and an API Interceptor which handles my token refresh functionality.
Here is the scenario:
I send a GET request using getUsers to:
http://example.com/api/users
My token is expired so I get a 401 error
API Interceptor refreshes my token and calls the endpoint again:
response.config.headers.Authorization = "Bearer " + response.access_token;
$http(response.config);
It works fine, but how do I actually re-use my original method (getUsers) instead of just resending $http request because I need to modify some data on callback
What really worked in my case is actually intercepting requests, not responses. So basically I have a timestamp of my token creation in my local storage which I compare to current time every time I send an API request. If it's been longer than say 10 hours (insert your lifespan) I request a new token.