authentication implemented

This commit is contained in:
Moon Patel
2023-06-27 18:28:24 +05:30
parent b3fd3ade5c
commit 02b302e2c8
7 changed files with 179 additions and 3 deletions
+1
View File
@@ -31,6 +31,7 @@ router.post("/signup", async (req, res, next) => {
if (user) errors.username = "Username already exists";
if (Object.keys(errors).length > 0) {
console.log(errors);
return res.status(422).json({
message: "User signup failed due to validation errors.",
errors,
+2
View File
@@ -22,3 +22,5 @@ dist-ssr
*.njsproj
*.sln
*.sw?
.env
+12 -2
View File
@@ -3,7 +3,7 @@ import reactLogo from './assets/react.svg'
import viteLogo from '/vite.svg'
import './App.css'
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 MainLayout from './layout/MainLayout'
import Settings from './pages/Settings/Settings'
@@ -14,12 +14,16 @@ import Themes from './pages/Settings/Themes'
import PlayLayout from './pages/Play/Layout'
import PlayFriend from './pages/Play/PlayFriend'
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([{
path: '/',
element: <MainLayout />,
loader: () => getAuthToken() || redirect('/login'),
children: [
{ index: true, element: <Home /> },
{ path: 'home', element: <Home /> },
@@ -39,8 +43,14 @@ const router = createBrowserRouter([{
{ path: 'password', element: <Password /> },
{ 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() {
+30
View File
@@ -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 -1
View File
@@ -2,6 +2,7 @@ import { AppShell, Button, Container, Navbar, Paper, Text, useMantineTheme } fro
import React, { useState } from 'react'
import NavbarLinks from '../components/NavbarLinks';
import { Outlet } from 'react-router-dom'
import Logout from '../components/Logout';
const MainLayout = () => {
const theme = useMantineTheme();
@@ -27,7 +28,7 @@ const MainLayout = () => {
<NavbarLinks />
</Navbar.Section>
<Navbar.Section>
<Button color='red' size='md' px='xl'>Logout</Button>
<Logout />
</Navbar.Section>
</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;
+16
View File
@@ -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");
}