Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

topic/delete cookie and use getIdToken #568

Merged
merged 20 commits into from
Jan 30, 2025
Merged
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
1,245 changes: 296 additions & 949 deletions web/package-lock.json

Large diffs are not rendered by default.

1 change: 0 additions & 1 deletion web/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,6 @@
"notistack": "^3.0.1",
"prop-types": "^15.8.1",
"react": "^18.2.0",
"react-cookie": "^7.2.1",
"react-dom": "^18.2.0",
"react-error-boundary": "^5.0.0",
"react-icons": "^5.0.1",
Expand Down
5 changes: 3 additions & 2 deletions web/src/components/PTeamLabel.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import { Box, IconButton, Typography } from "@mui/material";
import PropTypes from "prop-types";
import React, { useState } from "react";

import { useSkipUntilAuthTokenIsReady } from "../hooks/auth";
import { useSkipUntilAuthUserIsReady } from "../hooks/auth";
import { useGetUserMeQuery } from "../services/tcApi";
import { APIError } from "../utils/APIError";
import { errorToString } from "../utils/func";
Expand All @@ -16,7 +16,8 @@ export function PTeamLabel(props) {

const [pteamSettingsModalOpen, setPTeamSettingsModalOpen] = useState(false);

const skip = useSkipUntilAuthTokenIsReady();
const skip = useSkipUntilAuthUserIsReady();

const {
data: userMe,
error: userMeError,
Expand Down
4 changes: 2 additions & 2 deletions web/src/components/PTeamSettingsModal.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ import React, { useState } from "react";

import { TabPanel } from "../components/TabPanel";
import dialogStyle from "../cssModule/dialog.module.css";
import { useSkipUntilAuthTokenIsReady } from "../hooks/auth";
import { useSkipUntilAuthUserIsReady } from "../hooks/auth";
import { useGetPTeamQuery } from "../services/tcApi";
import { APIError } from "../utils/APIError";
import { a11yProps, errorToString } from "../utils/func.js";
Expand All @@ -27,7 +27,7 @@ export function PTeamSettingsModal(props) {
const { pteamId, onSetShow, show, defaultTabIndex } = props;
const [tab, setTab] = useState(defaultTabIndex ?? 0);

const skip = useSkipUntilAuthTokenIsReady() || !pteamId;
const skip = useSkipUntilAuthUserIsReady() || !pteamId;

const {
data: pteam,
Expand Down
6 changes: 2 additions & 4 deletions web/src/hooks/auth.js
Original file line number Diff line number Diff line change
@@ -1,7 +1,5 @@
import { useSelector } from "react-redux";

export function useSkipUntilAuthTokenIsReady() {
const authToken = useSelector((state) => state.auth.token);
const skip = authToken === undefined;
return skip;
export function useSkipUntilAuthUserIsReady() {
return !useSelector((state) => state.auth.authUserIsReady);
}
79 changes: 38 additions & 41 deletions web/src/index.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@ import { CssBaseline } from "@mui/material";
import { createTheme, ThemeProvider } from "@mui/material/styles";
import { SnackbarProvider } from "notistack";
import React from "react";
import { CookiesProvider } from "react-cookie";
import { createRoot } from "react-dom/client";
import { Provider } from "react-redux";
import { BrowserRouter as Router, Navigate, Route, Routes } from "react-router-dom";
Expand All @@ -28,46 +27,44 @@ const root = createRoot(container);

root.render(
<React.StrictMode>
<CookiesProvider>
<Provider store={store}>
<ThemeProvider theme={createTheme()}>
<CssBaseline />
<SnackbarProvider
maxSnack={3}
preventDuplicate={true}
anchorOrigin={{ horizontal: "center", vertical: "top" }}
autoHideDuration={5000}
>
<Router basename={import.meta.env.VITE_PUBLIC_URL}>
<Routes>
<Route exact path="/login" element={<Login />} />
<Route path="/reset_password" element={<ResetPassword />} />
<Route path="/sign_up" element={<SignUp />} />
<Route path="/email_verification" element={<EmailVerification />} />
<Route path="/" element={<App />}>
<Route index element={<Status />} />
<Route path="account">
<Route index element={<Account />} />
</Route>
<Route path="pteam">
<Route index element={<PTeam />} />
<Route path="join" element={<AcceptPTeamInvitation />} />
</Route>
<Route path="tags">
<Route index element={<Navigate to="/" />} />
<Route path=":tagId" element={<Tag />} />
</Route>
<Route path="*" element={<Navigate to="/" />} />
<Route path="topics">
<Route index element={<TopicManagement />} />
<Route path=":topicId" element={<TopicDetail />} />
</Route>
<Provider store={store}>
<ThemeProvider theme={createTheme()}>
<CssBaseline />
<SnackbarProvider
maxSnack={3}
preventDuplicate={true}
anchorOrigin={{ horizontal: "center", vertical: "top" }}
autoHideDuration={5000}
>
<Router basename={import.meta.env.VITE_PUBLIC_URL}>
<Routes>
<Route exact path="/login" element={<Login />} />
<Route path="/reset_password" element={<ResetPassword />} />
<Route path="/sign_up" element={<SignUp />} />
<Route path="/email_verification" element={<EmailVerification />} />
<Route path="/" element={<App />}>
<Route index element={<Status />} />
<Route path="account">
<Route index element={<Account />} />
</Route>
</Routes>
</Router>
</SnackbarProvider>
</ThemeProvider>
</Provider>
</CookiesProvider>
<Route path="pteam">
<Route index element={<PTeam />} />
<Route path="join" element={<AcceptPTeamInvitation />} />
</Route>
<Route path="tags">
<Route index element={<Navigate to="/" />} />
<Route path=":tagId" element={<Tag />} />
</Route>
<Route path="*" element={<Navigate to="/" />} />
<Route path="topics">
<Route index element={<TopicManagement />} />
<Route path=":topicId" element={<TopicDetail />} />
</Route>
</Route>
</Routes>
</Router>
</SnackbarProvider>
</ThemeProvider>
</Provider>
</React.StrictMode>,
);
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import { useSnackbar } from "notistack";
import React from "react";
import { useLocation, useNavigate } from "react-router-dom";

import { useSkipUntilAuthTokenIsReady } from "../../hooks/auth";
import { useSkipUntilAuthUserIsReady } from "../../hooks/auth";
import { useApplyPTeamInvitationMutation, useGetPTeamInvitationQuery } from "../../services/tcApi";
import { APIError } from "../../utils/APIError";
import { commonButtonStyle } from "../../utils/const";
Expand All @@ -19,7 +19,7 @@ export function AcceptPTeamInvitation() {
const params = new URLSearchParams(useLocation().search);
const tokenId = params.get("token");

const skip = useSkipUntilAuthTokenIsReady();
const skip = useSkipUntilAuthUserIsReady();
const {
data: detail,
error: detailError,
Expand Down
4 changes: 2 additions & 2 deletions web/src/pages/Account/AccountPage.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ import { useSnackbar } from "notistack";
import React, { useState } from "react";

import { UUIDTypography } from "../../components/UUIDTypography";
import { useSkipUntilAuthTokenIsReady } from "../../hooks/auth";
import { useSkipUntilAuthUserIsReady } from "../../hooks/auth";
import { useGetUserMeQuery, useUpdateUserMutation } from "../../services/tcApi";
import { APIError } from "../../utils/APIError";
import { errorToString } from "../../utils/func";
Expand All @@ -28,7 +28,7 @@ export function Account() {
const { enqueueSnackbar } = useSnackbar();

const [updateUser] = useUpdateUserMutation();
const skip = useSkipUntilAuthTokenIsReady();
const skip = useSkipUntilAuthUserIsReady();
const {
data: userMe,
error: userMeError,
Expand Down
16 changes: 13 additions & 3 deletions web/src/pages/App/AppBar.jsx
Original file line number Diff line number Diff line change
@@ -1,14 +1,17 @@
import { Menu as MenuIcon } from "@mui/icons-material";
import { AppBar as MuiAppBar, Box, Button, Divider, IconButton, Toolbar } from "@mui/material";
import { styled } from "@mui/material/styles";
import { signOut } from "firebase/auth";
import React from "react";
import { ErrorBoundary } from "react-error-boundary";
import { useDispatch, useSelector } from "react-redux";
import { useNavigate } from "react-router-dom";

import { firebaseApi } from "../../services/firebaseApi";
import { tcApi } from "../../services/tcApi";
import { setAuthUserIsReady } from "../../slices/auth";
import { setDrawerOpen } from "../../slices/system";
import Firebase from "../../utils/Firebase";
import { drawerWidth } from "../../utils/const";

import { AppFallback } from "./AppFallback";
Expand Down Expand Up @@ -46,9 +49,16 @@ export function AppBar() {
const handleLogout = () => {
dispatch(firebaseApi.util.resetApiState()); // reset RTKQ
dispatch(tcApi.util.resetApiState()); // reset RTKQ
navigate("/login", {
state: { message: "Logged out successfully.", from: null, search: null },
});
dispatch(setAuthUserIsReady(false));
signOut(Firebase.getAuth())
.then(() => {
navigate("/login", {
state: { message: "Logged out successfully.", from: null, search: null },
});
})
.catch((error) => {
console.error(error);
});
};

return (
Expand Down
34 changes: 12 additions & 22 deletions web/src/pages/App/AppPage.jsx
Original file line number Diff line number Diff line change
@@ -1,14 +1,13 @@
import { Box } from "@mui/material";
import { onAuthStateChanged, signOut } from "firebase/auth";
import React, { useEffect } from "react";
import { useCookies } from "react-cookie";
import { ErrorBoundary } from "react-error-boundary";
import { useDispatch, useSelector } from "react-redux";
import { useLocation, useNavigate } from "react-router-dom";

import { useSkipUntilAuthTokenIsReady } from "../../hooks/auth";
import { useTryLoginMutation } from "../../services/tcApi";
import { setAuthToken } from "../../slices/auth";
import { authCookieName, mainMaxWidth } from "../../utils/const";
import { setAuthUserIsReady } from "../../slices/auth";
import Firebase from "../../utils/Firebase";
import { mainMaxWidth } from "../../utils/const";

import { AppBar } from "./AppBar";
import { AppFallback } from "./AppFallback";
Expand All @@ -17,37 +16,28 @@ import { Main } from "./Main";
import { OutletWithCheckedParams } from "./OutletWithCheckedParams";

export function App() {
const [cookies, _setCookie, _removeCookie] = useCookies([authCookieName]);

const skip = useSkipUntilAuthTokenIsReady();

const dispatch = useDispatch();
const system = useSelector((state) => state.system);
const location = useLocation();
const navigate = useNavigate();

const [tryLogin] = useTryLoginMutation();
const dispatch = useDispatch();

useEffect(() => {
if (!skip) return; // auth token is ready
const _checkToken = async () => {
try {
const accessToken = cookies[authCookieName];
if (!accessToken) throw new Error("Missing cookie");
dispatch(setAuthToken(accessToken));
await tryLogin().unwrap(); // throw error if accessToken is expired
} catch (error) {
onAuthStateChanged(Firebase.getAuth(), (user) => {
if (!user) {
dispatch(setAuthUserIsReady(false));
navigate("/login", {
state: {
from: location.pathname,
search: location.search,
message: "Please login to continue.",
},
});
Copy link
Collaborator

Choose a reason for hiding this comment

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

tryLogin()して、エラーならloginページにnavigate、
の処理は消してはいけないと思います。

} else {
dispatch(setAuthUserIsReady(true));
}
};
_checkToken();
}, [cookies, dispatch, location, navigate, skip, tryLogin]);
});
}, [location, dispatch, navigate]);

return (
<>
Expand Down
4 changes: 2 additions & 2 deletions web/src/pages/App/OutletWithCheckedParams.jsx
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import React, { useEffect } from "react";
import { Outlet, useLocation, useNavigate } from "react-router-dom";

import { useSkipUntilAuthTokenIsReady } from "../../hooks/auth";
import { useSkipUntilAuthUserIsReady } from "../../hooks/auth";
import { useGetUserMeQuery } from "../../services/tcApi";
import { APIError } from "../../utils/APIError";
import { errorToString } from "../../utils/func";
Expand All @@ -11,7 +11,7 @@ export function OutletWithCheckedParams() {
const navigate = useNavigate();
const location = useLocation();

const skip = useSkipUntilAuthTokenIsReady();
const skip = useSkipUntilAuthUserIsReady();

const {
data: userMe,
Expand Down
4 changes: 2 additions & 2 deletions web/src/pages/App/TeamSelector.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import { grey } from "@mui/material/colors";
import React, { useEffect, useMemo, useState } from "react";
import { useLocation, useNavigate } from "react-router-dom";

import { useSkipUntilAuthTokenIsReady } from "../../hooks/auth";
import { useSkipUntilAuthUserIsReady } from "../../hooks/auth";
import { useGetUserMeQuery } from "../../services/tcApi";
import { APIError } from "../../utils/APIError";
import { LocationReader } from "../../utils/LocationReader";
Expand Down Expand Up @@ -35,7 +35,7 @@ export function TeamSelector() {
const [openPTeamCreationModal, setOpenPTeamCreationModal] = useState(false);
const locationReader = useMemo(() => new LocationReader(location), [location]);

const skip = useSkipUntilAuthTokenIsReady();
const skip = useSkipUntilAuthUserIsReady();
const {
data: userMe,
error: userMeError,
Expand Down
15 changes: 5 additions & 10 deletions web/src/pages/Login/LoginPage.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -14,8 +14,8 @@ import {
TextField,
Typography,
} from "@mui/material";
import { signOut } from "firebase/auth";
import React, { useEffect, useState } from "react";
import { useCookies } from "react-cookie";
import { useDispatch } from "react-redux";
import { useLocation, useNavigate } from "react-router-dom";

Expand All @@ -25,9 +25,8 @@ import {
useSignInWithSamlPopupMutation,
} from "../../services/firebaseApi";
import { useCreateUserMutation, useTryLoginMutation } from "../../services/tcApi";
import { clearAuth } from "../../slices/auth";
import { setAuthUserIsReady } from "../../slices/auth";
import Firebase from "../../utils/Firebase";
import { authCookieName, cookiesOptions } from "../../utils/const";

export function Login() {
const [message, setMessage] = useState(null);
Expand All @@ -37,19 +36,17 @@ export function Login() {
const location = useLocation();
const navigate = useNavigate();

const [_cookies, setCookie, removeCookie] = useCookies([authCookieName]);

const [sendEmailVerification] = useSendEmailVerificationMutation();
const [signInWithEmailAndPassword] = useSignInWithEmailAndPasswordMutation();
const [signInWithSamlPopup] = useSignInWithSamlPopupMutation();
const [createUser] = useCreateUserMutation();
const [tryLogin] = useTryLoginMutation();

useEffect(() => {
dispatch(clearAuth());
removeCookie(authCookieName, cookiesOptions);
dispatch(setAuthUserIsReady(false));
setMessage(location.state?.message);
}, [dispatch, location, removeCookie]);
signOut(Firebase.getAuth());
}, [dispatch, location]);

const callSignInWithEmailAndPassword = async (email, password) => {
return await signInWithEmailAndPassword({ email, password })
Expand Down Expand Up @@ -79,8 +76,6 @@ export function Login() {
};

const navigateInternalPage = async (userCredential) => {
const accessToken = userCredential.user.accessToken;
setCookie(authCookieName, accessToken, cookiesOptions);
try {
await tryLogin().unwrap();
navigate({
Expand Down
4 changes: 2 additions & 2 deletions web/src/pages/PTeam/PTeamMemberMenu.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ import { Button, Dialog, DialogContent, Menu, MenuItem } from "@mui/material";
import PropTypes from "prop-types";
import React, { useState } from "react";

import { useSkipUntilAuthTokenIsReady } from "../../hooks/auth";
import { useSkipUntilAuthUserIsReady } from "../../hooks/auth";
import { useGetPTeamQuery, useGetUserMeQuery } from "../../services/tcApi";
import { APIError } from "../../utils/APIError";
import { errorToString, checkAdmin } from "../../utils/func";
Expand All @@ -23,7 +23,7 @@ export function PTeamMemberMenu(props) {
const [anchorEl, setAnchorEl] = useState(null);
const open = Boolean(anchorEl);

const skip = useSkipUntilAuthTokenIsReady() || !pteamId;
const skip = useSkipUntilAuthUserIsReady() || !pteamId;
const {
data: userMe,
error: userMeError,
Expand Down
Loading