├── .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 | }
141 | onClick={() =>
142 | signInWithGoogle()
143 | .then(user => {
144 | handleRedirectToOrBack()
145 | console.log(user)
146 | })
147 | .catch(e => console.log(e.message))
148 | }
149 | >
150 | Sign in with Google
151 |
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 | }
117 | onClick={() =>
118 | signInWithGoogle()
119 | .then(user => console.log(user))
120 | .catch(e => console.log(e.message))
121 | }
122 | >
123 | Sign in with Google
124 |
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 |
--------------------------------------------------------------------------------