Refresh Auth Token Rotation (Node
js & React ) — Part 2
Tékos Bence - Follow
12min read - Mar 18, 2024
¥ 7
Ss -
Auth Token Rotation ( Node js & React js )
Welcome back! In this section, we'll create a small frontend demo to test the
token rotation solution we implemented in the previous part. Let’s start by
creating a new React app.
We create a React app with vite:apm create
And install the axios:
apm install axios
We'll also install React Redux because it makes token handling much easier:
apm instal
apm install
Introduction to Redux Store:
Redux is a state management library commonly used with React applications
to manage application state in a predictable and centralized manner. The
Redux store serves as a single source of truth for the entire application state,
allowing components to access and update state consistently across the
application. It helps simplify complex data flow and makes it easier to manage
application state, especially in large-scale applications with many components.
Let’s implement the redux store what we will use in our little demo. Make a
folder store and create two files: authSlice and store.
Here is the authSlice:import { createSlice } from
Import jwt decode from "jwt-decode”
const initialstate = {
token: null,
pull,
authTokenSlice = createSlice ((
addToken (state, action)
action.payload;
export const authTokenActions
export default authTokens:
thTokenSlice.actions;
In this code snippet, we’re defining a Redux slice named authTokenSlice using
createSlice from the Redux Toolkit. This slice manages the authentication-
related state in our Redux store. The initial state includes properties for the
authentication token (token), user ID (userld). The slice contains reducer
functions to add or delete the authentication token from the state. When
adding a token, jwt_decode is used to extract the user ID from the JWT token
payload, which is then stored in the state. This slice provides actions like
addToken and deleteToken, which can be dispatched to update the
authentication state in the Redux store.And here is the store:
mport { configure’
import authTokensL
//Pexs:
mport
mport {
onfigurestore (
authTokel 1e.reducer) ,
In this code snippet, we're configuring the Redux store using configureStore
from the Redux Toolkit. We’re also setting up state persistence using redux-
persist to persist the authToken slice’s state across browser sessions. The
authTokenPersistConfig defines the configuration for persisting the
authentication token slice using sessionStorage. This ensures that the
authentication state persists even after the user closes and reopens the
browser. Finally, we export the configured Redux store and persistor for use
throughout our application. This setup provides a robust and reliable
mechanism for managing authentication state in our React application.
Now we need to use it so let’s add to our main,jsx:import React from ‘react!
import Rea
mport
M from 'react-dom/client'
mport {
import {
import {
mport
stGate ) from "redux-p. Jinvegration/react";
Re:
DOM. createRoot (document .getELlementById ("root") ) . render (
In this code snippet, we’re setting up the entry point for our React application.
First, we import necessary dependencies such as React, ReactDOM, App
component, and stylesheets. We also import BrowserRouter from “react-
router-dom” to enable client-side routing in our application. Next, we import
our Redux store and persistor from “./store/store” to provide centralized state
management across the application. We then import Provider from “react-
redux” to wrap our entire application with the Redux store, allowing
components to access the Redux state. Additionally, we import PersistGate
from “redux-persist/integration/react” to ensure that our Redux store is
rehydrated with persisted state before rendering the application. Finally, we
use ReactDOM.createRoot to render our application into the root element in
the HTML document. Within the render method, we wrap our entire
application with Provider and PersistGate, ensuring that the Redux store and
persisted state are available throughout the component tree. Inside the
Provider, we nest BrowserRouter to enable routing within our application, with
the App component serving as the root component. Overall, this setup ensuresseamless integration of Redux state management and client-side routing in our
React application, providing a smooth and responsive user experience.
Alright, now that we've configured the Redux store and routing, it’s time to
create our pages. We'll start by setting up a pages folder where we'll
implement our pages. Given that this tutorial focuses on token rotation, we'll
create just three pages: a main protected page, a login page, and a sign-up page.
First we implement the login pag
import React, { useState } from
import AxiosInstance from "../axios/axiosInstance";
import { useDispatch } from "react-redux";
okenActions } from "
« /store/a\
import {
import { useNavigate } from "react-router-don";
const Login = () =)
const [loginData, setLoginData] = uses
passwort
Me
const [errors, setBrror
const dispatch = useDispateh ();
const navigate = useNavigate();
const AxiosInstance ({
“Content~Type": "application/json",
utChangeHiandle = (e
const { name, value } ~ event
setLoginData({ ...loginData,
email") {
vemail = "5
: value J);
if (name
}
if (name === “password") {
errozs.password = "";
}
const onk
n= async (e) =>
e.preventDefault ();
const validationErrors = validateForm(loginData) ;
if (Object keys (validationBrrors) length === 0) {
console. log(loginData) +await axiosInstance
-post ("/users/signin", loginData)
-then( (response) => (
Lf (response.data.accessToken) (
dispatch (authTokenActions.addToken (response .data.accessToken) ) +
navigate ("/");
,
»
scatch( (error) => {
console. log (error);
De
} else {
setErrors (validationtrrors);
const validateForm = (data) => {
const errors = {};
if (Idata.password) {
exrors.password =
}
if (Jdatavemaily {
Password is required";
errors.email = "Email is required";
} else if (JisValidamail (data-email)) {
errors.email = "Invalid email format";
}
return errors;
be
const isValidfmail = (email) => {
const emailpattern = /*[a-zA-20-9._-1+8[a~2A~20-9.-]+\. [a-zA~2] (2,418/¢
// Test the email against the pattern
retuzn emailPattern.test (email);
ie
return (