I am trying to learn react js and springboot application, and when java restart i am invalidating a tokenVersion (timestamp in jwt payload but not actual token) in jwt token.
when java restart and if i try to reload react page or navigating spring security throws 403 forbidden.
using the response i am checking and try to force the user to logout and try to use signOut hook to cleanup the state or session but i am getting the react hook error.
I tried many ways from the chat gpt but none of them give the solution.
these are my following react codes:
1. EndpointConfig.js
import axios from 'axios';
import useSetupInterceptors from '../util/useSetupInterceptors'
const config = window.APP_CONFIG;
const endPointURLIP = config.endPointIP;
const defaultUrl = "localhost";
if(!endPointURLIP)
endPointURLIP = defaultUrl;
//Retrieving Token from the cookie which saved while login.
const getTokenFromCookie = () => {
const cookies = document.cookie.split(';').map(cookie => cookie.trim());
for (const cookie of cookies) {
if (cookie.startsWith('_auth=')) {
const tokenCookie = cookie.split('=');
if (tokenCookie.length === 2) {
return tokenCookie[1];
}
}
}
return null; // Token not found in cookies
};
const authToken = getTokenFromCookie();
const getAxiosHeader = () => {
const API_BASE_URL = 'http://'+endPointURLIP+':9081';
const AUTHENTICATION_HEADER_TOKEN = authToken ? `Bearer ${authToken}` : null;
return axios.create({
baseURL: API_BASE_URL,
headers: {
Authorization: AUTHENTICATION_HEADER_TOKEN,
},
});
};
const authAxiosHeader = getAxiosHeader();
useSetupInterceptors(authAxiosHeader);
const endpointConfig = {
a: 'http://'+endPointURLIP+':9081',
HISTORY_ENDPOINT: '/demo/history',
AUTHENTICATION_HEADER_TOKEN: authToken ? `Bearer ${authToken}` : null,
AUTHENTICATION_HEADER:authAxiosHeader,
};
export default endpointConfig;
2. useSetupInterceptors.js
import { useEffect } from 'react';
import { useSignOut } from 'react-auth-kit';
const useSetupInterceptors = ({ axiosInstance }) => {
const signOut = useSignOut();
useEffect(() => {
const interceptor = axiosInstance.interceptors.response.use(
(response) => response,
(error) => {
if (error.response && error.response.status === 403) {
alert('Request failed with status code 403:', error.message);
handleLogout();
}
return Promise.reject(error);
}
);
const handleLogout = () => {
window.location.href = '/';
signOut();
};
return () => {
axiosInstance.interceptors.response.eject(interceptor);
};
}, [axiosInstance, signOut]);
};
export default useSetupInterceptors;
3. DemoService.js
import axios from 'axios';
import endpointConfig from './EndpointConfig';
class DemoService {
constructor() {
this.baseUrl = endpointConfig.API_BASE_URL;
this.getHistoryEndpoint = endpointConfig.HISTORY_ENDPOINT;
this.axiosHeader = endpointConfig.AUTHENTICATION_HEADER;
}
async getDemoHistory() {
try {
const response = await this.axiosHeader.get(`${this.getHistoryEndpoint}`);
const configData = response.data;
return configData;
} catch (error) {
console.log('Error retrieving data:'+error);
}
}
}
export default new DemoService();
DemoHistory.js
import React, { Component } from 'react';
import { DataTable } from 'primereact/datatable';
import { Column } from 'primereact/column';
import DemoService from '../services/DemoService';
class DemoHistory extends Component {
constructor(props) {
super(props);
this.state = {
resultFromController: [],
isLoading: true,
};
}
componentDidMount() {
this.getDemoHistory();
}
getDemoHistory = () => {
DemoService.getDemoHistory()
.then((res) => {
this.setState({ resultFromController: res, isLoading: false });
})
.catch((error) => {
console.error('Error fetching Demo history:', error);
this.setState({ isLoading: false });
});
};
render() {
const { resultFromController, isLoading } = this.state;
if (isLoading) {
return <div>Loading...</div>;
}
return (
<div>
<h1>Demo History</h1>
<DataTable value={resultFromController}>
<Column field="fromUser" header="From User" />
<Column field="toUser" header="To User" />
</DataTable>
</div>
);
}
}
export default DemoHistory;
App.js
import React from 'react';
import { BrowserRouter, Route, Routes } from 'react-router-dom';
import { AuthProvider, RequireAuth } from 'react-auth-kit';
import DemoHistory from './components/DemoHistory';
function App() {
return (
<AuthProvider
authType={"cookie"}
authName={"_auth"}
cookieDomain={window.location.hostname}
cookieSecure={false}
>
<div>
<h2 className="header_name">Demo</h2>
<BrowserRouter>
<Routes>
<Route path="DemoHistory" element={<RequireAuth loginPath="/"><DemoHistory /></RequireAuth>} />
</Routes>
</BrowserRouter>
</div>
</AuthProvider>
);
}
export default App;
Login.js
import React, { useState } from "react";
import { Button, Form } from 'react-bootstrap';
import 'bootstrap/dist/css/bootstrap.min.css';
import { useNavigate } from "react-router-dom";
import { useAuthHeader, useSignIn ,useSignOut } from "react-auth-kit";
import LoginService from "../services/LoginService";
export default function Login() {
const [loginId, setLoginId] = useState("");
const [password, setPassword] = useState("");
const loginService = new LoginService();
const navigate = useNavigate();
const { setAuth, setHeader } = useAuthHeader();
const signIn = useSignIn();
const [loginStatus, setLoginStatus] = useState(null);
function validate() {
return loginId.length > 0 && password.length > 0;
}
async function submit(event) {
try{
event.preventDefault();
loginService.setLoginId(loginId);
loginService.setPassword(password);
const result = await loginService.validateLogin(loginId,password);
if (result && result.response === "success") {
signIn({
token: result.token,
expiresIn: 24*60*60,//24 hours
tokenType: "Bearer",
authState: { username: "admin" },
});
window.location.href = "/history";
} else {
setLoginStatus("invalid");
console.log("Login failed...");
}
}catch(error){
setLoginStatus("invalid");
console.log("Error While login."+error);
}
}
return (
<form
name="vishing_form"
method="post"
className="login_tab_form"
onSubmit={submit}
>
<p align="center" style={{ fontWeight: 'bolder', fontSize: '18px', fontFamily: 'helvetica' }}>
RedShift Login
</p>
<div>
<label>Login ID</label>
<input className="loginTextField"
autoFocus
type="text"
value={loginId}
onChange={(e) => setLoginId(e.target.value)}
/>
</div>
<div>
<label>Password</label>
<input className="loginTextField"
type="password"
value={password}
onChange={(e) => setPassword(e.target.value)}
/>
</div>
{loginStatus === "invalid" && (
<p>
Invalid credentials. Please try again.
</p>
)}
<div>
<button
id="submit"
type="submit"
name="login"
className="submitBtn"
disabled={!validate()}
>
Login
</button>
</div>
</div>
</form>
);
}
error:
Compiled with problems:
ERROR
[eslint]
src\services\EndpointConfig.js
Line 38:3: React Hook "useSetupInterceptors" cannot be called at the top level. React Hooks must be called in a React function component or a custom React Hook function react-hooks/rules-of-hooks
i am trying to learn react js please help how to fix this issue or any better way to handle the session management and browser closure handling
Thanks
You cannot call React hooks outside React components and custom React hooks. You can move the useSetupInterceptors
hook call into your React code.
Here's an example using a local component that is rendered within the AuthProvider
so that it can call the useSetupInterceptors
hook and the useSetupInterceptors
hook can reference the useSignOut
hook from `react-auth-kit';
EndpointConfig.js
import axios from 'axios';
import useSetupInterceptors from '../util/useSetupInterceptors';
const config = window.APP_CONFIG;
const endPointURLIP = config.endPointIP;
const defaultUrl = "localhost";
if (!endPointURLIP) {
endPointURLIP = defaultUrl;
}
const getTokenFromCookie = () => {
const [, token = null] = document.cookie
.split(";")
.map(cookie => cookie.trim().split("="))
.find(([key, value]) => key === "_auth") ?? [];
return token;
};
const authToken = getTokenFromCookie();
const getAxiosInstance = () => {
const API_BASE_URL = 'http://' + endPointURLIP + ':9081';
const AUTHENTICATION_HEADER_TOKEN = authToken ? `Bearer ${authToken}` : null;
return axios.create({
baseURL: API_BASE_URL,
headers: {
Authorization: AUTHENTICATION_HEADER_TOKEN,
},
});
};
export const authAxiosInstance = getAxiosInstance(); // <-- export for app usage
const endpointConfig = {
a: 'http://'+endPointURLIP+':9081',
HISTORY_ENDPOINT: '/demo/history',
AUTHENTICATION_HEADER_TOKEN: authToken ? `Bearer ${authToken}` : null,
AUTHENTICATION_HEADER: authAxiosInstance,
};
export default endpointConfig;
import React from 'react';
import { BrowserRouter, Route, Routes } from 'react-router-dom';
import { AuthProvider, AuthOutlet } from 'react-auth-kit';
import DemoHistory from './components/DemoHistory';
import useSetupInterceptors from '../util/useSetupInterceptors';
import { authAxiosInstance } from './EndpointConfig';
const SetupAxios = () => {
useSetupInterceptors(authAxiosInstance);
return null;
};
function App() {
return (
<AuthProvider
authType={"cookie"}
authName={"_auth"}
cookieDomain={window.location.hostname}
cookieSecure={false}
>
<BrowserRouter>
<SetupAxios />
<div>
<h2 className="header_name">Demo</h2>
<Routes>
<Route element={<AuthOutlet fallbackPath='/' />}>
<Route path="DemoHistory" element={<DemoHistory />} />
</Route>
...
</Routes>
</div>
</BrowserRouter>
</AuthProvider>
);
}
export default App;