I've built a JWT API for user authentication. I want to use it with react. my app has these routes:
Node js API Routes:
http://localhost:8080/api/auth/signin
-> this route accepts username and password from react and sends back an access token to react which it will save it inside localstorage(I'm planning to use memory instead) and cookie containing refresh token with httpOnly enabled.
http://localhost:8080/api/auth/signup
-> this route will add a new user to database based on user input(doesn't relate to authentication process)
http://localhost:8080/api/auth/refreshtoken
-> this route will return a new access token based on the sent refresh token from client which was saved inside cookies. and then client will replace it with the expired access token.
http://localhost:8080/api/test/user
-> this will check if user is signed in with the access token from client and will send the user data back
React Client Routes:
http://localhost:3000/login
-> a route for sending user information for logging in
http://localhost:3000/register
-> a route for creating a new user
http://localhost:3000/user
-> a protected route which needs user to be logged in to view data.
when I log in to my website I can see the data inside the user route for 20 seconds before the access token expiry. when the access token is expired and I do a new request the server will response with 401 unauthorized code. so this way I can make sure that the access token is expired and I need to use refresh token. I've done that like this:
const API_URL = "http://localhost:8080/api/test/";
const getUserBoard = () => {
return axios.get(API_URL + "user", { headers: authHeader() })
.catch((error) => {
if(error.response.status === 401) {
// if error response status was 401 then request a new token
authService.refreshToken();
window.location.reload();
}else if(error.response.status === 403) {
authService.logout();
window.location.href = "/login";
}
});
};
code to get new access token with refresh token:
const refreshToken = () => {
axios({
url: API_URL + "refreshtoken",
method: "POST",
withCredentials: true
})
.then((response) => {
if(response.data.accessToken) {
const user = JSON.parse(localStorage.getItem("user"));
user.accessToken = response.data.accessToken;
localStorage.setItem("user", JSON.stringify(user));
}
})
.catch((error) => {
console.log("server: " + JSON.stringify(error.response));
})
}
code to set header for receiving data from server using access token:
export default function authHeader() {
const user = JSON.parse(localStorage.getItem('user'));
if (user && user.accessToken) {
// for Node.js Express back-end
return { 'x-access-token': user.accessToken };
} else {
return {};
}
}
the first part for getting new access token on 401 error works good but my refresh token also expires after 40 second. if I send the expired refresh token it won't return a new access token. and because of that again I will receive a 401 error which again will cause a 403 error and here I'll get stuck in an infinite reload loop.
any idea? how can I control tokens expiry?
You're issuing tokens in your node.js app, right? So that is where you should adjust the expiration time of the token. The code which issue tokens should have an option to set the expiration time.
Remember that once the refresh token is expired you should log in again. You can implement something which is called a rolling refresh token. So whenever you call the /api/auth/refreshtoken
endpoint you can also issue a new refresh token, with a new expiration time and return it in a cookie.