I created a React application, where I try to set some key in the local storage of the browser. I also set the session storage. When I run my application on development mode, it works - I successfully set keys in both session and local storage. However, on production, only the session storage successfully being set.
This is my code:
import React, { useEffect } from 'react';
import { useNavigate, useSearchParams } from 'react-router-dom';
import { connect } from 'react-redux';
import type { PayloadAction } from '@reduxjs/toolkit';
import type { IAutoAuthResponseData, ICliAuthResponseData } from '@exlint.io/common';
import { authActions } from '@/store/reducers/auth';
import type { IAuthPayload } from '@/store/interfaces/auth';
import BackendService from '@/services/backend';
import CliBackendService from '@/services/cli-backend';
import type { AppState } from '@/store/app';
import ExternalAuthRedirectView from './ExternalAuthRedirect.view';
interface IPropsFromState {
readonly isAuthenticated: boolean | null;
}
interface PropsFromDispatch {
readonly auth: (loginPayload: IAuthPayload) => PayloadAction<IAuthPayload>;
}
interface IProps extends IPropsFromState, PropsFromDispatch {}
const ExternalAuthRedirect: React.FC<IProps> = (props: React.PropsWithChildren<IProps>) => {
const navigate = useNavigate();
const [searchParams] = useSearchParams();
const refreshToken = searchParams.get('refreshToken');
const port = searchParams.get('port');
useEffect(() => {
if (refreshToken) {
localStorage.setItem('token', refreshToken);
const fetchResults = async () => {
let autoAuthResponseData: IAutoAuthResponseData;
try {
autoAuthResponseData = await BackendService.get<IAutoAuthResponseData>('/user/auth');
console.log(1);
sessionStorage.setItem('token', autoAuthResponseData.accessToken);
} catch {
console.log(2);
localStorage.clear();
let navigateUrl = '/auth';
if (port) {
navigateUrl += `?port=${port}`;
}
navigate(navigateUrl);
return;
}
if (port) {
let cliToken: string;
let email: string;
try {
const responseData = await CliBackendService.get<ICliAuthResponseData>(
'/user/auth/auth',
);
cliToken = responseData.cliToken;
email = responseData.email;
} catch {
navigate(`/cli-auth?port=${port}`);
return;
}
try {
const res = await fetch(`http://localhost:${port}/${cliToken}/${email}`);
if (!res.ok) {
throw new Error();
}
navigate('/cli-authenticated');
} catch {
navigate(`/cli-auth?port=${port}`);
return;
}
}
props.auth({
id: autoAuthResponseData.id,
name: autoAuthResponseData.name,
createdAt: autoAuthResponseData.createdAt,
});
};
fetchResults();
} else {
let navigateUrl = '/auth';
if (port) {
navigateUrl += `?port=${port}`;
}
navigate(navigateUrl);
}
}, [refreshToken, port]);
useEffect(() => {
if (!port && props.isAuthenticated) {
navigate('/', { replace: true });
}
}, [port, props.isAuthenticated]);
return <ExternalAuthRedirectView />;
};
ExternalAuthRedirect.displayName = 'ExternalAuthRedirect';
ExternalAuthRedirect.defaultProps = {};
const mapStateToProps = (state: AppState) => {
return {
isAuthenticated: state.auth.isAuthenticated,
};
};
export default connect(mapStateToProps, { auth: authActions.auth })(React.memo(ExternalAuthRedirect));
As you can see, I have this line of code: localStorage.setItem('token', refreshToken);
.
I can guarantee it is executed, as I see an HTTP request is outgoing off my browser (and the code does call for HTTP request right after this line of code as you can see).
This same code, for some reason, does not set the local storage. However, you can see in the next lines of code, I also set the session storage: sessionStorage.setItem('token', autoAuthResponseData.accessToken);
. Then, the session storage is successfully set also on production.
When I check my console in the browser on production, I see this message: Storage access automatically granted for First-Party isolation “https://api.exlint.io” on “https://app.exlint.io”.
. Not sure how relevant it is.
Note the console.log(1)
and console.log(2)
, I can tell that only console.log(1)
is being logged
I also have middleware of redux to clear storage after logout:
listenerMiddleware.startListening({
actionCreator: authActions.setUnauthenticated,
effect: () => {
listenerMiddleware.stopListening({
actionCreator: authActions.auth,
effect: authEffect,
cancelActive: true,
});
listenerMiddleware.startListening({
actionCreator: authActions.auth,
effect: authEffect,
});
localStorage.clear();
sessionStorage.clear();
},
});