Updated refresh token endpoint to enable rotation
This commit is contained in:
@@ -7,6 +7,7 @@ import {
|
||||
Anchor,
|
||||
Avatar,
|
||||
Button,
|
||||
Card,
|
||||
Checkbox,
|
||||
Container,
|
||||
Group,
|
||||
@@ -16,13 +17,15 @@ import {
|
||||
PasswordInput,
|
||||
Text,
|
||||
TextInput,
|
||||
Title
|
||||
Title,
|
||||
UnstyledButton
|
||||
} from '@mantine/core';
|
||||
import Cookies from 'js-cookie';
|
||||
import { useEffect, useState } from 'react';
|
||||
import { useForm } from '@mantine/form';
|
||||
import { login, logout, me } from '@/api/auth';
|
||||
import { login, register, logout, me } from '@/api/auth';
|
||||
import { User } from '@/api/auth.types';
|
||||
import { useToggle } from '@mantine/hooks';
|
||||
|
||||
interface HeaderItem {
|
||||
name: string;
|
||||
@@ -68,7 +71,7 @@ const headerItems: HeaderItem[] = [
|
||||
|
||||
export default function Topbar() {
|
||||
const pathName = usePathname();
|
||||
const [showLogin, setShowLogin] = useState(false);
|
||||
const [modalType, toggle] = useToggle([undefined, 'login', 'register', 'reset']);
|
||||
const [headers, setHeaders] = useState<HeaderItem[]>([]);
|
||||
const [user, setUser] = useState<User | undefined>(undefined);
|
||||
useEffect(() => {
|
||||
@@ -108,46 +111,80 @@ export default function Topbar() {
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
<div className='user'>
|
||||
<Menu shadow='md' width={200} trigger='hover' openDelay={100} closeDelay={400}>
|
||||
<Menu.Target>
|
||||
<Avatar style={{ cursor: 'pointer' }} />
|
||||
</Menu.Target>
|
||||
<Menu.Dropdown>
|
||||
{!user && <Menu.Item onClick={() => setShowLogin(true)}>Login</Menu.Item>}
|
||||
{user && (
|
||||
<Menu.Item
|
||||
onClick={async () => {
|
||||
const response = await logout();
|
||||
if (response?.status == 200) {
|
||||
Cookies.remove('logged_in');
|
||||
setUser(undefined);
|
||||
}
|
||||
}}
|
||||
>
|
||||
Logout
|
||||
</Menu.Item>
|
||||
)}
|
||||
</Menu.Dropdown>
|
||||
</Menu>
|
||||
<div className='user-section'>
|
||||
{user ? (
|
||||
<Menu shadow='md' width={200} openDelay={100} closeDelay={400}>
|
||||
<Menu.Target>
|
||||
<UnstyledButton className='user user-button'>
|
||||
<Group>
|
||||
<Avatar />
|
||||
<div style={{ flex: 1 }}>
|
||||
<Text size='sm' fw={500}>
|
||||
{user.first_name} {user.last_name}
|
||||
</Text>
|
||||
|
||||
<Text c='dimmed' size='xs'>
|
||||
{user.role}
|
||||
</Text>
|
||||
</div>
|
||||
</Group>
|
||||
</UnstyledButton>
|
||||
</Menu.Target>
|
||||
<Menu.Dropdown>
|
||||
<Card>
|
||||
<Card.Section h={140} style={{}} />
|
||||
<Avatar size={80} radius={80} mx={'auto'} mt={-30} />
|
||||
<Text ta='center' fz='lg' fw={500} mt='sm'>
|
||||
{user.first_name} {user.last_name}
|
||||
</Text>
|
||||
<Text ta='center' fz='sm' c='dimmed'>
|
||||
{user.role}
|
||||
</Text>
|
||||
<Button
|
||||
fullWidth
|
||||
radius='md'
|
||||
mt='xl'
|
||||
size='md'
|
||||
variant='default'
|
||||
onClick={async () => {
|
||||
const response = await logout();
|
||||
if (response?.status == 200) {
|
||||
Cookies.remove('logged_in');
|
||||
setUser(undefined);
|
||||
}
|
||||
}}
|
||||
>
|
||||
Logout
|
||||
</Button>
|
||||
</Card>
|
||||
</Menu.Dropdown>
|
||||
</Menu>
|
||||
) : (
|
||||
<Group className='user'>
|
||||
<Button onClick={() => toggle('login')}>Login</Button>
|
||||
<Button variant='outline' onClick={() => toggle('register')}>
|
||||
Sign up
|
||||
</Button>
|
||||
</Group>
|
||||
)}
|
||||
</div>
|
||||
</nav>
|
||||
<LoginModal showLogin={showLogin} setShowLogin={setShowLogin} setUser={setUser} />
|
||||
<LoginModal type={modalType} toggle={toggle} setUser={setUser} />
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
function LoginModal({
|
||||
showLogin,
|
||||
setShowLogin,
|
||||
setUser
|
||||
}: {
|
||||
showLogin: boolean;
|
||||
setShowLogin: (show: boolean) => void;
|
||||
interface LoginModalProps {
|
||||
type?: string;
|
||||
toggle: any;
|
||||
setUser: (user: User) => void;
|
||||
}) {
|
||||
}
|
||||
|
||||
function LoginModal({ type, toggle, setUser }: LoginModalProps) {
|
||||
const form = useForm({
|
||||
initialValues: {
|
||||
firstName: '',
|
||||
lastName: '',
|
||||
email: '',
|
||||
password: '',
|
||||
remember: false
|
||||
@@ -155,53 +192,122 @@ function LoginModal({
|
||||
});
|
||||
|
||||
function onClose() {
|
||||
setShowLogin(false);
|
||||
toggle(undefined);
|
||||
if (!form.values.remember) {
|
||||
form.reset();
|
||||
}
|
||||
}
|
||||
|
||||
return (
|
||||
<Modal opened={showLogin} onClose={onClose} withCloseButton={false}>
|
||||
<Container>
|
||||
<Title ta='center'>Welcome back!</Title>
|
||||
<Text c='dimmed' size='sm' ta='center' mt={5}>
|
||||
Do not have an account yet?{' '}
|
||||
<Anchor size='sm' component='button'>
|
||||
Create account
|
||||
</Anchor>
|
||||
</Text>
|
||||
|
||||
<Paper withBorder shadow='md' p={30} mt={30} radius='md'>
|
||||
<form
|
||||
onSubmit={form.onSubmit(async (values) => {
|
||||
const response = await login(values.email, values.password);
|
||||
if (response) {
|
||||
setUser(response.user);
|
||||
onClose();
|
||||
}
|
||||
})}
|
||||
>
|
||||
<TextInput label='Email' placeholder='you@example.com' required {...form.getInputProps('email')} />
|
||||
<PasswordInput
|
||||
label='Password'
|
||||
placeholder='Your password'
|
||||
required
|
||||
mt='md'
|
||||
{...form.getInputProps('password')}
|
||||
/>
|
||||
<Group justify='space-between' mt='lg'>
|
||||
<Checkbox label='Remember me' {...form.getInputProps('remember')} />
|
||||
<Anchor component='button' size='sm'>
|
||||
Forgot password?
|
||||
</Anchor>
|
||||
</Group>
|
||||
<Button type='submit' fullWidth mt='xl'>
|
||||
<Modal opened={type !== undefined} onClose={onClose} withCloseButton={false}>
|
||||
{type == 'reset' ? (
|
||||
<Container>
|
||||
<Title ta='center'>Reset password</Title>
|
||||
<Text c='dimmed' size='sm' ta='center' mt={5}>
|
||||
Enter your email and we will send you a link to reset your password
|
||||
</Text>
|
||||
<Paper withBorder shadow='md' p={30} mt={30} radius='md'>
|
||||
<form
|
||||
onSubmit={form.onSubmit(async (values) => {
|
||||
const response = await login(values.email, values.password);
|
||||
if (response) {
|
||||
setUser(response.user);
|
||||
onClose();
|
||||
}
|
||||
})}
|
||||
>
|
||||
<TextInput label='Email' placeholder='you@example.com' required {...form.getInputProps('email')} />
|
||||
<Button type='submit' fullWidth mt='xl'>
|
||||
Reset password
|
||||
</Button>
|
||||
</form>
|
||||
</Paper>
|
||||
</Container>
|
||||
) : type == 'register' ? (
|
||||
<Container>
|
||||
<Title ta='center'>Create account</Title>
|
||||
<Text c='dimmed' size='sm' ta='center' mt={5}>
|
||||
Already have an account?{' '}
|
||||
<Anchor size='sm' component='button' onClick={() => toggle('login')}>
|
||||
Sign in
|
||||
</Button>
|
||||
</form>
|
||||
</Paper>
|
||||
</Container>
|
||||
</Anchor>
|
||||
</Text>
|
||||
|
||||
<Paper withBorder shadow='md' p={30} mt={30} radius='md'>
|
||||
<form
|
||||
onSubmit={form.onSubmit(async (values) => {
|
||||
const registerResponse = await register({
|
||||
first_name: values.firstName,
|
||||
last_name: values.lastName,
|
||||
email: values.email,
|
||||
password: values.password
|
||||
});
|
||||
if (registerResponse) {
|
||||
const loginResponse = await login(values.email, values.password);
|
||||
if (loginResponse) {
|
||||
setUser(loginResponse.user);
|
||||
onClose();
|
||||
}
|
||||
}
|
||||
})}
|
||||
>
|
||||
<TextInput label='First name' placeholder='John' required {...form.getInputProps('firstName')} />
|
||||
<TextInput label='Last name' placeholder='Smith' required mt='md' {...form.getInputProps('lastName')} />
|
||||
<TextInput label='Email' placeholder='you@example.com' required {...form.getInputProps('email')} />
|
||||
<PasswordInput
|
||||
label='Password'
|
||||
placeholder='Your password'
|
||||
required
|
||||
mt='md'
|
||||
{...form.getInputProps('password')}
|
||||
/>
|
||||
<Button type='submit' fullWidth mt='xl'>
|
||||
Sign up
|
||||
</Button>
|
||||
</form>
|
||||
</Paper>
|
||||
</Container>
|
||||
) : (
|
||||
<Container>
|
||||
<Title ta='center'>Welcome back!</Title>
|
||||
<Text c='dimmed' size='sm' ta='center' mt={5}>
|
||||
Do not have an account yet?{' '}
|
||||
<Anchor size='sm' component='button' onClick={() => toggle('register')}>
|
||||
Create account
|
||||
</Anchor>
|
||||
</Text>
|
||||
|
||||
<Paper withBorder shadow='md' p={30} mt={30} radius='md'>
|
||||
<form
|
||||
onSubmit={form.onSubmit(async (values) => {
|
||||
const response = await login(values.email, values.password);
|
||||
if (response) {
|
||||
setUser(response.user);
|
||||
onClose();
|
||||
}
|
||||
})}
|
||||
>
|
||||
<TextInput label='Email' placeholder='you@example.com' required {...form.getInputProps('email')} />
|
||||
<PasswordInput
|
||||
label='Password'
|
||||
placeholder='Your password'
|
||||
required
|
||||
mt='md'
|
||||
{...form.getInputProps('password')}
|
||||
/>
|
||||
<Group justify='space-between' mt='lg'>
|
||||
<Checkbox label='Remember me' {...form.getInputProps('remember')} />
|
||||
<Anchor component='button' size='sm' onClick={() => toggle('reset')}>
|
||||
Forgot password?
|
||||
</Anchor>
|
||||
</Group>
|
||||
<Button type='submit' fullWidth mt='xl'>
|
||||
Sign in
|
||||
</Button>
|
||||
</form>
|
||||
</Paper>
|
||||
</Container>
|
||||
)}
|
||||
</Modal>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -9,11 +9,6 @@
|
||||
display: flex;
|
||||
}
|
||||
|
||||
.navbar .user {
|
||||
padding-left: 1em;
|
||||
padding-right: 1em;
|
||||
}
|
||||
|
||||
.navbar .title {
|
||||
padding-left: 2em;
|
||||
padding-right: 2em;
|
||||
@@ -25,20 +20,14 @@
|
||||
margin: auto;
|
||||
}
|
||||
|
||||
.navbar .avatar {
|
||||
padding-right: 2em;
|
||||
margin-top: auto;
|
||||
margin-bottom: auto;
|
||||
}
|
||||
|
||||
.header-items {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
}
|
||||
|
||||
.header-items .header-item {
|
||||
padding-left: 2em;
|
||||
padding-right: 2em;
|
||||
padding-left: 2rem;
|
||||
padding-right: 2rem;
|
||||
margin: auto;
|
||||
border-bottom: 2px solid transparent;
|
||||
}
|
||||
@@ -50,3 +39,23 @@
|
||||
.header-items .active {
|
||||
border-bottom: 2px solid #5f5f5f;
|
||||
}
|
||||
|
||||
.user-section {
|
||||
margin-left: 2rem;
|
||||
margin-right: 2rem;
|
||||
}
|
||||
|
||||
.user {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
border-radius: 0.5rem;
|
||||
padding: 0.5rem;
|
||||
padding-left: 1rem;
|
||||
padding-right: 1rem;
|
||||
margin-top: 0.5rem;
|
||||
margin-bottom: 0.5rem;
|
||||
}
|
||||
|
||||
.user-button:hover {
|
||||
background-color: #e6e6e6;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user