Added guard for unauthorized/authorized users

This commit is contained in:
Sianida26 2024-01-22 13:17:35 +07:00
parent a5090d2cc3
commit 047e1f6fa9
13 changed files with 115 additions and 16 deletions

View File

@ -23,6 +23,7 @@
"@types/bcrypt": "^5.0.2", "@types/bcrypt": "^5.0.2",
"@types/jsonwebtoken": "^9.0.5", "@types/jsonwebtoken": "^9.0.5",
"bcrypt": "^5.1.1", "bcrypt": "^5.1.1",
"client-only": "^0.0.1",
"clsx": "^2.1.0", "clsx": "^2.1.0",
"jsonwebtoken": "^9.0.2", "jsonwebtoken": "^9.0.2",
"next": "14.1.0", "next": "14.1.0",

View File

@ -47,6 +47,9 @@ dependencies:
bcrypt: bcrypt:
specifier: ^5.1.1 specifier: ^5.1.1
version: 5.1.1 version: 5.1.1
client-only:
specifier: ^0.0.1
version: 0.0.1
clsx: clsx:
specifier: ^2.1.0 specifier: ^2.1.0
version: 2.1.0 version: 2.1.0

View File

@ -0,0 +1,14 @@
import React from "react";
import guestOnly from "@/features/auth/actions/guestOnly";
interface Props {
children: React.ReactNode;
}
export default async function LoginLayout({ children }: Props) {
await guestOnly()
return <>{children}</>;
}

View File

@ -1,5 +1,6 @@
"use client"; "use client";
import getUser from "@/features/auth/actions/getUser";
import signIn from "@/features/auth/actions/signIn"; import signIn from "@/features/auth/actions/signIn";
import { import {
Paper, Paper,
@ -12,7 +13,9 @@ import {
Button, Button,
Alert, Alert,
} from "@mantine/core"; } from "@mantine/core";
import React from "react"; import { redirect } from "next/navigation";
import { useRouter } from "next/navigation";
import React, { useEffect, useState } from "react";
import { useFormState } from "react-dom"; import { useFormState } from "react-dom";
const initialState = { const initialState = {

View File

@ -0,0 +1,21 @@
"use client"
import getUser from "@//features/auth/actions/getUser";
import logout from "@/features/auth/actions/logout";
import { redirect } from "next/navigation";
import React, { useEffect } from "react";
/**
* LogoutPage component handles the logout process.
* It checks if a user is logged in, logs them out, and redirects to the login page.
*/
export default function LogoutPage() {
useEffect(() => {
const logoutAction = async () => await logout()
logoutAction()
}, [])
return <div></div>;
}

View File

@ -0,0 +1,14 @@
import React from "react";
import guestOnly from "@/features/auth/actions/guestOnly";
interface Props {
children: React.ReactNode;
}
export default async function RegisterLayout({ children }: Props) {
await guestOnly()
return <>{children}</>;
}

View File

@ -13,9 +13,9 @@ interface Props {
children: React.ReactNode children: React.ReactNode
} }
export default function Layout(props: Props) { export default async function Layout(props: Props) {
const user = getUser() const user = await getUser()
if (!user){ if (!user){
redirect("/login") redirect("/login")

View File

@ -15,7 +15,7 @@ import logo from "@/assets/logos/logo-dsg.png";
import cx from "clsx"; import cx from "clsx";
import classNames from "./styles.module.css"; import classNames from "./styles.module.css";
import { TbChevronDown, TbLogout, TbSettings } from "react-icons/tb"; import { TbChevronDown, TbLogout, TbSettings } from "react-icons/tb";
import userMenuItems from "./_data/UserMenuItems"; import userMenuItems from "./_data/userMenuItems";
import UserMenuItem from "./_components/UserMenuItem/UserMenuItem"; import UserMenuItem from "./_components/UserMenuItem/UserMenuItem";
interface Props { interface Props {

View File

@ -1,6 +1,6 @@
import { Menu, rem } from '@mantine/core' import { Menu, rem } from '@mantine/core'
import React from 'react' import React from 'react'
import { UserMenuItem } from '../../_data/UserMenuItems' import { UserMenuItem } from '../../_data/userMenuItems'
interface Props { interface Props {
item: UserMenuItem item: UserMenuItem
@ -10,12 +10,14 @@ export default function UserMenuItem({item}: Props) {
return ( return (
<Menu.Item <Menu.Item
color={item.color} color={item.color}
component='a'
leftSection={ leftSection={
<item.icon <item.icon
style={{ width: rem(16), height: rem(16) }} style={{ width: rem(16), height: rem(16) }}
strokeWidth={1.5} strokeWidth={1.5}
/> />
} }
href={item.href}
> >
{item.label} {item.label}
</Menu.Item> </Menu.Item>

View File

@ -5,7 +5,8 @@ import { TbLogout, TbSettings } from "react-icons/tb"
export interface UserMenuItem { export interface UserMenuItem {
label: string, label: string,
icon: React.FC<any>, icon: React.FC<any>,
color?: ThemeIconProps['color'] color?: ThemeIconProps['color'],
href?: string,
} }
const userMenuItems: UserMenuItem[] = [ const userMenuItems: UserMenuItem[] = [
@ -16,7 +17,8 @@ const userMenuItems: UserMenuItem[] = [
{ {
label: "Logout", label: "Logout",
icon: TbLogout, icon: TbLogout,
color: "red" color: "red",
href: "/logout"
} }
]; ];

View File

@ -4,13 +4,17 @@ import { cookies } from "next/headers"
import "server-only" import "server-only"
import { decodeJwtToken } from "../authUtils"; import { decodeJwtToken } from "../authUtils";
import prisma from "@/db"; import prisma from "@/db";
import AuthError, { AuthErrorCode } from "../AuthError";
import logout from "./logout";
export default async function getUser(){ export default async function getUser(){
try {
const token = cookies().get('token'); const token = cookies().get('token');
if (!token) return null; if (!token) return null;
const decodedToken = decodeJwtToken(token.value) as {id: string, iat: number}; const decodedToken = decodeJwtToken(token.value) as {id: string, iat: number};
console.log('token', decodedToken)
const user = await prisma.user.findFirst({ const user = await prisma.user.findFirst({
where: { where: {
@ -19,4 +23,10 @@ export default async function getUser(){
}); });
return user; return user;
} catch (e: unknown){
if (e instanceof AuthError && e.errorCode === AuthErrorCode.INVALID_JWT_TOKEN){
return null;
}
throw e;
}
} }

View File

@ -0,0 +1,13 @@
"use server"
import { redirect } from "next/navigation";
import getUser from "./getUser"
export default async function guestOnly(){
const user = await getUser();
if (user){
redirect("dashboard")
}
}

View File

@ -0,0 +1,16 @@
"use server"
import { cookies } from "next/headers"
import { redirect } from "next/navigation";
import "server-only"
/**
* Handles user logout by deleting the authentication token and redirecting to the login page.
* This function is intended to be used on the server side.
*
* @returns A promise that resolves when the logout process is complete.
*/
export default async function logout(){
cookies().delete("token");
redirect("/login")
}