feat: logout + better token generation
This commit is contained in:
parent
7991ce7ee0
commit
77234531c5
|
@ -2,9 +2,9 @@ import styles from '@/styles/Dropdown.module.scss';
|
|||
|
||||
import Link from 'next/link';
|
||||
|
||||
import { useRef } from 'react';
|
||||
import { MouseEventHandler, useRef } from 'react';
|
||||
|
||||
export default function Dropdown({ children, items, className }: { children: React.ReactNode, items: { divider?: boolean, icon?: any, label?: string, href?: string }[], className: string }) {
|
||||
export default function Dropdown({ children, items, className }: { children: React.ReactNode, items: { divider?: boolean, icon?: any, label?: string, href?: string, onClick?: MouseEventHandler<HTMLAnchorElement> }[], className: string }) {
|
||||
const dropDownRef = useRef<HTMLDivElement>(null);
|
||||
|
||||
const handleClick = () => {
|
||||
|
@ -26,11 +26,19 @@ export default function Dropdown({ children, items, className }: { children: Rea
|
|||
</li>
|
||||
) || (
|
||||
<li key={i}>
|
||||
<Link href={item.href!}>
|
||||
{item.icon && <item.icon />}
|
||||
{item.href ? (
|
||||
<Link href={item.href!}>
|
||||
{item.icon && <item.icon />}
|
||||
|
||||
{item.label}
|
||||
</Link>
|
||||
{item.label}
|
||||
</Link>
|
||||
) : (
|
||||
<a onClick={item.onClick}>
|
||||
{item.icon && <item.icon />}
|
||||
|
||||
{item.label}
|
||||
</a>
|
||||
)}
|
||||
</li>
|
||||
)
|
||||
))}
|
||||
|
|
|
@ -40,6 +40,8 @@ export default function NavBar({ currentPage }: { currentPage: string }) {
|
|||
|
||||
const { publicRuntimeConfig } = getConfig();
|
||||
|
||||
const router = useRouter();
|
||||
|
||||
const server = {
|
||||
version: '1.12.2'
|
||||
};
|
||||
|
@ -154,7 +156,15 @@ export default function NavBar({ currentPage }: { currentPage: string }) {
|
|||
{
|
||||
icon: LogOut,
|
||||
label: 'Logout',
|
||||
href: '/logout'
|
||||
onClick: async (e) => {
|
||||
e.preventDefault();
|
||||
|
||||
await fetch('/api/v1/session', {
|
||||
method: 'DELETE'
|
||||
});
|
||||
|
||||
router.reload();
|
||||
}
|
||||
}
|
||||
]} className={styles.dropDown}>
|
||||
<User />
|
||||
|
@ -179,7 +189,15 @@ export default function NavBar({ currentPage }: { currentPage: string }) {
|
|||
{
|
||||
icon: LogOut,
|
||||
label: 'Logout',
|
||||
href: '/logout'
|
||||
onClick: async (e) => {
|
||||
e.preventDefault();
|
||||
|
||||
await fetch('/api/v1/session', {
|
||||
method: 'DELETE'
|
||||
});
|
||||
|
||||
router.reload();
|
||||
}
|
||||
}
|
||||
]} className={styles.dropDown}>
|
||||
<User />
|
||||
|
|
|
@ -107,10 +107,11 @@ class Database {
|
|||
|
||||
const sum = crypto.createHmac('sha256', process.env.AUTH_SECRET!);
|
||||
const base = Buffer.from(user.id).toString('base64');
|
||||
const date = Buffer.from((Date.now() - 1688940000000).toString()).toString('base64').replaceAll('=', '');
|
||||
|
||||
sum.update(userData.access_token);
|
||||
|
||||
const sid = base + '.' + sum.digest('hex');
|
||||
const sid = base + '.' + date + '.' + sum.digest('hex');
|
||||
|
||||
const [ result ] = await this.mysqlPool!.execute('INSERT INTO sessions (sid, uid, access_token, refresh_token, id_token, user_agent, expires) VALUES (?, ?, ?, ?, ?, ?, ?)', [
|
||||
sid,
|
||||
|
@ -128,6 +129,10 @@ class Database {
|
|||
}
|
||||
}
|
||||
|
||||
async deleteSession(sid: string): Promise<void> {
|
||||
await this.mysqlPool!.execute('DELETE FROM sessions WHERE sid = ?', [ sid ]);
|
||||
}
|
||||
|
||||
async getSession(sid: string): Promise<any> {
|
||||
const [ rows ] = await this.mysqlPool!.execute('SELECT * FROM sessions WHERE sid = ?', [ sid ]);
|
||||
|
||||
|
|
48
pages/api/v1/session.ts
Normal file
48
pages/api/v1/session.ts
Normal file
|
@ -0,0 +1,48 @@
|
|||
import Database from '@/lib/Database';
|
||||
import type { NextApiRequest, NextApiResponse } from 'next';
|
||||
|
||||
import { serialize } from 'cookie';
|
||||
|
||||
type Data = {
|
||||
success: string;
|
||||
};
|
||||
|
||||
interface Error {
|
||||
error: string;
|
||||
}
|
||||
|
||||
export default async function handler(
|
||||
req: NextApiRequest,
|
||||
res: NextApiResponse<Data | Error>,
|
||||
) {
|
||||
const db = new Database();
|
||||
const sid = req.cookies.session;
|
||||
|
||||
if (!sid)
|
||||
return res.status(401).json({ error: 'Unauthorized' });
|
||||
|
||||
const session = await db.getSession(sid!);
|
||||
|
||||
if (!session)
|
||||
return res.status(401).json({ error: 'Unauthorized' });
|
||||
|
||||
const user = await db.getUser(session.uid);
|
||||
|
||||
if (!user)
|
||||
return res.status(404).json({ error: 'Not Found' });
|
||||
|
||||
if (req.method === 'DELETE') {
|
||||
db.deleteSession(sid!);
|
||||
|
||||
res.setHeader('Set-Cookie', serialize('session', '', {
|
||||
secure: process.env.NODE_ENV === 'production',
|
||||
sameSite: 'strict',
|
||||
path: '/',
|
||||
expires: new Date(0),
|
||||
}));
|
||||
|
||||
return res.status(200).json({ success: 'Delete Session for ' + user.username });
|
||||
}
|
||||
|
||||
res.status(405).json({ error: 'Method Not Allowed' });
|
||||
}
|
|
@ -53,6 +53,8 @@ a {
|
|||
|
||||
text-decoration: none;
|
||||
|
||||
cursor: pointer;
|
||||
|
||||
&:hover {
|
||||
text-decoration: underline;
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue