authentication implemented
This commit is contained in:
@@ -31,6 +31,7 @@ router.post("/signup", async (req, res, next) => {
|
|||||||
if (user) errors.username = "Username already exists";
|
if (user) errors.username = "Username already exists";
|
||||||
|
|
||||||
if (Object.keys(errors).length > 0) {
|
if (Object.keys(errors).length > 0) {
|
||||||
|
console.log(errors);
|
||||||
return res.status(422).json({
|
return res.status(422).json({
|
||||||
message: "User signup failed due to validation errors.",
|
message: "User signup failed due to validation errors.",
|
||||||
errors,
|
errors,
|
||||||
|
|||||||
@@ -22,3 +22,5 @@ dist-ssr
|
|||||||
*.njsproj
|
*.njsproj
|
||||||
*.sln
|
*.sln
|
||||||
*.sw?
|
*.sw?
|
||||||
|
|
||||||
|
.env
|
||||||
+12
-2
@@ -3,7 +3,7 @@ import reactLogo from './assets/react.svg'
|
|||||||
import viteLogo from '/vite.svg'
|
import viteLogo from '/vite.svg'
|
||||||
import './App.css'
|
import './App.css'
|
||||||
import { Button } from '@mantine/core'
|
import { Button } from '@mantine/core'
|
||||||
import { createBrowserRouter, RouterProvider } from 'react-router-dom'
|
import { createBrowserRouter, redirect, RouterProvider } from 'react-router-dom'
|
||||||
import Home from './pages/Home'
|
import Home from './pages/Home'
|
||||||
import MainLayout from './layout/MainLayout'
|
import MainLayout from './layout/MainLayout'
|
||||||
import Settings from './pages/Settings/Settings'
|
import Settings from './pages/Settings/Settings'
|
||||||
@@ -14,12 +14,16 @@ import Themes from './pages/Settings/Themes'
|
|||||||
import PlayLayout from './pages/Play/Layout'
|
import PlayLayout from './pages/Play/Layout'
|
||||||
import PlayFriend from './pages/Play/PlayFriend'
|
import PlayFriend from './pages/Play/PlayFriend'
|
||||||
import Play from './pages/Play/Play'
|
import Play from './pages/Play/Play'
|
||||||
|
import AuthenticationPage, { loginAction, signupAction } from './pages/Authentication/Authentication'
|
||||||
|
import { getAuthToken } from '../utils/auth'
|
||||||
|
import { logoutAction } from './components/Logout'
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
const router = createBrowserRouter([{
|
const router = createBrowserRouter([{
|
||||||
path: '/',
|
path: '/',
|
||||||
element: <MainLayout />,
|
element: <MainLayout />,
|
||||||
|
loader: () => getAuthToken() || redirect('/login'),
|
||||||
children: [
|
children: [
|
||||||
{ index: true, element: <Home /> },
|
{ index: true, element: <Home /> },
|
||||||
{ path: 'home', element: <Home /> },
|
{ path: 'home', element: <Home /> },
|
||||||
@@ -39,8 +43,14 @@ const router = createBrowserRouter([{
|
|||||||
{ path: 'password', element: <Password /> },
|
{ path: 'password', element: <Password /> },
|
||||||
{ path: 'friends', element: <Friends /> },
|
{ path: 'friends', element: <Friends /> },
|
||||||
]
|
]
|
||||||
}
|
},
|
||||||
]
|
]
|
||||||
|
}, {
|
||||||
|
path: '/login', element: <AuthenticationPage isLogin={true} />, action: loginAction, loader: () => { if (getAuthToken()) return redirect('/home'); else return null; }
|
||||||
|
}, {
|
||||||
|
path: '/signup', element: <AuthenticationPage isLogin={false} />, action: signupAction, loader: () => { if (getAuthToken()) return redirect('/signup'); else return null; }
|
||||||
|
}, {
|
||||||
|
path: '/logout', loader: () => { getAuthToken() || redirect('/home') }, action: logoutAction
|
||||||
}]);
|
}]);
|
||||||
|
|
||||||
function App() {
|
function App() {
|
||||||
|
|||||||
@@ -0,0 +1,30 @@
|
|||||||
|
import React from 'react'
|
||||||
|
import { Button, Flex, Modal, Text, Title } from '@mantine/core'
|
||||||
|
import { Form, redirect } from 'react-router-dom'
|
||||||
|
import { useDisclosure } from '@mantine/hooks'
|
||||||
|
|
||||||
|
const Logout = () => {
|
||||||
|
const [isOpen, { open, close }] = useDisclosure(false);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
<Modal opened={isOpen} onClose={close} title={<Title order={3}>Logout</Title>} centered>
|
||||||
|
<Text>Are you sure you want to logout?</Text>
|
||||||
|
<Flex gap={'sm'} my="20px">
|
||||||
|
<Form action='/logout' method='POST'>
|
||||||
|
<Button type="submit" color='red' px='xl'>Logout</Button>
|
||||||
|
</Form>
|
||||||
|
<Button color='gray'>Cancel</Button>
|
||||||
|
</Flex>
|
||||||
|
</Modal>
|
||||||
|
<Button onClick={open} type="submit" color='red' size='md' px='xl'>Logout</Button>
|
||||||
|
</>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
export const logoutAction = ({ request }) => {
|
||||||
|
localStorage.removeItem('token');
|
||||||
|
return redirect('/login');
|
||||||
|
}
|
||||||
|
|
||||||
|
export default Logout
|
||||||
@@ -2,6 +2,7 @@ import { AppShell, Button, Container, Navbar, Paper, Text, useMantineTheme } fro
|
|||||||
import React, { useState } from 'react'
|
import React, { useState } from 'react'
|
||||||
import NavbarLinks from '../components/NavbarLinks';
|
import NavbarLinks from '../components/NavbarLinks';
|
||||||
import { Outlet } from 'react-router-dom'
|
import { Outlet } from 'react-router-dom'
|
||||||
|
import Logout from '../components/Logout';
|
||||||
|
|
||||||
const MainLayout = () => {
|
const MainLayout = () => {
|
||||||
const theme = useMantineTheme();
|
const theme = useMantineTheme();
|
||||||
@@ -27,7 +28,7 @@ const MainLayout = () => {
|
|||||||
<NavbarLinks />
|
<NavbarLinks />
|
||||||
</Navbar.Section>
|
</Navbar.Section>
|
||||||
<Navbar.Section>
|
<Navbar.Section>
|
||||||
<Button color='red' size='md' px='xl'>Logout</Button>
|
<Logout />
|
||||||
</Navbar.Section>
|
</Navbar.Section>
|
||||||
</Navbar>
|
</Navbar>
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,116 @@
|
|||||||
|
import React, { useState } from 'react';
|
||||||
|
import { Button, Card, Container, Text, TextInput } from '@mantine/core';
|
||||||
|
import { Form, redirect, useNavigate } from 'react-router-dom';
|
||||||
|
import { isLoggedIn } from '../../../utils/auth';
|
||||||
|
|
||||||
|
const AuthenticationPage = (props) => {
|
||||||
|
const navigate = useNavigate();
|
||||||
|
const { isLogin } = props;
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Container maw="100%" bg="gray" style={{ display: 'flex', justifyContent: 'center', alignItems: 'center', height: '100vh' }}>
|
||||||
|
<Card shadow="md" p="lg" style={{ maxWidth: 400, width: '100%' }}>
|
||||||
|
<Text align="center" variant="h4" style={{ marginBottom: 20 }}>
|
||||||
|
{isLogin ? 'Login' : 'Sign Up'}
|
||||||
|
</Text>
|
||||||
|
<div>
|
||||||
|
{isLogin ? <LoginForm /> : <SignupForm />}
|
||||||
|
|
||||||
|
{/* Toggle between login and signup */}
|
||||||
|
<Text align="center" style={{ marginBottom: 10 }}>
|
||||||
|
{isLogin ? 'Don\'t have an account?' : 'Already have an account?'}
|
||||||
|
<Button
|
||||||
|
variant="link"
|
||||||
|
color="lime"
|
||||||
|
size="md"
|
||||||
|
p="10px"
|
||||||
|
onClick={() => { navigate(isLogin ? '/signup' : '/login', { replace: true }) }}
|
||||||
|
>
|
||||||
|
<Text color='lime'>
|
||||||
|
{isLogin ? 'Sign Up' : 'Login'}
|
||||||
|
</Text>
|
||||||
|
</Button>
|
||||||
|
</Text>
|
||||||
|
</div>
|
||||||
|
</Card>
|
||||||
|
</Container>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
const LoginForm = () => {
|
||||||
|
return (
|
||||||
|
<Form action='/login' method='POST'>
|
||||||
|
<TextInput name="username" type="text" placeholder="username" style={{ marginBottom: 10, padding: '10px' }} />
|
||||||
|
<TextInput name="password" type="password" placeholder="Password" style={{ marginBottom: 10, padding: '10px' }} />
|
||||||
|
<Button type="submit" fullWidth variant="filled" color="lime" style={{ marginBottom: 10 }}>
|
||||||
|
Login
|
||||||
|
</Button>
|
||||||
|
</Form>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
const SignupForm = () => {
|
||||||
|
return (
|
||||||
|
<Form action='/signup' method='POST'>
|
||||||
|
<TextInput name="username" type="text" placeholder="Username" style={{ marginBottom: 10, padding: '10px' }} />
|
||||||
|
<TextInput name="email" type="email" placeholder="Email" style={{ marginBottom: 10, padding: '10px' }} />
|
||||||
|
<TextInput name="password" type="password" placeholder="Password" style={{ marginBottom: 10, padding: '10px' }} />
|
||||||
|
<Button type="submit" fullWidth variant="filled" color="lime" style={{ marginBottom: 10 }}>
|
||||||
|
Sign Up
|
||||||
|
</Button>
|
||||||
|
</Form>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export const loginAction = async ({ request }) => {
|
||||||
|
const data = await request.formData();
|
||||||
|
let url = `${import.meta.env.VITE_BACKEND_HOST}/api/auth/login`;
|
||||||
|
|
||||||
|
const authData = {
|
||||||
|
username: data.get('username'),
|
||||||
|
password: data.get('password')
|
||||||
|
}
|
||||||
|
|
||||||
|
const response = await fetch(url, {
|
||||||
|
method: 'POST',
|
||||||
|
body: JSON.stringify(authData),
|
||||||
|
headers: {
|
||||||
|
'Content-Type': 'application/json'
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
if (response.status >= 400) return response;
|
||||||
|
else {
|
||||||
|
const responseBody = await response.json();
|
||||||
|
localStorage.setItem('token', responseBody.token);
|
||||||
|
return redirect('/home');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export const signupAction = async ({ request }) => {
|
||||||
|
const data = await request.formData();
|
||||||
|
let url = `${import.meta.env.VITE_BACKEND_HOST}/api/auth/signup`;
|
||||||
|
|
||||||
|
const authData = {
|
||||||
|
username: data.get('username'),
|
||||||
|
email: data.get('email'),
|
||||||
|
password: data.get('password')
|
||||||
|
}
|
||||||
|
|
||||||
|
const response = await fetch(url, {
|
||||||
|
method: 'POST',
|
||||||
|
body: JSON.stringify(authData),
|
||||||
|
headers: {
|
||||||
|
'Content-Type': 'application/json'
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
if (response.status >= 400) return response;
|
||||||
|
else {
|
||||||
|
const responseBody = await response.json();
|
||||||
|
localStorage.setItem('token', responseBody.token);
|
||||||
|
return redirect('/home');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export default AuthenticationPage;
|
||||||
@@ -0,0 +1,16 @@
|
|||||||
|
import { redirect } from "react-router-dom";
|
||||||
|
|
||||||
|
export function checkAuthStatus() {
|
||||||
|
const token = localStorage.getItem("token");
|
||||||
|
if (token) return token;
|
||||||
|
else return redirect("/login");
|
||||||
|
}
|
||||||
|
|
||||||
|
export function isLoggedIn() {
|
||||||
|
if(localStorage.getItem('token')) return true;
|
||||||
|
else return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function getAuthToken() {
|
||||||
|
return localStorage.getItem("token");
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user