User Authorization with Firebase Walkthrough
Steps Used to add Firebase to Crypto Tracking Application

Useful Links
- Final Github-Repo of Crypto Hunter
- Live-Site
- User Auth and Watchlist Tutorial
- Original Crypto Tracker Tutorial
- Starting Github-Repo

What is Firebase?
Firebase concept is simple. When you build a client-side app with JavaScript or any of its frameworks, for instance, Google Firebase can turn this into a serverless app in no time. It also removes the need to manage databases yourself, as it does that for you.
Firebase is a backend platform for building Web, Android and IOS applications. It offers real time database, different APIs, multiple authentication types and hosting platform.
Adding Firebase
- Go to Firebase
- Login and Create New Project
- Give your project a name
- Accept terms and options SEO
- From within your apps console
- Register your app
- Install firebase via NPM
- npm i firebase
- Copy config file firebase provides
- add firebaseConfig.js inside config folder inside of src and paste our config
- Recommend add .gitignore and configuring .env file for firebaseConfig
- export default firebaseConfig
Choosing our Firebase Build
- In this project we want to choose Authentication
- We want to add Email/Password for Native providers
- Google for Additional Providers
- Confirm Project public-facing-name, typically default is fine.
- Add Project support email and Save
Integrating Firebase to your project
-
Create a new file called firebase.js in your src folder
-
Inside lets create the const firebaseApp and initialize with our firebaseConfig file
- Don't forget to import initialize from firebase/app
-
Create const auth = getAuth(firebaseApp)
-
Import the auth library from 'firebase/auth'
-
Create const db = getFirestore(firebaseApp)
-
Import firestore library from 'firebase/firestore'
-
Export auth and db to be utilized outside of this file.
Completed Example of 'firebase.js'
import firebaseConfig from './config/firebaseConfig';
import { initializeApp } from 'firebase/app';
import { getAuth } from 'firebase/auth';
import { getFirestore } from 'firebase/firestore';
const firebaseApp = initializeApp(firebaseConfig);
const auth = getAuth(firebaseApp);
const db = getFirestore(firebaseApp);
export { auth, db };Adding to our Context State
- In our Crypto Demo application we utilize Context API so we can tap into our state object without prop drilling.
- Since we want to add this to Login and have our user status accessible throughout the application this is an ideal location to configure
- Add user state to our crypto context.
CryptoContext Example
const CryptoContext = ({ children }) => {
const [currency, setCurrency] = useState('INR');
const [symbol, setSymbol] = useState('₹');
const [coins, setCoins] = useState([]);
const [loading, setLoading] = useState(false);
const [user, setUser] = useState(null)-
Add Material UI Modal Component for Login Button
- Inside of Components folder lets make Authentication folder
- Lets add AuthModal.js to the Authentication folder
- Copy Modal of Choice from MUI website and paste into AuthModal.js and rename the modal export to AuthModal
- Inside of Header.js Lets add <AuthModal/> after Select but inside of Toolbar. Remember to Import.
- Lets Remove the default button and add our own next in AuthModal.js
-
Add <Button> from Material-UI
- Login will be the content of the button
- variant will be 'contained'
- style get from example below
- Add onClick={handleOpen}
New MUI Button
<Button
variant="contained"
style={{
width: 85,
height: 40,
marginLeft: 15,
backgroundColor: '#EEBC1D',
}}
>
Login
</Button>-
Update Fade Component inside our AuthModal
- Remove all default tags inside our <div> inside of <Fade>
- Add MUI TABS component to our Auth Modal
- Import Tabs
- Add <AppBar> inside our div
- Notice that tabs needs 'value' and 'handleChange'
- Lets copy that from our TABS code from MUI documentation
-
Add styles to paper for useStyles inside our modal on AuthModal.js
const useStyles = makeStyles((theme) => ({
modal: {
display: 'flex',
alignItems: 'center',
justifyContent: 'center',
},
paper: {
width: 400,
backgroundColor: theme.palette.background.paper,
color: 'white',
borderRadius: 10,
}
}));Updating the TAB logic
- Add {value===0 && <Login/>} inside our AuthMOdal After </AppBar>
- Add {value ===1 && <SignUp/>} inside our AuthMOdal After </AppBar>
- Create Login and SignUp Components Inside our Authentication Folder
Configuring Login & Signup
- Import Login and SignUp to AuthModal.js
- We want to use handleClose method inside our Login and SignUp Componenents
- Lets pass handleClose down as a prop
- Add props inside our Login And SignUp Components so we can use them.
Example of HandleClose
{
value === 0 && <Login handleClose={handleClose} />;
}
{
value === 1 && <SignUp handleClose={handleClose} />;
}-
Inside of our Signup.js
- Configure useState hook for email, password and confirmPassword, set default state to ""
- In our Return we will use the Box Component from MUI core
- Import Box and add the following attributes
- Import TextField and put inside of our Box with the following attributes
- Copy TextField and Paste 2 times and edit for setPassowrd and confirmPassword
- Add a MUI Button next, import and add the Handle Submit onClick
- Check your Modal > SignUP and confirm our UI looks good
MUI MODAL with SIGNUP Code
return (
<Box p={3} style={{ display: 'flex', flexDirection: 'column', gap: '20px' }}>
<TextField
variant="outlined"
type="email"
label="Enter Email"
value={email}
onChange={(e) => setEmail(e.target.value)}
fullWidth
></TextField>
<TextField
variant="outlined"
type="password"
label="Enter Password"
value={password}
onChange={(e) => setPassword(e.target.value)}
fullWidth
></TextField>
<TextField
variant="outlined"
type="password"
label="Confirm Password"
value={confirmPassword}
onChange={(e) => setConfirmPassword(e.target.value)}
fullWidth
></TextField>
<Button
variant="contained"
size="large"
style={{ backgroundColor: '#EEBC1D' }}
onClick={handleSubmit}
>
Sign Up
</Button>
</Box>
);-
Setup Our Login.js
-
Copy our return Code and state from SignUp.js
-
Remove useState and TextField with ConfirmPassword References, we won't need it
-
Create handleSubmit const
-
confirm handleClose is a prop in Login
Login.js
import React, { useState } from 'react';
import { Box, Button, TextField } from '@material-ui/core';
const Login = ({ handleClose }) => {
const [email, setEmail] = useState('');
const [password, setPassword] = useState('');
const handleSubmit = () => {};
return (
<Box
p={3}
style={{ display: 'flex', flexDirection: 'column', gap: '20px' }}
>
<TextField
variant="outlined"
type="email"
label="Enter Email"
value={email}
onChange={(e) => setEmail(e.target.value)}
fullWidth
></TextField>
<TextField
variant="outlined"
type="password"
label="Enter Password"
value={password}
onChange={(e) => setPassword(e.target.value)}
fullWidth
></TextField>
<Button
variant="contained"
size="large"
style={{ backgroundColor: '#EEBC1D' }}
onClick={handleSubmit}
>
Sign Up
</Button>
</Box>
);
};
export default Login;HandleSubmit PW Logic on SignUp
-
Inside of HandleSugbmit lets add pw logic if (password !== confirmPassword)
-
Next we want to add a SnackBar alert from MUI to prompt
- Before we can implement we want to create a Context for our Alerting
- Inside CryptoContext alert/setAlert state with {open: false, message: '', type: 'success'}
- Remember to add the value into our provider to be passed down to our children.
Updated Context with Alerting
const [alert, setAlert] = useState({
open: false,
message: '',
type: 'success',
});
return (
<Crypto.Provider
value={{ currency, setCurrency, symbol, coins, loading, alert, setAlert }}
>
{children}
</Crypto.Provider>
);- New Alert.js Component Setup
- Create the component named Alert.js inside Components folder
- Import alert, setAlert from our CryptoState()
- Inside our return lets add the Snackbar from MUI
- Copy the JS example Snackbar code and paste inside of Alert.js
- Just copy the logic, no styles or return code needed
- Remove open/setOpen state, it already inside our alert state.
- Remove default handleClick also
- Change setOpen(false) to setAlert({open: false}) to match our logic in cryptoState
- copy props from example code too, open, autoHideDuration, onClose
- Import MuiAlert and put inside our snackbar
- Add props (see example below)
- Finally add {alert.message} as the content for our MuiAlert Component
Completed Alert.js Example
import { Snackbar } from '@material-ui/core';
import React from 'react';
import { CryptoState } from '../CryptoContext';
import MuiAlert from '@material-ui/lab/Alert'
const Alert = () => {
const { alert, setAlert } = CryptoState();
const handleClose = (event, reason) => {
if (reason === 'clickaway') {
return;
}
setAlert({open: false});
return (
<Snackbar
open={alert.open}
autoHideDuration={3000}
onClose={handleClose}
>
<MuiAlert
onClose={handleClose}
elevation={10}
variant="filled"
severity={alert.type}
>
{alert.message}
</MuiAlert>
</Snackbar>
)
};
export default Alert;
Adding our Alerts to SignUp.js
- import {CryptoState} from '../../CryptoContext.js'
- Lets destructure setAlert from CryptoState();
- Now we can add setAlert inside our password if block
- we will pass the type of alert we want when our PWs don't match
- Followed by an empty return
- Also we need to import our Alert into our APP.js
- Test your PW logic now on SignUp and see if your Alert pops
SignUp.js HandleSubmit
const handleSubmit = () => {
if (password !== confirmPassword) {
setAlert({
open: true,
message: 'Passwords do not match',
type: 'error',
});
return;
}
};Adding Try Catch Block
-
After our if/password check we want to add a try/catch but still iside our handleSubmit function
-
Insided of our try {} lets add
- const result = await createuserWithEmailAndPassword()
- Add async to our handleSubmit before our argument ()
- Pass auth as the first arg and make sure to import
- Pass email and password as the last two arguments
- setAlert with a confirmation and welcome of the user with their email
- Pass handleClose() so it will timeout our alert last
- add error to your catch block arg
- setAlert inside our catch and pass the error message (see example below)
- Test SignUp and you should get Alerting
- Also lets console.log(result) at the bottom of our try block
- You can now see the information that result has access too, and how we are able to provide that information to the message of our Alert
Try/Catch Error Handling with Firebase
import { auth } from '../../firebase';
try {
const result = await createUserWithEmailAndPassword(auth);
setAlert({
open: true,
message: `Sign Up Successful. Welcome ${result.user.email}`,
type: 'success',
});
handleClose();
} catch (error) {
setAlert({
open: true,
message: error.message,
type: 'error',
});
return;
}Configuring our Login.js HandleSubmit
- Destructure setAlert from our CryptoState()
- add if no email/password logic and set alert with return
- Now we can copy our SignUp catch logic as that will be the same
- we can create const result = await signInWithEmailAndPassword
- make sure to add async to the handleSubmit
- pass auth, email, password remember to import auth from firebase
- add console.log(result)to see our result object data on login
- Test with Login
Is User Logged In?
-
We need to be able to tell if our user is now logged in or not or it will get confusing for your user experience
-
Lets open CryptoContext again as we will need to be able to tap into this data throughout our App
-
First lets import and add another useEffect
-
lets call onAuthStateChanged(auth), this is a firebase method and pass auth and then a callback
-
that callback will be passed user,
-
if user setUser to user else set to null
useEffect
useEffect(() => {
onAuthStateChanged(auth, (user) => {
if (user) setUser(user);
else setUser(null);
});
}, []);Creating our User in Header
- Add user to our CryptoState destructure
- Now we have the user Object lets Wrap our AuthModal in jsx logic
{user ? 'Logout' : <AuthModal />} - Now we can test to confirm Logout display on login
Removing Logout String and Adding UserSideBar
- Replace 'logout' with <UserSideBar>
- Create <UserSideBar> Component inside of our Authentication folder
- We should see the sidebar text inside our our button for now.
- Lets grab the Drawer code from MUIv4
- Copy the Source and add to our UserSideBar
- Now we need to remove the unneeded default code
- Remove all the list references
- Add hello inside of our Drawer for now
- Remove all options that aren't 'right'
- Delete all imports that we no longer need, should be yellow lines underneath your imports in your IDE
- it should show right on your button now and a small side bar
Configuring our SideBar
- Lets first replace the Button with an Avatar in our UserSideBar.js
- We will need the user from the context to get the users avatar photo
- we will import this Component form MUIv4
- Now lets replace <Button> with <Avatar> and pass the onClick with toggleDrawer and the default args, anchor & true
- Add src prop to avatar and pass our user object with the photoURL (See Example Below)
- Add alt prop and pass the userobject with displayName OR email
Example Avatar
<Avatar
onClick={toggleDrawer(anchor, true)}
style={{
height: 38,
width: 38,
marginLeft: 15,
cursor: 'pointer',
backgroundColor: '#EEBC1D',
}}
src={user.photoURL}
alt={user.displayName || user.email}
/>
<Drawer
anchor={anchor}
open={state[anchor]}
onClose={toggleDrawer(anchor, false)}
>
Hello
</Drawer>- Styling the UserSideBar and Avatar
- Remove all the styles we copied inside our UserSideBar.js useStyles object
- Copy Container Snippet (see below for code)
- Lets add the profile style for the profile class (see below for code)
- Lets add the picture style for the picture class (see below for code)
- Lets add the logout style for the logout button class (see below for code)
- Lets add the watchlist style for the watchlist class (see below for code)
- Remove all the styles we copied inside our UserSideBar.js useStyles object
Styling Example Code
const useStyles = makeStyles({
container: {
width: 350,
padding: 25,
height: '100%',
display: 'flex',
flexDirection: 'column',
fontFamily: 'monospace',
},
profile: {
flex: 1,
display: 'flex',
flexDirection: 'column',
alignItems: 'center',
gap: '20px',
height: '92%',
},
picture: {
width: 200,
height: 200,
cursor: 'pointer',
backgroundColor: '#EEBC1D',
objectFit: 'contain',
},
logout: {
height: '8%',
width: '100%',
backgroundColor: '#EEBC1D',
marginTop: 20,
},
watchlist: {
flex: 1,
width: '100%',
backgroundColor: 'grey',
borderRadius: 10,
padding: 15,
paddingTop: 10,
display: 'flex',
flexDirection: 'column',
alignItems: 'center',
gap: 12,
overflowY: 'scroll',
},
});- Lets add classes.container to the first <div>inside of <Drawer>
- Lets add classes.profile to the second <div> inside of <Drawer>
- Copy our Avatar Tag and Paste inside our 2nd <div>.
- Remove the styles object and onClick
- After our 2nd Avatar tag, lets add a span (see example below for styles) that displays user.displayName or user.email
UserSideBar with Span
<React.Fragment key={anchor}>
<Avatar
className={classes.picture}
onClick={toggleDrawer(anchor, true)}
style={{
height: 38,
width: 38,
cursor: 'pointer',
backgroundColor: '#EEBC1D',
}}
src={user.photoURL}
alt={user.displayName || user.email}
/>
<Drawer
anchor={anchor}
open={state[anchor]}
onClose={toggleDrawer(anchor, false)}
>
<div className={classes.container}>
<div className={classes.profile}>
<Avatar
className={classes.picture}
src={user.photoURL}
alt={user.displayName || user.email}
/>
<span
style={{
width: '100%',
fontSize: 25,
textAlign: 'center',
fontWeight: 'bolder',
wordWrap: 'break-word',
}}
>
{user.displayName || user.email}
</span>
</div>
</div>
</Drawer>
</React.Fragment>Adding our Logout Button
- Lets add Button after our 1st <div> after <span> on our UserSideBar.js
- Next we need to create our logOut Function
- inside logout lets add the firebase function signOut()
- this should auto import the signout method from firebase/auth
- Lets import {auth} from '../../firebase' and pass it into the signOut fucntion
- Add setAlert to our destructured objects from CryptoState so we can use it during logout
- Now we can add the setAlert fucntion and pass the example code below.
- Don't forget to toggleDrawer() so our drawer closes upon logout.
LogOut Button and LogOut Function for UserSideBar.js
const logOut = () => {
signOut(auth);
setAlert({
open: true,
type: 'success',
message: 'Logout Successfull!',
});
toggleDrawer();
};
<Button variant="contained" className={classes.logout} onClick={logOut}>
Log Out
</Button>;Adding Google Authentication
- Inside of our AuthModal.js inside authentication folder
- Lets add a Box with the className of classes.google and import from MUI
- Add a span inside that box with OR
- Following our <span> lets add <GoogleButton>
- Import GoogleButton from 'react-google-button'
- lets create an empty function after our handleChange named signInWithGoogle
- Test and going to login prompt, confirm our Sign In with google Button Exists
- Now lets add the google styles to our AuthModal.js
google: {
padding: 24,
paddingTop: 0,
display: 'flex',
flexDirection: 'column',
textAlign: 'center',
gap: 20,
fontSize: 20,
},-
Adding Google Auth Provider
-
const googleProvider = new GoogleAuthProvider()
- import googleAuthProvder from 'firebase/auth'
-
create const signInWithPopup (this is a firebase function)
- import signInWithPopup from 'firebase/auth'
- pass auth as the first arg, don't forget to import
- pass googleProvider as our 2nd arg
-
add .then()
- const setAlert from CryptoState()
- import CrpytoState
- Add handleClose() to shut the login prompt
- const setAlert from CryptoState()
-
add .catch() for our error handling
- pass setAlert(see example for props)
- return
-
Test the Google Auth Login now, it should work!
-
Google Provider Code
const { setAlert } = CryptoState();
const googleProvider = new GoogleAuthProvider();
const signInWithGoogle = () => {
signInWithPopup(auth, googleProvider)
.then((res) => {
setAlert({
open: true,
message: `Sign Up Successful. Welcome ${res.user.email}`,
type: 'success',
});
handleClose();
})
.catch((error) => {
setAlert({
open: true,
message: error.message,
type: 'error',
});
return;
});Adding FIRESTORE for our WATCHLIST storage
- Go to Firebase Dashboard, under BUILD find FIRESTORE DATABASE. Click
- Swith radio start in test
- Test mode allows you more than 30 days of development for free
- Select a Server, default works
- Click Rule tab
- We want only logged in users to be able to add coins to their watchlist
- first remove the line that turns off the DB after 30 days.
- Next lets remove the code inside the first match block
Adding Endpoints to your Firstore DB
- add api endpoints you want to be asssociated with this data
- match /watchlist/{userId}
- We can create a function for our read and pass our userId to create a cleaner code
FireStore Endpoint Logic for Watchlist
rules_version = '2';
service cloud.firestore {
match /databases/{database}/documents {
match /watchlist/{userId}{
allow read: if isLoggedIn(userId)
allow write: if request.auth.uid == userId
}
}
function isLoggedIn(userId){
return request.auth.uid == userId
}
}Add to Watchlist Button and Frontend Logic
- First open CoinPage.js inside Pages
- Lets add user to our CryptoState Destructure
- We only want to display the button IF the user is loggedIn
- Go underneath our <span> tag and add {}
Add to Watchlist Button
{
user && (
<Button
variant="outlined"
style={{ width: '100%', height: 40, backgroundColor: '#EEBC1D' }}
>
Add to Watchlist
</Button>
);
}Adding Add to Watchlist Firebase Logic
- Inside our CoinPage.js lets create a const addToWatchList
- Inside addToWatchList we create another const coinRef
- add const coinRef = doc(db, 'watchlist', user.uid);
- doc is imported form our firestore
- db is imported from firebase
- Add a try catch block next
- add setDoc and import from firestore
- Pass our coinRef as the first arg that has all the doc data
- Pass an object with coins and ternirary logic for if a watchlist currently exists or needs to be created (see example below)
- Add watchlist and setWatchList to our cryptoContext useState
- pass it down via the Provider too
- now we can add watchlist to our destructure on coinpage
- after our setSoc lets add a setAlert to notify our user
- Lets add a variable inWatchList that checks if the coin are viewing is currently on the watchlist.
- Now we can Test and see if our firebase DB has the coin we added!
FireBase Watchlist Endpoint Code
const addToWatchlist = async () => {
const coinRef = doc(db, 'watchlist', user.uid);
try {
await setDoc(coinRef, {
coins: watchlist ? [...watchlist, coin.id] : [coin?.id],
});
setAlert({
open: true,
message: `${coin.name} Added to the Watchlist !`,
type: 'success',
});
} catch (error) {
setAlert({
open: true,
message: error.message,
type: 'error',
});
}
};Watchlist Logic on CoinPage.js
{
user && (
<Button
variant="outlined"
style={{ width: '100%', height: 40, backgroundColor: '#EEBC1D' }}
onClick={addToWatchlist}
>
{inWatchList ? 'Remove from Watchlist' : 'Add to Watchlist'}
</Button>
);
}Adding Watchlist Logic for If User Is Logged In
-
On CryptoContext we want to create a useEffect
-
if (user){} // If user is logged in
-
add user to useEffect depedency array
-
add our const coinRef = doc() //import doc
- pass db //import too
- pass endpoint // watchlist
- pass user id // user.uid
-
Now we can add onSnapshot() and check the db for changes to our reference
- Add coinRef as first arg
- (coin) as our second arg
- add if coin.exists() setWatchlist
- pass coin.data().coins
- else log "no items in Watchlist"
-
lets add var unsubscribe to our onSnapshot
-
Last add a return anonymous function and call unsubscribe()
-
Watchlist Example
useEffect(() => {
if (user) {
const coinRef = doc(db, 'watchlist', user.uid);
var unsubscribe = onSnapshot(coinRef, (coin) => {
if (coin.exists()) {
setWatchlist(coin.data().coins);
} else {
console.Console('No Items in Watchlist');
}
});
return () => {
unsubscribe();
};
}
}, [user]);Remove from Watchlist
-
Now we need to implement removeFromWatchlist
- On our CoinPage lets add const removeFromWatchList function
- copy our logic from addToWatchList as it will be similiar
-
Updating setDoc in our Try Block
- add coins: watchlist.filter((watch) => watch !== coin?.id)
- this takes our watchlist and updates the watchlist to have all coins but the coin we reference
- Add {merge: 'true'} to update hte doc with the new watchlist filtered data
- Update setALert to say "removed from watchlist instead of added"
Updated Remove From Watchlist Code
const removeFromWatchList = async () => {
const coinRef = doc(db, 'watchlist', user.uid);
try {
await setDoc(
coinRef,
{
coins: watchlist.filter((watch) => watch !== coin?.id),
},
{ merge: true }
);
setAlert({
open: true,
message: `${coin.name} Removed the Watchlist !`,
type: 'success',
});
} catch (error) {
setAlert({
open: true,
mesage: error.message,
type: 'error',
});
}
};Displaying our Watchlist items in sidebar
- Go to UserSideBar.js
- Lets add our watchlist from CryptoState so we can tap into our watchlist coins
- under our Watchlist span
- add {coins.map(coin => {})}
- add if watchlist includes (coin.id)
- return a <div> with classes.coin
- inside that div, put a span with {coin.name}
- add another span after with style (see example code)
- inside that span put {symbol} from our Context API and import
- After symbol Paste our numberWithCommas function we cretaed earlier and pass coin.current_price.toFixed(2) and import.
- We need an ICON now to Delete a Coin from the Watchlist
- npm i react-icons --force
- import {AiFillDelete} from 'react-icons/ai'
- Add coin styles (see github) to UserSideBar
Remove Watchlist Coin from UserSideBar Code
<div className={classes.watchlist}>
<span style={{ fontSize: 15, textShadow: '0 0 5px black' }}>Watchlist</span>
{coins.map((coin) => {
if (watchlist.includes(coin.id))
return (
<div className={classes.coin}>
<span>{coin.name}</span>
<span style={{ display: 'flex', gap: 8 }}>
{symbol} {numberWithCommas(coin.current_price.toFixed(2))}
<AiFillDelete
style={{ cursor: 'pointer' }}
fontSize="16"
onClick={() => removeFromWatchlist(coin)}
/>
</span>
</div>
);
else return <></>;
})}
</div>Remove from watchlist
- Lets Copy our RemoveFromWatchlist code from CoinPage.js
- We want to add coin as the arg1 for our pasted function
- import doc, db, and setdoc
- add removeFromWatchlist function to onClick for our AiFillDelete and pass coin as the arg
Cons of Google Firebase
- If not properly managed, the cost of maintaining Firebase on a pay-as-you-go service accumulates as reads and writes increase. So maintenance costs can spike at some points.
- It's hard to export data stored in Firestore into another database. Even if you eventually find a way, it often requires a high level of technicality. Plus, it can be quite costly, too.
- It can be less platform-agnostic, as it delves more towards Android than iOS.
- The larger the query result gets, the messier and slower things become.
Noteables
- With Firebase, it's pretty simple to connect and use built-in third-party authentication providers, including Google, Facebook, Twitter, among others.
Learn more about it here