Я создаю поток аутентификации с помощью React-Router и Redux, где мой бэкэнд возвращает JWT в виде Session Cook ie (с HTTP-заголовком Set-Cook ie), который Frontend использует по порядку сделать последующие запросы. (Я не использую localStorage, поскольку общая рекомендация - предпочесть Cook ie из-за риска того, что JS может его прочитать в случае XSS)
// my App component
const { isAuthenticated } = useSelector((state) => state.user);
<Container>
{!isAuthenticated ? (
<LoginForm />
) : (
<Switch>
<Route exact path="/" component={MessageList} />
<Route path="/messages" component={MessageList} />
<Route path="/login" component={LoginForm} />
<Route path="/message/:id" component={MessageContainer} />
</Switch>
)}
</Container>
// the api requests themselves abstracted
const login = (data) =>
axios
.post("auth/login", data, { withCredentials: true })
.then((response) => response.data);
const fetchItems = (page, token) =>
axios(`message/list/page/${page}`, {
withCredentials: true,
}).then((response) => response.data);
// actions
function handleError(err) {
return {
type: "ERROR_OCCURED",
payload: err,
};
}
function shouldFetchMessages(state, page) {
if (!state.message.all.length) {
return true;
} else if (state.message.isFetching) {
return false;
} else {
return state.message.all;
}
}
export let getMessages = (page = 1) => async (dispatch, getState) => {
let err, response;
dispatch(requestMessages(page));
[err, response] = await to(api.fetchItems(page, getState().user.accessToken));
if (err) {
return handleError(err);
}
return dispatch(receiveMessages(response));
};
export function getMessagesIfNeeded(page) {
return (dispatch, getState) => {
if (shouldFetchMessages(getState(), page)) {
return dispatch(getMessages(page));
}
};
}
// reducer
// where would the localStorage isAuthenticated value be synced with the store?
export default (state = defaultState, action) => {
switch (action.type) {
// skipped the other reducers for brevity
case "ERROR_OCCURED":
return {
...state,
isAuthenticated: false, // this should be saved in the localStorage
};
default:
return state;
}
};
Мне нужен BOOLEAN
, который хранит "состояние" isAuthenticated
, чтобы я мог показать соответствующий компонент.
Моя основная проблема в том, что состояние Redux, конечно, не сохраняется при обновлении страниц.
Я думал о наличии переменной localStorage
(или новой cookie
). (Cook ie, который содержит JWT, - это httpOnly
и, насколько я понимаю, не может быть доступен JavaScript).
Вопросы:
- это устоявшаяся практика
- как будет обрабатываться истечение срока действия JWT? (Думаю, я мог бы установить для него значение false, если HTTP-запрос завершится с ошибкой с 401.)
- где будет синхронизироваться переменная localStorage с состоянием Redux, чтобы избежать побочных эффектов в моих редукторах?