Log out from all tabs/devices using React, JWT and cookie - reactjs

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);
}
);

Related

React Query to store global state for user?

Is there a way to use react-query to store global state for user, if user is logged in or not?
Right not currently I am only storing cookie as bearer token and refresh token
And I am forcing react query to hit an API endpoint that checks if user has valid bearer token
So right now its making unnecessary requests and getting failed error response if use is not logged in.
What can I do to store user info when user is logged in, so that I don't have to make unnecessary requests to /verify endpoint?
There are several options to do it. But most common are:
Keep auth data in localStorage and before API call check if the authToken is not expired.
Keep auth data in the cookie and do the same
This will help you to avoid unnecessary requests and you will be able to make requests even after page reload/closing the browser tab

Misunderstanding the process of JWT authentication

I create project using React + Redux + Apollo Client + Graphql
When we need to log in in our app we need to use token (saved in localStorage for example) which is put in the headers parameter like in the code below:
const client = new ApolloClient ({
uri: 'http://localhost:4000/api',
headers: {
authorization: `Bearer ${localStorage.token}`,
},
});
After request server verifies token and becomes aware who is the user.
My question: from where do we need to get token and put it to the headers parameter for log on (sign up) process? A new customer comes to our log on page, he has no token (in localStorage or somewhere else) at the beginning but server requires it in the requests. And if we remove headers parameter from our client, the log on process will proceed but server won't understand who is the current user.
Typically the server would be the one issuing the JWT token, and this would happen during user login or maybe during account creation. For these particular calls, you should not be expecting the JWT in the header. Instead, the user would be passing credentials, such as username and password. For most other calls, it is appropriate to pass the JWT in the header of the request.
Keep in mind that the main purpose of the JWT is free the user from having to provide personal credentials during most requests. Instead, the user can just present a JWT, much as one would present a passport, to get access to your web services.
In response to your comments below, I would suggest that you keep the signup/registration process separate from the user-only area of your application. Here is a typical workflow:
Prospective user visits your site, and creates an account, by choosing a username and password, and possibly by providing certain other personal information
Your application creates an account, and then sends an email verification link to the user's email address. The server lands the user on a page which mentions all of this
The user opens the email, which contains a verification link, which when clicked will activate the account. Your application returns a web page which then asks the user to login.
Finally, the user logs in from the normal login page.
Note carefully here, that JWT were not at all involved in the signup process, nor do they need to be. The user JWT only needs to come into existence after the user actually logs in for the first time.
Decision:
you need to check for token in localStorage and update the request if token exists
const client = new ApolloClient({
uri: 'http://localhost:4000/api',
request (operation) {
const headers = {};
const token = localStorage.getItem('token');
if (token) headers.authorization = 'Bearer ' + token;
operation.setContext({ headers });
}
})

how to refresh token when expired in jwt

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.

How to security save Facebook response token to React app?

I'm developing an React app using facebook login, after login success, facbook return a list of pages that user current manager, includes tokens. These tokens I will use in future in my app. This is response I want to save:
[
{
// page 1
access_token: "this_is_token",
category: "Community",
id: "461787374317076",
name: "page_name",
......
},
{
// page 2
},
....// other pages
]
But I don't know how to save this response in my app for security. which is best secure to save? Redux state (not secure), Redux store? Cookies? localstorage? or inmemory?
In my app, I'll use these tokens many times, so I think should dispatch them to Redux store, but does it secure?
Thanks you very much!
Token that you receive do not have password inside. You are saying if you can store it any where?
The website we are on now (stackoverflow) also store that token in local-storage
You can see in this pic they are storing token in localstorage.
is it safe ?
Yes as it do not have any important data like password or credit card number. More over if you as user have this token on your computer then you will not give this string to some other people to access your account (it is like you are giving somebody your password which is not developers fault) and this string is too large that no other can create acceptable string by own.
redux / cookies / localstorage ?
The way I use it is . On signIn/signUp I will set it in localstorage and request headers and after refresh i will check first if token is present in localstorage or not if yes then will need to set headers again as on refresh they will lost.
By axios you can easily set headers like this. Then on server you need to get headers from request and verify your token.
import axios from 'axios'
export default function setAuthToken(token){
if(token){
axios.defaults.headers.common['autherization'] = `Bearer ${token}`
}
else{
delete axios.defaults.headers.common['autherization']
}
}

Using OAuth2 refresh token in React Redux app

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

Resources