From 09f53b5ac24f44f857bb6fb6d2aa3448ad12c43c Mon Sep 17 00:00:00 2001 From: seb Date: Tue, 23 Jun 2026 11:08:40 +0200 Subject: [PATCH] Auth rework --- README.md | 3 +- src/App.jsx | 2 +- .../{AuthContext.jsx => AuthProvider.jsx} | 37 +++++-------------- src/auth/context.js | 3 ++ src/auth/useAuth.js | 8 ++++ src/components/LoginPage.jsx | 2 +- src/components/UserMenu.jsx | 4 +- src/main.jsx | 2 +- 8 files changed, 27 insertions(+), 34 deletions(-) rename src/auth/{AuthContext.jsx => AuthProvider.jsx} (64%) create mode 100644 src/auth/context.js create mode 100644 src/auth/useAuth.js diff --git a/README.md b/README.md index 6079e27..ab004bb 100644 --- a/README.md +++ b/README.md @@ -42,5 +42,6 @@ requirement without needing HTTPS on the frontend dev server. - `src/api/index.js` — all backend communication (`credentials: 'include'`). - `src/auth/googleSignIn.js` — Google Identity Services helper. -- `src/auth/AuthContext.jsx` — session state + login/logout. +- `src/auth/AuthProvider.jsx` — session state + login/logout. +- `src/auth/useAuth.js` — auth context hook. - `src/components/*` — small function components with local state. diff --git a/src/App.jsx b/src/App.jsx index 3f42e81..56da491 100644 --- a/src/App.jsx +++ b/src/App.jsx @@ -1,6 +1,6 @@ import Box from '@mui/material/Box'; import CircularProgress from '@mui/material/CircularProgress'; -import { useAuth } from './auth/AuthContext'; +import { useAuth } from './auth/useAuth'; import LoginPage from './components/LoginPage'; import AppShell from './components/AppShell'; diff --git a/src/auth/AuthContext.jsx b/src/auth/AuthProvider.jsx similarity index 64% rename from src/auth/AuthContext.jsx rename to src/auth/AuthProvider.jsx index 39988f3..40fd69d 100644 --- a/src/auth/AuthContext.jsx +++ b/src/auth/AuthProvider.jsx @@ -1,17 +1,9 @@ -import { - createContext, - useCallback, - useContext, - useEffect, - useMemo, - useState, -} from 'react'; +import { useCallback, useEffect, useMemo, useState } from 'react'; import { useSnackbar } from 'notistack'; import { auth } from '../api'; +import { AuthContext } from './context'; -const AuthContext = createContext(null); - -export function AuthProvider({ children }) { +export default function AuthProvider({ children }) { const { enqueueSnackbar } = useSnackbar(); const [user, setUser] = useState(null); const [googleClientId, setGoogleClientId] = useState(null); @@ -42,22 +34,17 @@ export function AuthProvider({ children }) { }; }, [enqueueSnackbar]); - const loginWithGoogle = useCallback( - async (credential) => { - const { user: me } = await auth.loginWithGoogle(credential); - setUser(me); - setStatus('authed'); - enqueueSnackbar(`Welcome, ${me.email}`, { variant: 'success' }); - }, - [enqueueSnackbar], - ); + const loginWithGoogle = useCallback(async (credential) => { + const { user: me } = await auth.loginWithGoogle(credential); + setUser(me); + setStatus('authed'); + }, []); const logout = useCallback(async () => { await auth.logout(); setUser(null); setStatus('anon'); - enqueueSnackbar('Signed out', { variant: 'info' }); - }, [enqueueSnackbar]); + }, []); const value = useMemo( () => ({ user, googleClientId, status, loginWithGoogle, logout }), @@ -66,9 +53,3 @@ export function AuthProvider({ children }) { return {children}; } - -export function useAuth() { - const ctx = useContext(AuthContext); - if (!ctx) throw new Error('useAuth must be used within an AuthProvider'); - return ctx; -} diff --git a/src/auth/context.js b/src/auth/context.js new file mode 100644 index 0000000..a3e3c7d --- /dev/null +++ b/src/auth/context.js @@ -0,0 +1,3 @@ +import { createContext } from 'react'; + +export const AuthContext = createContext(null); diff --git a/src/auth/useAuth.js b/src/auth/useAuth.js new file mode 100644 index 0000000..cfcfe48 --- /dev/null +++ b/src/auth/useAuth.js @@ -0,0 +1,8 @@ +import { useContext } from 'react'; +import { AuthContext } from './context'; + +export function useAuth() { + const ctx = useContext(AuthContext); + if (!ctx) throw new Error('useAuth must be used within an AuthProvider'); + return ctx; +} diff --git a/src/components/LoginPage.jsx b/src/components/LoginPage.jsx index 79d711b..34bf35c 100644 --- a/src/components/LoginPage.jsx +++ b/src/components/LoginPage.jsx @@ -7,7 +7,7 @@ import Stack from '@mui/material/Stack'; import Typography from '@mui/material/Typography'; import GoogleIcon from '@mui/icons-material/Google'; import { useSnackbar } from 'notistack'; -import { useAuth } from '../auth/AuthContext'; +import { useAuth } from '../auth/useAuth'; import { requestGoogleIdToken } from '../auth/googleSignIn'; export default function LoginPage() { diff --git a/src/components/UserMenu.jsx b/src/components/UserMenu.jsx index 8026cb3..80f4a48 100644 --- a/src/components/UserMenu.jsx +++ b/src/components/UserMenu.jsx @@ -7,7 +7,7 @@ import MenuItem from '@mui/material/MenuItem'; import ListItemIcon from '@mui/material/ListItemIcon'; import ListItemText from '@mui/material/ListItemText'; import LogoutIcon from '@mui/icons-material/Logout'; -import { useAuth } from '../auth/AuthContext'; +import { useAuth } from '../auth/useAuth'; // Menu open/close state is local; toggling it only re-renders this component. export default function UserMenu() { @@ -22,7 +22,7 @@ export default function UserMenu() { onClick={(e) => setAnchorEl(e.currentTarget)} sx={{ p: 0, width: 32, height: 32, borderRadius: '50%' }} > - + {user?.email?.[0]?.toUpperCase()} diff --git a/src/main.jsx b/src/main.jsx index c7a9c9c..29f8bc2 100644 --- a/src/main.jsx +++ b/src/main.jsx @@ -2,7 +2,7 @@ import { createRoot } from 'react-dom/client'; import CssBaseline from '@mui/material/CssBaseline'; import { ThemeProvider, createTheme } from '@mui/material/styles'; import { SnackbarProvider } from 'notistack'; -import { AuthProvider } from './auth/AuthContext'; +import AuthProvider from './auth/AuthProvider'; import App from './App'; const theme = createTheme({