Part-1 we implemented user login and logout using the HttpOnly Jwt cookie in our Vue application. In this article, we are going to understand the refresh token(refresh token also stored in HttpOnly Jwt Cookie).
Gaurd Routes:
One of the important cases we always need to consider while implementing authentication is guard routes between user authenticated and non authenticates cases.
Suppose an authenticated user can't access the login page. Similarly, a non-authenticated user can't access the dashboard page(any page that needs user authentication). So to fix this kind of case we have to define our navigation rules at our route guard.
But before implementing our route guard logic, we need to set one flag in browser local storage. The flag we will add represents that the user is authenticated. This flag helps us to avoid unnecessary calls to the profile API if the user reloads the application.
Now let's add the flag to browser local storage on user authenticated. So let's update the store action method 'loginApi'.
src/store/module/auth.js:
const actions = { // code hiden for display async loginApi({ commit }, payload) { const response = await axios .post("http://localhost:3000/auth/login", payload, { withCredentials: true, credentials: "include", }) .catch((err) => { console.log(err); }); if (response && response.data) { localStorage.setItem("isAuthenticated", "true"); commit("setLoginApiStatus", "success"); } else { commit("setLoginApiStatus", "failed"); } } };
- (Line: 14) Adding a flag to browser local storage.
Now delete the flag on user logout.
src/store/modules/auth.js:
const actions = { // hidden code for display purpose async userLogout({ commit }) { const response = await axios .get("http://localhost:3000/logout", { withCredentials: true, credentials: "include", }) .catch((err) => { console.log(err); }); if (response && response.data) { commit("setLogout", true); localStorage.removeItem("isAuthenticated"); } else { commit("setLogout", false); } }, };
- (Line: 15) Remove the authentication flag from the browser's local storage.
So in Part-1, we implemented a partial navigation route guard, now here we are going to update its complete logic.
src/appRouter.js:
routeConfig.beforeEach(async (to, from, next) => { let userProfile = store.getters["auth/getUserProfile"]; let isAuthenticated = localStorage.getItem("isAuthenticated"); if (userProfile.id === 0 && isAuthenticated) { await store.dispatch("auth/userProfile"); userProfile = store.getters["auth/getUserProfile"]; } if (to.meta.requiredAuth) { if (userProfile.id === 0) { return next({ path: "/login" }); } } else { if (userProfile.id !== 0) { return next({ path: "/dashboard" }); } } return next(); });
- Here invoking profile API only when no user data from store and authentication flag exist in browser storage.
- Handle navigation routes based on the user information and guarding them.
Refresh Token:
Generally, the refresh token is to regenerate the expired jwt access token. So here our nestjs API generates a jwt access token and refresh token inside of the cookie. So from the client-side we simply calling refresh token endpoint is enough.
Our server refresh token endpoint looks like below.
http://localhost:3000/refresh-token
http://localhost:3000/refresh-token
Invoke The Refresh Token Endpoint:
In any client-side application the best and recommended approach to invoking the refresh token endpoint to use the interceptors.
So interceptors are used to bypass the request. If any secured api got unauthorized response, the interceptor will immediately invoke the refresh token endpoint and then again invokes the secured api call which is failed initially.
So let's create an interceptor in the new folder like 'shared'.
shared/jwt.interceptor.js:
import axios from "axios"; const jwtInterceptor = axios.create({}); jwtInterceptor.interceptors.request.use((config) => { return config; }); jwtInterceptor.interceptors.response.use( (response) => { return response; }, async (error) => { if (error.response.status === 401) { var response = await axios.get( "http://localhost:3000/refresh-token", { withCredentials: true, } ) .catch((err) => { return Promise.reject(err); }); if(response && response.data){ return axios(error.config); } else{ return Promise.reject(error); } } else { return Promise.reject(error); } } ); export default jwtInterceptor;
- (Line: 9-35) On receiving the response, if the interceptor receives status like '401', which means the jwt token expired. Then invoke the refresh token api, on the success of the refresh token API, we will re-initiate the api that is actually failed.
Video Session:
Support Me!
Buy Me A Coffee
PayPal Me
Wrapping Up:
Hopefully, I think this article delivered some useful information on refresh token usage. I love to have your feedback, suggestions, and better techniques in the comment section below.
Comments
Post a Comment