├── frontend ├── src │ ├── components │ │ ├── AllRoutes │ │ │ ├── Loading │ │ │ │ ├── Loading.module.css │ │ │ │ └── Loading.tsx │ │ │ ├── Layout │ │ │ │ ├── Layout.module.css │ │ │ │ └── Layout.tsx │ │ │ ├── CustomInput │ │ │ │ └── CustomInput.tsx │ │ │ ├── CustomAlert │ │ │ │ └── CustomAlert.tsx │ │ │ ├── CustomLink │ │ │ │ └── CustomLink.tsx │ │ │ └── CustomButton │ │ │ │ └── CustomButton.tsx │ │ ├── Private │ │ │ ├── Main │ │ │ │ ├── Main.module.css │ │ │ │ └── Main.tsx │ │ │ ├── FlexContainer │ │ │ │ ├── FlexContainer.module.css │ │ │ │ └── FlexContainer.tsx │ │ │ ├── Footer │ │ │ │ ├── Footer.module.css │ │ │ │ └── Footer.tsx │ │ │ ├── Header │ │ │ │ ├── Header.module.css │ │ │ │ └── Header.tsx │ │ │ └── HeaderDropdown │ │ │ │ └── HeaderDropdown.tsx │ │ └── Public │ │ │ ├── GoogleIdentityServices │ │ │ ├── Globals.d.ts │ │ │ ├── GoogleIdentityServices.module.css │ │ │ └── GoogleIdentityServices.tsx │ │ │ ├── FirebaseGoogleSignInButton │ │ │ ├── FirebaseGoogleSignInButton.module.css │ │ │ └── FirebaseGoogleSignInButton.tsx │ │ │ └── FirebaseFacebookSignInButton │ │ │ ├── FirebaseFacebookSignInButton.module.css │ │ │ └── FirebaseFacebookSignInButton.tsx │ ├── Globals.d.ts │ ├── assets │ │ ├── github.png │ │ ├── google.png │ │ ├── facebook.png │ │ ├── instagram.png │ │ ├── linkedin.png │ │ ├── logo-header.png │ │ ├── landing_page_icon_1.png │ │ ├── spinner-circle-dark.svg │ │ ├── spinner-circle-light.svg │ │ ├── landing_page_icon_3.svg │ │ ├── landing_page_icon_4.svg │ │ └── login-background.svg │ ├── interfaces │ │ └── index.ts │ ├── pages │ │ ├── Private │ │ │ └── Home │ │ │ │ ├── Home.module.css │ │ │ │ └── Home.tsx │ │ ├── Public │ │ │ ├── AccountActivation │ │ │ │ ├── AccountActivation.module.css │ │ │ │ └── AccountActivation.tsx │ │ │ ├── ForgotPassword │ │ │ │ ├── ForgotPassword.module.css │ │ │ │ └── ForgotPassword.tsx │ │ │ ├── Login │ │ │ │ ├── Login.module.css │ │ │ │ └── Login.tsx │ │ │ ├── ResetPassword │ │ │ │ ├── ResetPassword.module.css │ │ │ │ └── ResetPassword.tsx │ │ │ └── Register │ │ │ │ └── Register.module.css │ │ └── MFA │ │ │ └── LoginVerificationCode │ │ │ ├── LoginVerificationCode.module.css │ │ │ └── LoginVerificationCode.tsx │ ├── reducers │ │ ├── index.ts │ │ ├── isDisableReducer.ts │ │ └── errorReducer.ts │ ├── actions │ │ └── index.ts │ ├── App.css │ ├── config │ │ └── firebase-config.ts │ ├── routes │ │ ├── MFARoutes.tsx │ │ ├── PrivateRoutes.tsx │ │ └── PublicRoutes.tsx │ ├── helpers │ │ └── auth.ts │ ├── index.tsx │ ├── App.tsx │ └── App.module.css ├── public │ ├── robots.txt │ ├── favicon.ico │ ├── logo192.png │ ├── logo512.png │ ├── manifest.json │ └── index.html ├── tailwind.config.js ├── tsconfig.json ├── .env.example └── package.json ├── backend ├── nodemon.json ├── src │ ├── constants │ │ ├── v1AuthenticationUserSettings.ts │ │ ├── v1AuthenticationJWTTokensSettings.ts │ │ ├── v1AuthenticationCookiesSettings.ts │ │ └── v1AuthenticationErrorCodes.ts │ ├── config │ │ └── firebase-credential.example.json │ ├── utils │ │ ├── tryCatch.ts │ │ ├── ErrorResponse.ts │ │ ├── generateRandomUsernameSSO.ts │ │ ├── generateRandomPasswordSSO.ts │ │ └── sendEmail.ts │ ├── models │ │ ├── csrfTokenSecretModel.ts │ │ ├── googleAuthenticatorModel.ts │ │ ├── profileModel.ts │ │ └── userModel.ts │ ├── types │ │ └── index.ts │ ├── interfaces │ │ └── index.ts │ ├── middlewares │ │ ├── errorHandler.ts │ │ ├── v1AuthenticationLimiter.ts │ │ └── index.ts │ ├── routes │ │ └── v1AuthenticationRouter.ts │ └── server.ts ├── .env.example ├── Globals.d.ts ├── package.json └── tsconfig.json ├── screenshots ├── image.png ├── image-1.png ├── image-2.png ├── image-3.png ├── image-4.png ├── image-5.png ├── image-6.png ├── image-7.png ├── image-8.png └── image-9.png ├── .gitignore └── README.md /frontend/src/components/AllRoutes/Loading/Loading.module.css: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /frontend/src/components/Private/Main/Main.module.css: -------------------------------------------------------------------------------- 1 | .main { 2 | height: auto; 3 | } -------------------------------------------------------------------------------- /frontend/public/robots.txt: -------------------------------------------------------------------------------- 1 | # https://www.robotstxt.org/robotstxt.html 2 | User-agent: * 3 | Disallow: 4 | -------------------------------------------------------------------------------- /backend/nodemon.json: -------------------------------------------------------------------------------- 1 | { 2 | "watch": ["src"], 3 | "ext": ".ts,.js", 4 | "exec": "ts-node --esm ./src/server.ts" 5 | } -------------------------------------------------------------------------------- /screenshots/image.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GabrielSalangsang013/ts-mern-with-auth-boilerplate/HEAD/screenshots/image.png -------------------------------------------------------------------------------- /frontend/src/components/Public/GoogleIdentityServices/Globals.d.ts: -------------------------------------------------------------------------------- 1 | export {}; 2 | declare global { 3 | const google: any; 4 | } -------------------------------------------------------------------------------- /screenshots/image-1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GabrielSalangsang013/ts-mern-with-auth-boilerplate/HEAD/screenshots/image-1.png -------------------------------------------------------------------------------- /screenshots/image-2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GabrielSalangsang013/ts-mern-with-auth-boilerplate/HEAD/screenshots/image-2.png -------------------------------------------------------------------------------- /screenshots/image-3.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GabrielSalangsang013/ts-mern-with-auth-boilerplate/HEAD/screenshots/image-3.png -------------------------------------------------------------------------------- /screenshots/image-4.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GabrielSalangsang013/ts-mern-with-auth-boilerplate/HEAD/screenshots/image-4.png -------------------------------------------------------------------------------- /screenshots/image-5.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GabrielSalangsang013/ts-mern-with-auth-boilerplate/HEAD/screenshots/image-5.png -------------------------------------------------------------------------------- /screenshots/image-6.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GabrielSalangsang013/ts-mern-with-auth-boilerplate/HEAD/screenshots/image-6.png -------------------------------------------------------------------------------- /screenshots/image-7.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GabrielSalangsang013/ts-mern-with-auth-boilerplate/HEAD/screenshots/image-7.png -------------------------------------------------------------------------------- /screenshots/image-8.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GabrielSalangsang013/ts-mern-with-auth-boilerplate/HEAD/screenshots/image-8.png -------------------------------------------------------------------------------- /screenshots/image-9.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GabrielSalangsang013/ts-mern-with-auth-boilerplate/HEAD/screenshots/image-9.png -------------------------------------------------------------------------------- /frontend/public/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GabrielSalangsang013/ts-mern-with-auth-boilerplate/HEAD/frontend/public/favicon.ico -------------------------------------------------------------------------------- /frontend/public/logo192.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GabrielSalangsang013/ts-mern-with-auth-boilerplate/HEAD/frontend/public/logo192.png -------------------------------------------------------------------------------- /frontend/public/logo512.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GabrielSalangsang013/ts-mern-with-auth-boilerplate/HEAD/frontend/public/logo512.png -------------------------------------------------------------------------------- /frontend/src/Globals.d.ts: -------------------------------------------------------------------------------- 1 | declare module "*.module.css"; 2 | declare module "*.module.scss"; 3 | declare module "*.png"; 4 | declare module "*.svg"; -------------------------------------------------------------------------------- /frontend/src/assets/github.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GabrielSalangsang013/ts-mern-with-auth-boilerplate/HEAD/frontend/src/assets/github.png -------------------------------------------------------------------------------- /frontend/src/assets/google.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GabrielSalangsang013/ts-mern-with-auth-boilerplate/HEAD/frontend/src/assets/google.png -------------------------------------------------------------------------------- /frontend/src/assets/facebook.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GabrielSalangsang013/ts-mern-with-auth-boilerplate/HEAD/frontend/src/assets/facebook.png -------------------------------------------------------------------------------- /frontend/src/assets/instagram.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GabrielSalangsang013/ts-mern-with-auth-boilerplate/HEAD/frontend/src/assets/instagram.png -------------------------------------------------------------------------------- /frontend/src/assets/linkedin.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GabrielSalangsang013/ts-mern-with-auth-boilerplate/HEAD/frontend/src/assets/linkedin.png -------------------------------------------------------------------------------- /frontend/src/assets/logo-header.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GabrielSalangsang013/ts-mern-with-auth-boilerplate/HEAD/frontend/src/assets/logo-header.png -------------------------------------------------------------------------------- /frontend/src/assets/landing_page_icon_1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GabrielSalangsang013/ts-mern-with-auth-boilerplate/HEAD/frontend/src/assets/landing_page_icon_1.png -------------------------------------------------------------------------------- /frontend/src/components/AllRoutes/Loading/Loading.tsx: -------------------------------------------------------------------------------- 1 | const Loading = () => { 2 | return ( 3 | <> 4 | 5 | ) 6 | } 7 | 8 | export default Loading; -------------------------------------------------------------------------------- /frontend/src/components/Private/FlexContainer/FlexContainer.module.css: -------------------------------------------------------------------------------- 1 | .flex_container { 2 | width: min(100% - 2rem, 1216px); 3 | margin: auto; 4 | height: 100%; 5 | } -------------------------------------------------------------------------------- /frontend/src/interfaces/index.ts: -------------------------------------------------------------------------------- 1 | export interface AllReducers { 2 | error: { 3 | hasError: boolean; 4 | errorMessage: string; 5 | }; 6 | isDisabled: boolean; 7 | } -------------------------------------------------------------------------------- /frontend/src/components/Private/Footer/Footer.module.css: -------------------------------------------------------------------------------- 1 | .footer { 2 | height: 3rem; 3 | width: 100%; 4 | height: 4rem; 5 | background-color: #1f2937; 6 | border-top: 1px solid #323c4b; 7 | } -------------------------------------------------------------------------------- /frontend/src/pages/Private/Home/Home.module.css: -------------------------------------------------------------------------------- 1 | .box_message { 2 | padding: 1.5rem; 3 | background-color: #1f2937; 4 | width: 100%; 5 | margin-top: 3rem; 6 | margin-bottom: 3rem; 7 | border-radius: 0.5rem; 8 | } -------------------------------------------------------------------------------- /frontend/tailwind.config.js: -------------------------------------------------------------------------------- 1 | /** @type {import('tailwindcss').Config} */ 2 | module.exports = { 3 | content: [ 4 | "./src/**/*.{js,jsx,ts,tsx}", 5 | ], 6 | theme: { 7 | extend: {}, 8 | }, 9 | plugins: [], 10 | } 11 | 12 | -------------------------------------------------------------------------------- /backend/src/constants/v1AuthenticationUserSettings.ts: -------------------------------------------------------------------------------- 1 | export const DEFAULT_PROFILE_PICTURE: string = "https://res.cloudinary.com/dgo6vnzjl/image/upload/c_fill,q_50,w_150/v1685085963/default_male_avatar_xkpekq.webp"; // * DEFAULT PROFILE PICTURE OF USER -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # See https://help.github.com/articles/ignoring-files/ for more about ignoring files. 2 | 3 | /frontend/.env 4 | /frontend/node_modules 5 | 6 | /backend/node_modules 7 | /backend/dist 8 | /backend/.env 9 | /backend/src/config/firebase-credential.json -------------------------------------------------------------------------------- /backend/src/config/firebase-credential.example.json: -------------------------------------------------------------------------------- 1 | { 2 | "type": "", 3 | "project_id": "", 4 | "private_key_id": "", 5 | "private_key": "", 6 | "client_email": "", 7 | "client_id": "", 8 | "auth_uri": "", 9 | "token_uri": "", 10 | "auth_provider_x509_cert_url": "", 11 | "client_x509_cert_url": "", 12 | "universe_domain": "" 13 | } -------------------------------------------------------------------------------- /backend/src/utils/tryCatch.ts: -------------------------------------------------------------------------------- 1 | import express from 'express'; 2 | 3 | const tryCatch = (controller: any) => async (req: express.Request, res: express.Response, next: express.NextFunction) => { 4 | try { 5 | await controller(req, res); 6 | } catch (error) { 7 | return next(error); 8 | } 9 | }; 10 | 11 | export default tryCatch; -------------------------------------------------------------------------------- /backend/src/utils/ErrorResponse.ts: -------------------------------------------------------------------------------- 1 | class ErrorResponse extends Error { 2 | statusCode: number; 3 | errorCode: number; 4 | 5 | constructor(statusCode: number, message: string, errorCode: number) { 6 | super(message); 7 | this.statusCode = statusCode; 8 | this.errorCode = errorCode; 9 | } 10 | } 11 | 12 | export default ErrorResponse; -------------------------------------------------------------------------------- /frontend/src/components/AllRoutes/Layout/Layout.module.css: -------------------------------------------------------------------------------- 1 | .container { 2 | min-height: 100vh; 3 | height: auto; 4 | background-color: #111827; 5 | display: grid; 6 | grid-template-rows: auto 1fr auto; 7 | grid-template-columns: 100%; 8 | } 9 | 10 | .disabledContainer { 11 | opacity: 0.5; 12 | pointer-events: none; 13 | } -------------------------------------------------------------------------------- /frontend/src/reducers/index.ts: -------------------------------------------------------------------------------- 1 | import { combineReducers } from 'redux'; 2 | 3 | // * ALL REDUCERS 4 | import isDisableReducer from './isDisableReducer'; 5 | import errorReducer from './errorReducer'; 6 | // * END ALL REDUCERS 7 | 8 | const allReducers = combineReducers({ 9 | isDisabled: isDisableReducer, 10 | error: errorReducer 11 | }) 12 | 13 | export default allReducers; -------------------------------------------------------------------------------- /frontend/src/components/Private/Main/Main.tsx: -------------------------------------------------------------------------------- 1 | import { ReactNode } from 'react'; 2 | import style from './Main.module.css'; 3 | 4 | interface MainProps { 5 | children: ReactNode; 6 | } 7 | 8 | const Main = ({children}: MainProps) => { 9 | return ( 10 |
11 | {children} 12 |
13 | ) 14 | } 15 | 16 | export default Main; -------------------------------------------------------------------------------- /frontend/src/assets/spinner-circle-dark.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /frontend/src/assets/spinner-circle-light.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /frontend/src/components/Private/Footer/Footer.tsx: -------------------------------------------------------------------------------- 1 | import style from './Footer.module.css'; 2 | import FlexContainer from '../../../components/Private/FlexContainer/FlexContainer'; 3 | 4 | const Footer = () => { 5 | return ( 6 | 11 | ) 12 | } 13 | 14 | export default Footer; -------------------------------------------------------------------------------- /frontend/src/reducers/isDisableReducer.ts: -------------------------------------------------------------------------------- 1 | type actionType = { 2 | type: string 3 | } 4 | 5 | const isDisableReducer = (state: boolean = false, action: actionType) => { 6 | switch(action.type) { 7 | case 'SET_DISABLE': 8 | return true; 9 | case 'SET_NOT_DISABLE': 10 | return false; 11 | default: 12 | return state; 13 | } 14 | } 15 | 16 | export default isDisableReducer; -------------------------------------------------------------------------------- /frontend/src/components/Private/FlexContainer/FlexContainer.tsx: -------------------------------------------------------------------------------- 1 | import { ReactNode } from 'react'; 2 | import style from './FlexContainer.module.css'; 3 | 4 | interface FlexContainerProps { 5 | children: ReactNode; 6 | } 7 | 8 | const FlexContainer = ({children}: FlexContainerProps) => { 9 | return ( 10 |
11 | {children} 12 |
13 | ) 14 | } 15 | 16 | export default FlexContainer; -------------------------------------------------------------------------------- /frontend/src/actions/index.ts: -------------------------------------------------------------------------------- 1 | export const setDisable = () => { 2 | return { 3 | type: 'SET_DISABLE' 4 | } 5 | } 6 | 7 | export const setNotDisable = () => { 8 | return { 9 | type: 'SET_NOT_DISABLE' 10 | } 11 | } 12 | 13 | export const hasError = (errorMessage: string) => { 14 | return { 15 | type: 'HAS_ERROR', 16 | errorMessage: errorMessage 17 | } 18 | } 19 | 20 | export const hasNoError = () => { 21 | return { 22 | type: 'HAS_NO_ERROR' 23 | } 24 | } -------------------------------------------------------------------------------- /backend/src/models/csrfTokenSecretModel.ts: -------------------------------------------------------------------------------- 1 | import mongoose, { Schema } from 'mongoose'; 2 | import { CsrfTokenSecret } from '../interfaces/index.js'; 3 | 4 | const csrfTokenSecretSchema: Schema = new Schema({ 5 | secret: { 6 | type: String, 7 | required: true 8 | }, 9 | user_id: { 10 | type: Schema.Types.ObjectId, 11 | select: false, 12 | ref: 'User' 13 | } 14 | }, {versionKey: false}); 15 | 16 | export default mongoose.model('CSRFTokenSecret', csrfTokenSecretSchema); -------------------------------------------------------------------------------- /frontend/src/components/AllRoutes/CustomInput/CustomInput.tsx: -------------------------------------------------------------------------------- 1 | import { InputHTMLAttributes } from 'react'; 2 | import { useSelector } from 'react-redux'; 3 | import { AllReducers } from '../../../interfaces'; 4 | import { Field } from 'formik'; 5 | 6 | const CustomInput = ({ ...props }: InputHTMLAttributes) => { 7 | const isDisabled = useSelector((state: AllReducers) => state.isDisabled); 8 | 9 | return ( 10 | <> 11 | 12 | 13 | ); 14 | } 15 | 16 | export default CustomInput; 17 | -------------------------------------------------------------------------------- /frontend/src/components/Private/Header/Header.module.css: -------------------------------------------------------------------------------- 1 | .header { 2 | width: 100%; 3 | height: 4rem; 4 | background-color: #1f2937; 5 | border-bottom: 1px solid #323c4b; 6 | } 7 | 8 | .flex_header_container { 9 | display: flex; 10 | justify-content: space-between; 11 | align-items: center; 12 | height: 100%; 13 | } 14 | 15 | .logo_container { 16 | width: 50px; 17 | display: flex; 18 | justify-content: center; 19 | align-items: center; 20 | } 21 | 22 | .logo { 23 | width: 32px; 24 | object-fit: cover; 25 | max-width: 32px; 26 | } 27 | -------------------------------------------------------------------------------- /frontend/public/manifest.json: -------------------------------------------------------------------------------- 1 | { 2 | "short_name": "React App", 3 | "name": "Create React App Sample", 4 | "icons": [ 5 | { 6 | "src": "favicon.ico", 7 | "sizes": "64x64 32x32 24x24 16x16", 8 | "type": "image/x-icon" 9 | }, 10 | { 11 | "src": "logo192.png", 12 | "type": "image/png", 13 | "sizes": "192x192" 14 | }, 15 | { 16 | "src": "logo512.png", 17 | "type": "image/png", 18 | "sizes": "512x512" 19 | } 20 | ], 21 | "start_url": ".", 22 | "display": "standalone", 23 | "theme_color": "#000000", 24 | "background_color": "#ffffff" 25 | } 26 | -------------------------------------------------------------------------------- /backend/src/utils/generateRandomUsernameSSO.ts: -------------------------------------------------------------------------------- 1 | function generateRandomUsernameSSO() { 2 | const validChars = 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789_'; 3 | const minLength = 8; 4 | const maxLength = 8; 5 | 6 | let username = ''; 7 | const usernameLength = Math.floor(Math.random() * (maxLength - minLength + 1)) + minLength; 8 | 9 | for (let i = 0; i < usernameLength; i++) { 10 | const randomIndex = Math.floor(Math.random() * validChars.length); 11 | username += validChars.charAt(randomIndex); 12 | } 13 | 14 | return username; 15 | } 16 | 17 | export default generateRandomUsernameSSO; -------------------------------------------------------------------------------- /frontend/src/components/AllRoutes/Layout/Layout.tsx: -------------------------------------------------------------------------------- 1 | import { ReactNode } from 'react'; 2 | import style from './Layout.module.css'; 3 | import { useSelector } from 'react-redux'; 4 | import { AllReducers } from '../../../interfaces'; 5 | 6 | interface LayoutProps { 7 | children: ReactNode; 8 | } 9 | 10 | const Layout = ({children}: LayoutProps) => { 11 | const isDisabled = useSelector((state: AllReducers) => state.isDisabled); 12 | 13 | return ( 14 | <> 15 |
16 | {children} 17 |
18 | 19 | ) 20 | } 21 | 22 | export default Layout; -------------------------------------------------------------------------------- /frontend/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "target": "es5", 4 | "lib": [ 5 | "dom", 6 | "dom.iterable", 7 | "esnext" 8 | ], 9 | "allowJs": true, 10 | "skipLibCheck": true, 11 | "esModuleInterop": true, 12 | "allowSyntheticDefaultImports": true, 13 | "strict": true, 14 | "forceConsistentCasingInFileNames": true, 15 | "noFallthroughCasesInSwitch": true, 16 | "module": "esnext", 17 | "moduleResolution": "node", 18 | "resolveJsonModule": true, 19 | "isolatedModules": true, 20 | "noEmit": true, 21 | "jsx": "react-jsx" 22 | }, 23 | "include": [ 24 | "src", 25 | "tailwind.config.js" 26 | ] 27 | } -------------------------------------------------------------------------------- /frontend/src/App.css: -------------------------------------------------------------------------------- 1 | @tailwind base; 2 | @tailwind components; 3 | @tailwind utilities; 4 | @import url('https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700&display=swap'); 5 | 6 | 7 | * { 8 | margin: 0px; 9 | padding: 0px; 10 | box-sizing: border-box; 11 | color: white; 12 | text-decoration: none; 13 | -webkit-font-smoothing: antialiased; 14 | -moz-osx-font-smoothing: grayscale; 15 | font-family: 'Inter'; 16 | } 17 | 18 | body { 19 | background-color: #111827; 20 | } 21 | 22 | [disabled], .disabled { 23 | opacity: 0.5; 24 | pointer-events: none; 25 | } 26 | 27 | [disabled]:hover { 28 | cursor: initial !important; /* DEFAULT */ 29 | opacity: 0.5 !important; 30 | } -------------------------------------------------------------------------------- /frontend/src/components/AllRoutes/CustomAlert/CustomAlert.tsx: -------------------------------------------------------------------------------- 1 | import Alert from '@mui/material/Alert'; 2 | import Stack from '@mui/material/Stack'; 3 | import { useSelector } from 'react-redux'; 4 | import { AllReducers } from '../../../interfaces'; 5 | 6 | const CustomAlert = () => { 7 | const error = useSelector((state: AllReducers) => state.error); 8 | 9 | if(!error.hasError) { 10 | return ( 11 | <> 12 | ) 13 | } 14 | 15 | return ( 16 | <> 17 | 18 | 19 | { error.errorMessage } 20 | 21 | 22 | 23 | ); 24 | } 25 | 26 | export default CustomAlert; 27 | -------------------------------------------------------------------------------- /frontend/src/components/AllRoutes/CustomLink/CustomLink.tsx: -------------------------------------------------------------------------------- 1 | import { ReactNode } from 'react'; 2 | import { Link, LinkProps } from 'react-router-dom'; 3 | import { useSelector, useDispatch } from 'react-redux'; 4 | import { AllReducers } from '../../../interfaces'; 5 | import { hasNoError } from '../../../actions'; 6 | 7 | interface CustomLinkProps extends LinkProps { 8 | children: ReactNode; 9 | } 10 | 11 | const CustomLink = ({ children, ...props }: CustomLinkProps) => { 12 | const isDisabled = useSelector((state: AllReducers) => state.isDisabled); 13 | const dispatch = useDispatch(); 14 | 15 | return ( 16 | dispatch(hasNoError())} className={isDisabled ? 'disabled' : ''} {...props}> 17 | {children} 18 | 19 | ); 20 | } 21 | 22 | export default CustomLink; 23 | -------------------------------------------------------------------------------- /backend/src/constants/v1AuthenticationJWTTokensSettings.ts: -------------------------------------------------------------------------------- 1 | export const JWT_AUTHENTICATION_TOKEN_EXPIRATION_STRING: string = "60m"; // * HOW LONG THE USER CAN BE AUTHENTICATED WHEN SUCCESS IN LOGIN? 2 | 3 | // * ---------------- FOR EMAIL ---------------- 4 | export const JWT_REGISTER_ACCOUNT_ACTIVATION_EXPIRES_IN_STRING: string = "5m"; // * HOW LONG THE EMAIL REGISTER ACCOUNT ACTIVATION LINK TO BE EXPIRED 5 | export const JWT_ACCOUNT_RECOVERY_RESET_PASSWORD_EXPIRES_IN_STRING: string = "5m"; // * HOW LONG THE EMAIL ACCOUNT RECOVERY RESET PASSWORD LINK TO BE EXPIRED 6 | 7 | // * ---------------- FOR MULTI FACTOR AUTHENTICATION LOGIN CODE ---------------- 8 | export const JWT_MFA_LOGIN_TOKEN_EXPIRATION_STRING: string = "5m"; // * HOW LONG THE MULTI FACTOR AUTHENTICATION TO BE ENDED BEFORE LOGIN AGAIN? -------------------------------------------------------------------------------- /frontend/.env.example: -------------------------------------------------------------------------------- 1 | # REACT API - BACKEND 2 | REACT_APP_API=http://localhost:4000 3 | 4 | # FIREBASE SSO CRENDENTIAL 5 | # (You can get firebase credentials in the project settings also but in "General" tab only. Scroll down and click the npm input radio button and copy the firebaseConfig variable value and put it in this .env file) 6 | REACT_APP_SSO_FIREBASE_API_KEY= 7 | REACT_APP_SSO_FIREBASE_AUTH_DOMAIN= 8 | REACT_APP_SSO_FIREBASE_PROJECT_ID= 9 | REACT_APP_SSO_FIREBASE_STORAGE_BUCKET= 10 | REACT_APP_SSO_FIREBASE_MESSAGING_SENDER_ID= 11 | REACT_APP_SSO_FIREBASE_APP_ID= 12 | REACT_APP_SSO_FIREBASE_MEASUREMENT_ID= 13 | 14 | # GOOGLE IDENTITY SERVICES SSO CRENDENTIAL 15 | # (Follow this tutorial on how to create GIS Client ID - [https://youtu.be/HtJKUQXmtok?si=gwlFBdj-vIt-XoSR]) 16 | REACT_APP_SSO_GOOGLE_IDENITY_SERVICES_CLIENT_ID= -------------------------------------------------------------------------------- /frontend/src/reducers/errorReducer.ts: -------------------------------------------------------------------------------- 1 | type stateType = { 2 | hasError: boolean, 3 | errorMessage: string 4 | } 5 | 6 | type actionType = { 7 | type: string, 8 | errorMessage: string 9 | } 10 | const defaultState = { 11 | hasError: false, 12 | errorMessage: '' 13 | } 14 | 15 | const errorReducer = (state: stateType = defaultState, action: actionType) => { 16 | switch(action.type) { 17 | case 'HAS_ERROR': 18 | return { 19 | hasError: true, 20 | errorMessage: action.errorMessage 21 | }; 22 | case 'HAS_NO_ERROR': 23 | return { 24 | hasError: false, 25 | errorMessage: '' 26 | }; 27 | default: 28 | return state; 29 | } 30 | } 31 | 32 | export default errorReducer; -------------------------------------------------------------------------------- /frontend/src/pages/Private/Home/Home.tsx: -------------------------------------------------------------------------------- 1 | import style from './Home.module.css'; 2 | import Layout from '../../../components/AllRoutes/Layout/Layout'; 3 | import Header from '../../../components/Private/Header/Header'; 4 | import Main from '../../../components/Private/Main/Main'; 5 | import Footer from '../../../components/Private/Footer/Footer'; 6 | import FlexContainer from '../../../components/Private/FlexContainer/FlexContainer'; 7 | 8 | const Home = () => { 9 | return ( 10 | 11 |
12 |
13 | 14 |
You're logged in!
15 |
16 |
17 |