├── .env.local.sample ├── .gitignore ├── .vscode └── settings.json ├── README.md ├── package-lock.json ├── package.json ├── public ├── favicon.ico ├── index.html ├── logo192.png ├── logo512.png ├── manifest.json └── robots.txt └── src ├── App.jsx ├── components ├── AppRouter.jsx ├── Card.jsx ├── DividerWithText.jsx ├── Layout.jsx ├── Navbar.jsx └── Navlink.jsx ├── contexts └── AuthContext.js ├── hooks └── useMounted.js ├── index.jsx ├── pages ├── ForgotPasswordPage.jsx ├── Homepage.jsx ├── Loginpage.jsx ├── NotfoundPage.jsx ├── Profilepage.jsx ├── Registerpage.jsx ├── ResetPasswordPage.jsx └── TestPage.jsx └── utils └── init-firebase.js /.env.local.sample: -------------------------------------------------------------------------------- 1 | REACT_APP_API_KEY=Aaskjasbdvkjbsdvasdkjvbaskdjbv 2 | REACT_APP_AUTH_DOMAIN=fir-functions.firebaseapp.com 3 | REACT_APP_PROJECT_ID=fir-functions 4 | REACT_APP_STORAGE_BUCKET=fir-functions.appspot.com 5 | REACT_APP_MESSAGING_SENDER_ID=72364587236485 6 | REACT_APP_APP_ID=4:397842834:web:kabsv9q834bgv3bkjbvkjb -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # See https://help.github.com/articles/ignoring-files/ for more about ignoring files. 2 | 3 | # dependencies 4 | /node_modules 5 | /.pnp 6 | .pnp.js 7 | 8 | # testing 9 | /coverage 10 | 11 | # production 12 | /build 13 | 14 | # misc 15 | .DS_Store 16 | .env.local 17 | .env.development.local 18 | .env.test.local 19 | .env.production.local 20 | 21 | npm-debug.log* 22 | yarn-debug.log* 23 | yarn-error.log* 24 | -------------------------------------------------------------------------------- /.vscode/settings.json: -------------------------------------------------------------------------------- 1 | { 2 | "files.exclude": { 3 | "**/.git": false, 4 | "**/.svn": true, 5 | "**/.hg": true, 6 | "**/CVS": true, 7 | "**/.DS_Store": true, 8 | "**/Thumbs.db": true, 9 | "**/.classpath": true, 10 | "**/.project": true, 11 | "**/.settings": true, 12 | "**/.factorypath": true, 13 | "**/node_modules": true 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # React Authentication using Firebase (new API v9) 2 | 3 | Reactjs authentication using firebase auth (using the new v9 API). The benefit of using this new API is that the final bundle size is much smaller compared to the previous - which in turn makes your React site efficient and load much faster. 4 | 5 | ## Why this? 6 | 7 | You can use this template as a starting point for any application that needs authentication. 8 | 9 | The template is based on [ChakraUI](https://chakra-ui.com/) (incl. **Dark Mode**), though you can use any UI library you like but you need to do the relevant changes and keep the code related to firebase authentication - **the logic remains the same in any UI**. 10 | 11 | ## Available Scripts 12 | 13 | In the project directory, you can run: 14 | 15 | ### `npm start` 16 | 17 | Runs the app in the development mode.\ 18 | Open [http://localhost:3000](http://localhost:3000) to view it in the browser. 19 | 20 | _**Note**: Before running `npm start` do place the relevant environment variables in the `.env.local` file for firebase configuration._ 21 | 22 | ## Learn More 23 | 24 | You can learn more about this project on our [Youtube channel yoursTruly](https://youtube.com/c/yourstruly267). 25 | 26 | ## Author 27 | 28 | [Truly Mittal](https://trulymittal.com) 29 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "react-firebase-auth", 3 | "version": "0.1.0", 4 | "private": true, 5 | "dependencies": { 6 | "@chakra-ui/react": "^1.6.8", 7 | "@emotion/react": "^11.4.1", 8 | "@emotion/styled": "^11.3.0", 9 | "@testing-library/jest-dom": "^5.14.1", 10 | "@testing-library/react": "^11.2.7", 11 | "@testing-library/user-event": "^12.8.3", 12 | "firebase": "^9.0.2", 13 | "framer-motion": "^4.1.17", 14 | "react": "^17.0.2", 15 | "react-dom": "^17.0.2", 16 | "react-icons": "^4.2.0", 17 | "react-router-dom": "^5.3.0", 18 | "react-scripts": "4.0.3", 19 | "web-vitals": "^1.1.2" 20 | }, 21 | "scripts": { 22 | "start": "react-scripts start", 23 | "build": "react-scripts build", 24 | "test": "react-scripts test", 25 | "eject": "react-scripts eject" 26 | }, 27 | "eslintConfig": { 28 | "extends": [ 29 | "react-app", 30 | "react-app/jest" 31 | ] 32 | }, 33 | "browserslist": { 34 | "production": [ 35 | ">0.2%", 36 | "not dead", 37 | "not op_mini all" 38 | ], 39 | "development": [ 40 | "last 1 chrome version", 41 | "last 1 firefox version", 42 | "last 1 safari version" 43 | ] 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /public/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/trulymittal/react-firebase-auth/435bb32cea8ed92c95a56b170d34cacf2f52f1bf/public/favicon.ico -------------------------------------------------------------------------------- /public/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 12 | 13 | 17 | 18 | 27 | React App 28 | 29 | 30 | 31 |
32 | 42 | 43 | 44 | -------------------------------------------------------------------------------- /public/logo192.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/trulymittal/react-firebase-auth/435bb32cea8ed92c95a56b170d34cacf2f52f1bf/public/logo192.png -------------------------------------------------------------------------------- /public/logo512.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/trulymittal/react-firebase-auth/435bb32cea8ed92c95a56b170d34cacf2f52f1bf/public/logo512.png -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /public/robots.txt: -------------------------------------------------------------------------------- 1 | # https://www.robotstxt.org/robotstxt.html 2 | User-agent: * 3 | Disallow: 4 | -------------------------------------------------------------------------------- /src/App.jsx: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import AppRouter from './components/AppRouter' 3 | import AuthContextProvider from './contexts/AuthContext' 4 | 5 | function App(props) { 6 | return ( 7 | 8 | 9 | 10 | ) 11 | } 12 | 13 | export default App 14 | -------------------------------------------------------------------------------- /src/components/AppRouter.jsx: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import { 3 | BrowserRouter as Router, 4 | Redirect, 5 | Route, 6 | Switch, 7 | useLocation, 8 | } from 'react-router-dom' 9 | import { useAuth } from '../contexts/AuthContext' 10 | import ForgotPasswordPage from '../pages/ForgotPasswordPage' 11 | import Homepage from '../pages/Homepage' 12 | import Loginpage from '../pages/Loginpage' 13 | import NotfoundPage from '../pages/NotfoundPage' 14 | import Profilepage from '../pages/Profilepage' 15 | import Registerpage from '../pages/Registerpage' 16 | import ResetPasswordPage from '../pages/ResetPasswordPage' 17 | import TestPage from '../pages/TestPage' 18 | 19 | export default function AppRouter(props) { 20 | return ( 21 | <> 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 34 | 39 | 40 | 41 | 42 | 43 | ) 44 | } 45 | 46 | function ProtectedRoute(props) { 47 | const { currentUser } = useAuth() 48 | const { path } = props 49 | console.log('path', path) 50 | const location = useLocation() 51 | console.log('location state', location.state) 52 | 53 | if ( 54 | path === '/login' || 55 | path === '/register' || 56 | path === '/forgot-password' || 57 | path === '/reset-password' 58 | ) { 59 | return currentUser ? ( 60 | 61 | ) : ( 62 | 63 | ) 64 | } 65 | return currentUser ? ( 66 | 67 | ) : ( 68 | 74 | ) 75 | } 76 | -------------------------------------------------------------------------------- /src/components/Card.jsx: -------------------------------------------------------------------------------- 1 | import { Box, useColorModeValue } from '@chakra-ui/react' 2 | import * as React from 'react' 3 | 4 | export const Card = props => ( 5 | 13 | ) 14 | -------------------------------------------------------------------------------- /src/components/DividerWithText.jsx: -------------------------------------------------------------------------------- 1 | import { Flex, Box, Divider, useColorModeValue, Text } from '@chakra-ui/react' 2 | import React from 'react' 3 | 4 | export default function DividerWithText(props) { 5 | const { children, ...flexProps } = props 6 | return ( 7 | 8 | 9 | 10 | 11 | 17 | {children} 18 | 19 | 20 | 21 | 22 | 23 | ) 24 | } 25 | -------------------------------------------------------------------------------- /src/components/Layout.jsx: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import { Box, Container } from '@chakra-ui/react' 3 | import { Navbar } from './Navbar' 4 | 5 | export function Layout({ children }) { 6 | return ( 7 | 8 | 9 | {children} 10 | 11 | ) 12 | } 13 | -------------------------------------------------------------------------------- /src/components/Navbar.jsx: -------------------------------------------------------------------------------- 1 | import { 2 | Box, 3 | HStack, 4 | IconButton, 5 | Spacer, 6 | useColorMode, 7 | useColorModeValue, 8 | } from '@chakra-ui/react' 9 | import React from 'react' 10 | import { FaMoon, FaSun } from 'react-icons/fa' 11 | import { useAuth } from '../contexts/AuthContext' 12 | import Navlink from './Navlink' 13 | 14 | export function Navbar() { 15 | const { toggleColorMode } = useColorMode() 16 | // const { logout, currentUser } = useAuth() 17 | const { logout, currentUser } = useAuth() 18 | 19 | return ( 20 | 26 | 32 | 33 | 34 | {!currentUser && } 35 | {!currentUser && } 36 | {currentUser && } 37 | {currentUser && ( 38 | { 42 | e.preventDefault() 43 | await logout() 44 | }} 45 | /> 46 | )} 47 | , )} 50 | onClick={toggleColorMode} 51 | aria-label='toggle-dark-mode' 52 | /> 53 | 54 | 55 | ) 56 | } 57 | -------------------------------------------------------------------------------- /src/components/Navlink.jsx: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import { NavLink as Link, useLocation } from 'react-router-dom' 3 | import { Button } from '@chakra-ui/react' 4 | 5 | export default function Navlink({ to, name, ...rest }) { 6 | const location = useLocation() 7 | 8 | const isActive = location.pathname === to 9 | 10 | return ( 11 | 12 | 19 | 20 | ) 21 | } 22 | -------------------------------------------------------------------------------- /src/contexts/AuthContext.js: -------------------------------------------------------------------------------- 1 | import React, { createContext, useContext, useEffect, useState } from 'react' 2 | import { auth } from '../utils/init-firebase' 3 | import { 4 | createUserWithEmailAndPassword, 5 | signInWithEmailAndPassword, 6 | sendPasswordResetEmail, 7 | onAuthStateChanged, 8 | signInWithPopup, 9 | GoogleAuthProvider, 10 | signOut, 11 | confirmPasswordReset, 12 | } from 'firebase/auth' 13 | 14 | const AuthContext = createContext({ 15 | currentUser: null, 16 | signInWithGoogle: () => Promise, 17 | login: () => Promise, 18 | register: () => Promise, 19 | logout: () => Promise, 20 | forgotPassword: () => Promise, 21 | resetPassword: () => Promise, 22 | }) 23 | 24 | export const useAuth = () => useContext(AuthContext) 25 | 26 | export default function AuthContextProvider({ children }) { 27 | const [currentUser, setCurrentUser] = useState(null) 28 | 29 | useEffect(() => { 30 | const unsubscribe = onAuthStateChanged(auth, user => { 31 | setCurrentUser(user ? user : null) 32 | }) 33 | return () => { 34 | unsubscribe() 35 | } 36 | }, []) 37 | 38 | useEffect(() => { 39 | console.log('The user is', currentUser) 40 | }, [currentUser]) 41 | 42 | function login(email, password) { 43 | return signInWithEmailAndPassword(auth, email, password) 44 | } 45 | 46 | function register(email, password) { 47 | return createUserWithEmailAndPassword(auth, email, password) 48 | } 49 | 50 | function forgotPassword(email) { 51 | return sendPasswordResetEmail(auth, email, { 52 | url: `http://localhost:3000/login`, 53 | }) 54 | } 55 | 56 | function resetPassword(oobCode, newPassword) { 57 | return confirmPasswordReset(auth, oobCode, newPassword) 58 | } 59 | 60 | function logout() { 61 | return signOut(auth) 62 | } 63 | 64 | function signInWithGoogle() { 65 | const provider = new GoogleAuthProvider() 66 | return signInWithPopup(auth, provider) 67 | } 68 | 69 | const value = { 70 | currentUser, 71 | signInWithGoogle, 72 | login, 73 | register, 74 | logout, 75 | forgotPassword, 76 | resetPassword, 77 | } 78 | return {children} 79 | } 80 | -------------------------------------------------------------------------------- /src/hooks/useMounted.js: -------------------------------------------------------------------------------- 1 | import { useEffect, useRef } from 'react' 2 | 3 | export default function useMounted() { 4 | const mounted = useRef(false) 5 | useEffect(() => { 6 | mounted.current = true 7 | return () => { 8 | mounted.current = false 9 | } 10 | }, []) 11 | 12 | return mounted 13 | } 14 | -------------------------------------------------------------------------------- /src/index.jsx: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import ReactDOM from 'react-dom' 3 | import App from './App' 4 | import { ChakraProvider, ColorModeScript, extendTheme } from '@chakra-ui/react' 5 | 6 | const config = { 7 | initialColorMode: 'light', 8 | useSystemColorMode: false, 9 | } 10 | 11 | const theme = extendTheme({ 12 | config, 13 | }) 14 | 15 | ReactDOM.render( 16 | 17 | 18 | 19 | 20 | 21 | , 22 | document.getElementById('root') 23 | ) 24 | -------------------------------------------------------------------------------- /src/pages/ForgotPasswordPage.jsx: -------------------------------------------------------------------------------- 1 | import { 2 | Button, 3 | Center, 4 | chakra, 5 | FormControl, 6 | FormLabel, 7 | Heading, 8 | Input, 9 | Stack, 10 | useToast, 11 | } from '@chakra-ui/react' 12 | import React, { useState } from 'react' 13 | import { useHistory } from 'react-router-dom' 14 | import { Card } from '../components/Card' 15 | import DividerWithText from '../components/DividerWithText' 16 | import { Layout } from '../components/Layout' 17 | import { useAuth } from '../contexts/AuthContext' 18 | 19 | export default function ForgotPasswordPage() { 20 | const history = useHistory() 21 | const { forgotPassword } = useAuth() 22 | const toast = useToast() 23 | 24 | const [email, setEmail] = useState('') 25 | 26 | return ( 27 | 28 | 29 | Forgot password 30 | 31 | 32 | { 34 | e.preventDefault() 35 | // your login logic here 36 | try { 37 | await forgotPassword(email) 38 | toast({ 39 | description: `An email is sent to ${email} for password reset instructions.`, 40 | status: 'success', 41 | duration: 9000, 42 | isClosable: true, 43 | }) 44 | } catch (error) { 45 | console.log(error.message) 46 | toast({ 47 | description: error.message, 48 | status: 'error', 49 | duration: 9000, 50 | isClosable: true, 51 | }) 52 | } 53 | }} 54 | > 55 | 56 | 57 | Email address 58 | setEmail(e.target.value)} 65 | /> 66 | 67 | 70 | 71 | 72 | OR 73 |
74 | 77 |
78 |
79 |
80 | ) 81 | } 82 | -------------------------------------------------------------------------------- /src/pages/Homepage.jsx: -------------------------------------------------------------------------------- 1 | import { 2 | Badge, 3 | chakra, 4 | Code, 5 | Heading, 6 | List, 7 | ListItem, 8 | OrderedList, 9 | } from '@chakra-ui/react' 10 | import React from 'react' 11 | import { Link, useLocation } from 'react-router-dom' 12 | import { Layout } from '../components/Layout' 13 | import { useAuth } from '../contexts/AuthContext' 14 | 15 | export default function Homepage() { 16 | return ( 17 | 18 | Home page 19 | {/* {currentUser?.email} */} 20 | 21 | 22 | Firebase Authentication 23 | 29 | v9 30 | 31 | 38 | NEW API 39 | 40 | 41 | 42 | Email password authentication (Register/Login) 43 | Google Sign in 44 | Forgot Password 45 | Custom Reset password page 46 | Protected routes 47 | 48 | Redirect TO or Back (keeping the 49 | state) 50 | 51 | 52 | custom Auth Hook useAuth() 53 | 54 | Loading indicators while sign-in/up 55 | 56 | Dark Mode enabled template using 57 | 64 | Chakra UI 65 | 66 | 67 | 68 | 69 | Some other links (only for reference): 70 | 71 | 72 | 73 | reset page 74 | 75 | 76 | forgot page 77 | 78 | 79 | test page 80 | 81 | 82 | 83 | ) 84 | } 85 | -------------------------------------------------------------------------------- /src/pages/Loginpage.jsx: -------------------------------------------------------------------------------- 1 | import { 2 | Button, 3 | chakra, 4 | FormControl, 5 | FormLabel, 6 | Heading, 7 | HStack, 8 | Input, 9 | Stack, 10 | useToast, 11 | } from '@chakra-ui/react' 12 | import React, { useState } from 'react' 13 | import { FaGoogle } from 'react-icons/fa' 14 | import { Link, useHistory, useLocation } from 'react-router-dom' 15 | import { Card } from '../components/Card' 16 | import DividerWithText from '../components/DividerWithText' 17 | import { Layout } from '../components/Layout' 18 | import { useAuth } from '../contexts/AuthContext' 19 | import useMounted from '../hooks/useMounted' 20 | 21 | export default function Loginpage() { 22 | const history = useHistory() 23 | const { signInWithGoogle, login } = useAuth() 24 | const [email, setEmail] = useState('') 25 | const [password, setPassword] = useState('') 26 | const [isSubmitting, setIsSubmitting] = useState(false) 27 | const toast = useToast() 28 | // const mounted = useRef(false) 29 | const location = useLocation() 30 | 31 | // useEffect(() => { 32 | // mounted.current = true 33 | // return () => { 34 | // mounted.current = false 35 | // } 36 | // }, []) 37 | 38 | const mounted = useMounted() 39 | 40 | function handleRedirectToOrBack() { 41 | // console.log(location?.state) 42 | history.replace(location.state?.from ?? '/profile') 43 | // if (location.state) { 44 | // history.replace(location.state?.from) 45 | // } else { 46 | // history.replace('/profile') 47 | // } 48 | } 49 | 50 | return ( 51 | 52 | 53 | Login 54 | 55 | 56 | { 58 | e.preventDefault() 59 | if (!email || !password) { 60 | toast({ 61 | description: 'Credentials not valid.', 62 | status: 'error', 63 | duration: 9000, 64 | isClosable: true, 65 | }) 66 | return 67 | } 68 | // your login logic here 69 | setIsSubmitting(true) 70 | login(email, password) 71 | .then(res => { 72 | handleRedirectToOrBack() 73 | }) 74 | .catch(error => { 75 | console.log(error.message) 76 | toast({ 77 | description: error.message, 78 | status: 'error', 79 | duration: 9000, 80 | isClosable: true, 81 | }) 82 | }) 83 | .finally(() => { 84 | // setTimeout(() => { 85 | // mounted.current && setIsSubmitting(false) 86 | // console.log(mounted.current) 87 | // }, 1000) 88 | mounted.current && setIsSubmitting(false) 89 | }) 90 | }} 91 | > 92 | 93 | 94 | Email address 95 | setEmail(e.target.value)} 102 | /> 103 | 104 | 105 | Password 106 | setPassword(e.target.value)} 113 | /> 114 | 115 | {/* */} 116 | 125 | 126 | 127 | 128 | 131 | 134 | 135 | OR 136 | 152 | 153 | 154 | ) 155 | } 156 | -------------------------------------------------------------------------------- /src/pages/NotfoundPage.jsx: -------------------------------------------------------------------------------- 1 | import { Heading } from '@chakra-ui/react' 2 | import React from 'react' 3 | import { Layout } from '../components/Layout' 4 | 5 | export default function NotfoundPage() { 6 | return ( 7 | 8 | Not found page 9 | 10 | ) 11 | } 12 | -------------------------------------------------------------------------------- /src/pages/Profilepage.jsx: -------------------------------------------------------------------------------- 1 | import { chakra, Container, Heading } from '@chakra-ui/react' 2 | import React from 'react' 3 | import { Layout } from '../components/Layout' 4 | import { useAuth } from '../contexts/AuthContext' 5 | 6 | export default function Profilepage() { 7 | const { currentUser } = useAuth() 8 | return ( 9 | 10 | Profile page 11 | 12 | 13 | {currentUser &&
 {JSON.stringify(currentUser, null, 2)}
} 14 |
15 |
16 |
17 | ) 18 | } 19 | -------------------------------------------------------------------------------- /src/pages/Registerpage.jsx: -------------------------------------------------------------------------------- 1 | import { 2 | Button, 3 | Center, 4 | chakra, 5 | FormControl, 6 | FormLabel, 7 | Heading, 8 | Input, 9 | Stack, 10 | useToast, 11 | } from '@chakra-ui/react' 12 | import React, { useEffect, useRef, useState } from 'react' 13 | import { FaGoogle } from 'react-icons/fa' 14 | import { useHistory } from 'react-router-dom' 15 | import { Card } from '../components/Card' 16 | import DividerWithText from '../components/DividerWithText' 17 | import { Layout } from '../components/Layout' 18 | import { useAuth } from '../contexts/AuthContext' 19 | 20 | export default function Registerpage() { 21 | const history = useHistory() 22 | const { signInWithGoogle, register } = useAuth() 23 | const [email, setEmail] = useState('') 24 | const [password, setPassword] = useState('') 25 | const [isSubmitting, setIsSubmitting] = useState(false) 26 | const toast = useToast() 27 | const mounted = useRef(false) 28 | 29 | useEffect(() => { 30 | mounted.current = true 31 | return () => { 32 | mounted.current = false 33 | } 34 | }, []) 35 | 36 | return ( 37 | 38 | 39 | Register 40 | 41 | 42 | { 44 | e.preventDefault() 45 | if (!email || !password) { 46 | toast({ 47 | description: 'Credentials not valid.', 48 | status: 'error', 49 | duration: 9000, 50 | isClosable: true, 51 | }) 52 | return 53 | } 54 | // your register logic here 55 | setIsSubmitting(true) 56 | register(email, password) 57 | .then(res => {}) 58 | .catch(error => { 59 | console.log(error.message) 60 | toast({ 61 | description: error.message, 62 | status: 'error', 63 | duration: 9000, 64 | isClosable: true, 65 | }) 66 | }) 67 | .finally(() => { 68 | mounted.current && setIsSubmitting(false) 69 | }) 70 | }} 71 | > 72 | 73 | 74 | Email address 75 | setEmail(e.target.value)} 82 | /> 83 | 84 | 85 | Password 86 | setPassword(e.target.value)} 93 | /> 94 | 95 | 104 | 105 | 106 |
107 | 110 |
111 | OR 112 | 125 |
126 |
127 | ) 128 | } 129 | -------------------------------------------------------------------------------- /src/pages/ResetPasswordPage.jsx: -------------------------------------------------------------------------------- 1 | import { 2 | Button, 3 | chakra, 4 | FormControl, 5 | FormLabel, 6 | Heading, 7 | Input, 8 | Stack, 9 | useToast, 10 | } from '@chakra-ui/react' 11 | import React, { useState } from 'react' 12 | import { Card } from '../components/Card' 13 | import { Layout } from '../components/Layout' 14 | import { useHistory, useLocation } from 'react-router-dom' 15 | import { useAuth } from '../contexts/AuthContext' 16 | 17 | // A custom hook that builds on useLocation to parse 18 | // the query string for you. 19 | function useQuery() { 20 | return new URLSearchParams(useLocation().search) 21 | } 22 | 23 | export default function ResetPasswordPage() { 24 | const { resetPassword } = useAuth() 25 | const query = useQuery() 26 | const history = useHistory() 27 | const [password, setPassword] = useState('') 28 | const toast = useToast() 29 | 30 | console.log(query.get('mode'), query.get('oobCode')) 31 | return ( 32 | 33 | 34 | Reset password 35 | 36 | 37 | { 39 | e.preventDefault() 40 | try { 41 | await resetPassword(query.get('oobCode'), password) 42 | toast({ 43 | description: 'Password has been changed, you can login now.', 44 | status: 'success', 45 | duration: 9000, 46 | isClosable: true, 47 | }) 48 | history.push('/login') 49 | } catch (error) { 50 | toast({ 51 | description: error.message, 52 | status: 'error', 53 | duration: 9000, 54 | isClosable: true, 55 | }) 56 | console.log(error.message) 57 | } 58 | }} 59 | > 60 | 61 | 62 | New password 63 | setPassword(e.target.value)} 69 | /> 70 | 71 | 74 | 75 | 76 | 77 | 78 | ) 79 | } 80 | -------------------------------------------------------------------------------- /src/pages/TestPage.jsx: -------------------------------------------------------------------------------- 1 | import { Container, Heading, Text } from '@chakra-ui/react' 2 | import React from 'react' 3 | import { Layout } from '../components/Layout' 4 | 5 | export default function TestPage() { 6 | return ( 7 | 8 | Test page 9 | 10 | Only for showing how redirects work, i.e. redict to or back 11 | 12 | 13 | ) 14 | } 15 | -------------------------------------------------------------------------------- /src/utils/init-firebase.js: -------------------------------------------------------------------------------- 1 | import { initializeApp } from 'firebase/app' 2 | import { getAuth } from 'firebase/auth' 3 | 4 | const firebaseConfig = { 5 | apiKey: process.env.REACT_APP_API_KEY, 6 | authDomain: process.env.REACT_APP_AUTH_DOMAIN, 7 | projectId: process.env.REACT_APP_PROJECT_ID, 8 | storageBucket: process.env.REACT_APP_STORAGE_BUCKET, 9 | messagingSenderId: process.env.REACT_APP_MESSAGING_SENDER_ID, 10 | appId: process.env.REACT_APP_APP_ID, 11 | } 12 | 13 | const app = initializeApp(firebaseConfig) 14 | 15 | export const auth = getAuth(app) 16 | --------------------------------------------------------------------------------