improved auth methods

This commit is contained in:
Ben Elferink
2021-09-29 10:45:40 +03:00
parent 562e59d588
commit b629a7014e
4 changed files with 87 additions and 42 deletions

View File

@@ -1,12 +1,11 @@
import {Fragment, useState} from 'react' import {Fragment, useState} from 'react'
import {Dialog, DialogTitle, TextField, Button, CircularProgress} from '@mui/material' import {Dialog, DialogTitle, TextField, Button, CircularProgress} from '@mui/material'
import axios from '../api'
import {useAuth} from '../contexts/AuthContext' import {useAuth} from '../contexts/AuthContext'
const textFieldSx = {mx: 2, my: 0.5} const textFieldSx = {mx: 2, my: 0.5}
export default function AuthModal({open, close, register, toggleRegister}) { export default function AuthModal({open, close, isRegisterMode, toggleRegister}) {
const {setIsLoggedIn, setToken, setAccount} = useAuth() const {login, register} = useAuth()
const [formData, setFormData] = useState({}) const [formData, setFormData] = useState({})
const [loading, setLoading] = useState(false) const [loading, setLoading] = useState(false)
@@ -22,16 +21,10 @@ export default function AuthModal({open, close, register, toggleRegister}) {
setError('') setError('')
try { try {
const requestPath = register ? '/auth/register' : '/auth/login' isRegisterMode ? await register(formData) : await login(formData)
const response = await axios.post(requestPath, formData)
setToken(response.data.token)
setAccount(response.data.data)
setIsLoggedIn(true)
close() close()
} catch (error) { } catch (error) {
console.error(error) setError(error)
setError(error?.response?.data?.message ?? error.message)
} }
setLoading(false) setLoading(false)
@@ -42,7 +35,7 @@ export default function AuthModal({open, close, register, toggleRegister}) {
return ( return (
<Dialog open={open} onClose={close}> <Dialog open={open} onClose={close}>
{register ? ( {isRegisterMode ? (
<RegisterForm formData={formData} handleChange={handleChange} /> <RegisterForm formData={formData} handleChange={handleChange} />
) : ( ) : (
<LoginForm formData={formData} handleChange={handleChange} /> <LoginForm formData={formData} handleChange={handleChange} />
@@ -57,13 +50,13 @@ export default function AuthModal({open, close, register, toggleRegister}) {
) : ( ) : (
<Button <Button
onClick={clickSubmit} onClick={clickSubmit}
disabled={register ? disabledRegisterButton : disabledLoginButton}> disabled={isRegisterMode ? disabledRegisterButton : disabledLoginButton}>
{register ? 'Register' : 'Login'} {isRegisterMode ? 'Register' : 'Login'}
</Button> </Button>
)} )}
<Button onClick={toggleRegister}> <Button onClick={toggleRegister}>
{register ? 'I already have an account' : "I don't have an account"} {isRegisterMode ? 'I already have an account' : "I don't have an account"}
</Button> </Button>
</Dialog> </Dialog>
) )
@@ -78,7 +71,7 @@ function LoginForm({formData, handleChange}) {
label='Username' label='Username'
name='username' name='username'
type='text' type='text'
value={formData['username'] ?? ''} value={formData['username'] || ''}
onChange={handleChange} onChange={handleChange}
variant='filled' variant='filled'
sx={textFieldSx} sx={textFieldSx}
@@ -88,7 +81,7 @@ function LoginForm({formData, handleChange}) {
label='Password' label='Password'
name='password' name='password'
type='password' type='password'
value={formData['password'] ?? ''} value={formData['password'] || ''}
onChange={handleChange} onChange={handleChange}
variant='filled' variant='filled'
sx={textFieldSx} sx={textFieldSx}
@@ -107,7 +100,7 @@ function RegisterForm({formData, handleChange}) {
label='Username' label='Username'
name='username' name='username'
type='text' type='text'
value={formData['username'] ?? ''} value={formData['username'] || ''}
onChange={handleChange} onChange={handleChange}
variant='filled' variant='filled'
sx={textFieldSx} sx={textFieldSx}
@@ -117,7 +110,7 @@ function RegisterForm({formData, handleChange}) {
label='Password' label='Password'
name='password' name='password'
type='password' type='password'
value={formData['password'] ?? ''} value={formData['password'] || ''}
onChange={handleChange} onChange={handleChange}
variant='filled' variant='filled'
sx={textFieldSx} sx={textFieldSx}

View File

@@ -40,7 +40,7 @@ export default function Header() {
<IconButton onClick={openPopover}> <IconButton onClick={openPopover}>
<OnlineIndicator online={isLoggedIn}> <OnlineIndicator online={isLoggedIn}>
<Avatar src={account?.username ?? ''} alt={account?.username ?? ''} /> <Avatar src={account?.username || ''} alt={account?.username || ''} />
</OnlineIndicator> </OnlineIndicator>
</IconButton> </IconButton>
@@ -69,7 +69,7 @@ export default function Header() {
<AuthModal <AuthModal
open={authModal} open={authModal}
close={() => setAuthModal(false)} close={() => setAuthModal(false)}
register={register} isRegisterMode={register}
toggleRegister={() => setRegister((prev) => !prev)} toggleRegister={() => setRegister((prev) => !prev)}
/> />
</header> </header>

View File

@@ -11,14 +11,71 @@ export function useAuth() {
// export the provider (handle all the logic here) // export the provider (handle all the logic here)
export function AuthProvider({children}) { export function AuthProvider({children}) {
const [token, setToken] = useState(localStorage.getItem('token') ?? null)
const [account, setAccount] = useState(null)
const [isLoggedIn, setIsLoggedIn] = useState(false) const [isLoggedIn, setIsLoggedIn] = useState(false)
const [account, setAccount] = useState(null)
const [token, setToken] = useState(localStorage.getItem('token') || null)
const register = (formData = {}) =>
new Promise((resolve, reject) => {
axios
.post('/auth/register', formData)
.then(({data: {data, token}}) => {
setAccount(data)
setToken(token)
setIsLoggedIn(true)
resolve(true)
})
.catch((error) => {
console.error(error)
reject(error?.response?.data?.message || error.message)
})
})
const login = (formData = {}) =>
new Promise((resolve, reject) => {
axios
.post('/auth/login', formData)
.then(({data: {data, token}}) => {
setAccount(data)
setToken(token)
setIsLoggedIn(true)
resolve(true)
})
.catch((error) => {
console.error(error)
reject(error?.response?.data?.message || error.message)
})
})
const logout = () => { const logout = () => {
setToken(null)
setAccount(null)
setIsLoggedIn(false) setIsLoggedIn(false)
setAccount(null)
setToken(null)
}
const getAccount = async () => {
try {
const headers = {headers: {authorization: `Bearer ${token}`}}
const response = await axios.get('/auth/account', headers)
setAccount(response.data.data)
setIsLoggedIn(true)
} catch (error) {
console.error(error)
if (error?.response?.statusCode === 401) setToken(null)
}
}
const getTokenPayload = () => {
if (!token) {
console.warn(`Token is ${null}/${undefined}`)
return {}
}
const informativePart = token.split('.')[1]
const payload = JSON.parse(window.atob(informativePart))
return payload
} }
// This side effect keeps local storage updated with recent token value, // This side effect keeps local storage updated with recent token value,
@@ -32,28 +89,23 @@ export function AuthProvider({children}) {
}, [token]) }, [token])
// This side effect runs only if we have a token, but no account or logged-in boolean. // This side effect runs only if we have a token, but no account or logged-in boolean.
// This "if" statement applies only when refreshed, or re-opened the browser, // This "if" statement is "true" only when refreshed, or re-opened the browser,
// if true, it will then ask the backend for the account information (and will get them if the token hasn't expired) // if true, it will then ask the backend for the account information (and will get them if the token hasn't expired)
useEffect(() => { useEffect(() => {
if (!isLoggedIn && !account && token) { if (!isLoggedIn && !account && token) getAccount()
;(async () => {
try {
const headers = {headers: {authorization: `Bearer ${token}`}}
const response = await axios.get('/auth/account', headers)
setAccount(response.data.data)
setIsLoggedIn(true)
} catch (error) {
console.error(error)
if (error?.response?.statusCode === 401) setToken(null)
}
})()
}
}, [isLoggedIn, account, token]) // eslint-disable-line react-hooks/exhaustive-deps }, [isLoggedIn, account, token]) // eslint-disable-line react-hooks/exhaustive-deps
return ( return (
<AuthContext.Provider <AuthContext.Provider
value={{isLoggedIn, setIsLoggedIn, token, setToken, account, setAccount, logout}}> value={{
isLoggedIn,
account,
token,
register,
login,
logout,
getTokenPayload,
}}>
{children} {children}
</AuthContext.Provider> </AuthContext.Provider>
) )

View File

@@ -1,6 +1,6 @@
:root { :root {
--online: #44b700; --online: #44b700;
--offline: rgb(183, 68, 0); --offline: #b74400;
} }
body { body {