Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions ui/src/main/js/Index.js
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import rootReducer from 'store/reducers';
import { ThemeProvider } from 'react-jss';
import theme from '_theme';
import { dom } from '@fortawesome/fontawesome-svg-core';
import HttpInterceptor from 'common/util/HttpInterceptor';

const initialState = {};
// Setup history
Expand All @@ -18,6 +19,9 @@ const history = createHistory();
// Configure store with redux, thunk and history
const store = createStore(rootReducer(history), initialState, applyMiddleware(thunk, routerMiddleware(history)));

// Set up HTTP interceptor for 401 handling
HttpInterceptor.setupGlobalInterceptor(store);

dom.watch({
autoReplaceSvgRoot: document
});
Expand Down
62 changes: 62 additions & 0 deletions ui/src/main/js/common/util/HttpInterceptor.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
class HttpInterceptor {
static AUTH_ENDPOINTS = [
'/alert/api/login',
'/alert/api/verify',
'/alert/api/csrf',
'/alert/api/verify/saml',
'/alert/api/logout'
];

static isAuthEndpoint(url) {
return this.AUTH_ENDPOINTS.some(endpoint => url.includes(endpoint));
}

static isAlertApiCall(url) {
return url && url.includes('/alert/api/');
}

static shouldHandleUnauthorized(response, url) {
return response.status === 401 &&
this.isAlertApiCall(url) &&
!this.isAuthEndpoint(url);
}

/**
*
* @param {*} store - Redux store to dispatch unauthorized action
* Sets up a global fetch interceptor to handle 401 Unauthorized responses.
* If a 401 response is detected from a protected endpoint, it dispatches
* the unauthorized action to log out the user.
*/
static setupGlobalInterceptor(store) {
// Leverage existing unauthorized action which will dispatch a logout action
const { unauthorized } = require('../../store/actions/session');
// Necessary to capture the original fetch coming from the browser since below this
// we are overwriting window.fetch IF there is a 401 response, else we are just
// returning the original request/response unmodified. Without this, we would get
// an infinite loop.
const originalFetch = window.fetch;

// This window.fetch acts as a wrapper around the original fetch
window.fetch = async (...args) => {
try {
// Wait for the original fetch to complete
const response = await originalFetch(...args);

// Check to see if we should handle unauthorized access
if (this.shouldHandleUnauthorized(response, args[0])) {
// if (this.shouldHandleUnauthorized({status: 401, ...response}, args[0])) {
Copy link

Copilot AI Sep 18, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Remove this commented-out debugging code before merging to production.

Suggested change
// if (this.shouldHandleUnauthorized({status: 401, ...response}, args[0])) {

Copilot uses AI. Check for mistakes.
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Keeping this in for testing purposes

Comment on lines +47 to +48
Copy link

Copilot AI Sep 18, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The URL parameter (args[0]) may not always be a string. When using fetch with a Request object, args[0] would be a Request instance, not a URL string. Consider extracting the URL properly: const url = typeof args[0] === 'string' ? args[0] : args[0].url;

Suggested change
if (this.shouldHandleUnauthorized(response, args[0])) {
// if (this.shouldHandleUnauthorized({status: 401, ...response}, args[0])) {
const url = typeof args[0] === 'string' ? args[0] : args[0].url;
if (this.shouldHandleUnauthorized(response, url)) {

Copilot uses AI. Check for mistakes.
console.log('Unauthorized access detected, logging out user');
store.dispatch(unauthorized());
}

return response;
} catch (error) {
// Don't interfere with network errors
throw error;
}
};
}
}

export default HttpInterceptor;