From 77234531c59a0eccdd55cb2bc5aee5add8a387c2 Mon Sep 17 00:00:00 2001 From: TheClashFruit Date: Thu, 29 Aug 2024 16:06:07 +0200 Subject: [PATCH] feat: logout + better token generation --- components/Dropdown.tsx | 20 +++++++++++------ components/NavBar.tsx | 22 +++++++++++++++++-- lib/Database.ts | 7 +++++- pages/api/v1/session.ts | 48 +++++++++++++++++++++++++++++++++++++++++ styles/globals.scss | 2 ++ 5 files changed, 90 insertions(+), 9 deletions(-) create mode 100644 pages/api/v1/session.ts diff --git a/components/Dropdown.tsx b/components/Dropdown.tsx index 9553613..3a63774 100644 --- a/components/Dropdown.tsx +++ b/components/Dropdown.tsx @@ -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 }[], className: string }) { const dropDownRef = useRef(null); const handleClick = () => { @@ -26,11 +26,19 @@ export default function Dropdown({ children, items, className }: { children: Rea ) || (
  • - - {item.icon && } + {item.href ? ( + + {item.icon && } - {item.label} - + {item.label} + + ) : ( + + {item.icon && } + + {item.label} + + )}
  • ) ))} diff --git a/components/NavBar.tsx b/components/NavBar.tsx index 0eb5be1..4a39199 100644 --- a/components/NavBar.tsx +++ b/components/NavBar.tsx @@ -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}> @@ -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}> diff --git a/lib/Database.ts b/lib/Database.ts index e13ce70..2a884b2 100644 --- a/lib/Database.ts +++ b/lib/Database.ts @@ -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 { + await this.mysqlPool!.execute('DELETE FROM sessions WHERE sid = ?', [ sid ]); + } + async getSession(sid: string): Promise { const [ rows ] = await this.mysqlPool!.execute('SELECT * FROM sessions WHERE sid = ?', [ sid ]); diff --git a/pages/api/v1/session.ts b/pages/api/v1/session.ts new file mode 100644 index 0000000..bae72ba --- /dev/null +++ b/pages/api/v1/session.ts @@ -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, +) { + 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' }); +} \ No newline at end of file diff --git a/styles/globals.scss b/styles/globals.scss index 8d682a1..18ea9d8 100644 --- a/styles/globals.scss +++ b/styles/globals.scss @@ -53,6 +53,8 @@ a { text-decoration: none; + cursor: pointer; + &:hover { text-decoration: underline; }