Oh! You finally made it to here. I know you want to learn full-stack and scale up your career and so do I. But hey, things take time and you should be consistent.
Welcome to the first chapter of my Note-Taking series called "Understanding Firebase Authentication" .
In the fast-paced world of web development, authentication is crucial for creating secure and personalized user experiences. Imagine building a food delivery app, where users need to create accounts, log in, and manage their orders. Firebase Authentication offers a streamlined solution for handling these authentication needs, allowing you to integrate login methods such as email/password and Google sign-in easily.
Let's walk through the steps of authentication.
Step 1: Setting Up Firebase
Before diving into the React app, let’s configure Firebase to handle authentication.
Create a Firebase Account and Project:
- Visit the Firebase Console, create an account (if you don't have one), and start a new project. Let's name it NoteTakingApp.
Enable Authentication:
- Inside your Firebase project dashboard, go to Authentication > Sign-in method, and enable Email/Password and Google Sign-In methods(I have used this two, you can choose whatever you want).
This will allow users to sign in using either email/password or their Google account to track their notes.
Add a Web App:
- In the Project Settings, click Add App and select Web. Firebase will provide you with the configuration needed to integrate it with React.
Step 2: Setting Up Your React App
Create a New React App: Create a basic React app by running:
npx create-react-app food-delivery-auth
Install Firebase and Tailwind CSS: I have always said and used Tailwind in my projects as it offers clean and clear approach to style your elements. Install it and firebase using this command:
npm install firebase npm install -D tailwindcss npx tailwindcss init
Configure Tailwind: After installation, configure Tailwind to be used in your React app by adding the necessary settings in your
tailwind.config.js
file.
Step 3: Firebase Configuration
In your React app, you’ll need to set up Firebase connection.
Create
firebase.js
in thesrc/firebase
folder. You will get this boilerplate from your firebase web app setup as well:import { initializeApp } from "firebase/app"; import { getAuth } from "firebase/auth"; const firebaseConfig = { apiKey: "YOUR_API_KEY", authDomain: "YOUR_AUTH_DOMAIN", projectId: "YOUR_PROJECT_ID", storageBucket: "YOUR_STORAGE_BUCKET", messagingSenderId: "YOUR_MESSAGING_SENDER_ID", appId: "YOUR_APP_ID" }; const app = initializeApp(firebaseConfig); const auth = getAuth(app); export { app, auth };
This configuration connects your React app to Firebase, enabling authentication features.
Step 4: Authentication Context Setup
In our Note-Taking App, users will want to sign in to create, view, and manage their personal notes. For seamless access to user data and notes throughout the app, we’ll use React's Context API.
Create an Authentication Context (
src/contexts/AuthContext.js
):import React, { useContext, useEffect, useState } from "react"; import { auth } from "../firebase"; import { onAuthStateChanged } from "firebase/auth"; const AuthContext = React.createContext(); export function useAuth() { return useContext(AuthContext); } export function AuthProvider({ children }) { const [currentUser, setCurrentUser] = useState(null); const [loading, setLoading] = useState(true); useEffect(() => { const unsubscribe = onAuthStateChanged(auth, (user) => { setCurrentUser(user); setLoading(false); }); return unsubscribe; }, []); const value = { currentUser }; return ( <AuthContext.Provider value={value}> {!loading && children} </AuthContext.Provider> ); }
This context will manage the authentication state across the entire app, ensuring the user’s login session persists when navigating between pages.
Step 5: Firebase Authentication Functions
Now, let’s define our authentication functions for signing in, signing up, and signing out.
Create
auth.js
inside thesrc/firebase
folder:import { createUserWithEmailAndPassword, signInWithEmailAndPassword, signInWithPopup, GoogleAuthProvider } from "firebase/auth"; import { auth } from "./firebase"; // Function for creating a new user with email and password export const registerWithEmail = (email, password) => { return createUserWithEmailAndPassword(auth, email, password); }; // Function for signing in a user with email and password export const loginWithEmail = (email, password) => { return signInWithEmailAndPassword(auth, email, password); }; // Function for signing in with Google export const loginWithGoogle = () => { const provider = new GoogleAuthProvider(); return signInWithPopup(auth, provider); }; // Function to sign out the user export const logout = () => { return auth.signOut(); };
These functions will allow users to sign in using either email/password or their Google account, keeping their food orders tied to their account.
** Want to get honest opinions or like the way I write? Have a gig for me?
Drop me a mail: sneha06.work@gmail.com **
Step 6: Login Component with Real-Life Example
Let’s say a user, Emma, wants to sign in to her Note Taking App account to track her recent notes. We’ll create a Login component that lets her sign in using email/password or her Google account.
Create
index.js
insidesrc/components/auth/login/
:```javascript import React, { useState } from 'react' import { doSignInWithEmailandPassword,doSignInWithGoogle } from '../../../firebase/auth'; import { useAuth } from '../../../contexts/authContexts'; import { Navigate, Link } from 'react-router-dom';
export default function Login() { const{userLoggedIn} = useAuth()
const [email,setEmail] = useState(''); const [password,setPassword] = useState(''); const [isSigningIn,setIsSigningIn] = useState(false); const [errorMessage,setErrorMessage] = useState('');
const onSubmit = async(e)=>{ e.preventDefault() if(!isSigningIn){ setIsSigningIn(true); await doSignInWithEmailandPassword(email,password) } }
const onGoogleSignIn = async(e)=>{ e.preventDefault() if(!isSigningIn){ setIsSigningIn(true); doSignInWithGoogle().catch(err=>{ setIsSigningIn(false) }) } } return (
{userLoggedIn && ()}Welcome Back
Email { setEmail(e.target.value) }} className="w-full mt-2 px-3 py-2 text-gray-500 bg-transparent outline-none border focus:border-indigo-600 shadow-sm rounded-lg transition duration-300" />
{errorMessage && ( {errorMessage} )}
{isSigningIn ? 'Signing In...' : 'Sign In'}
Don't have an account? Sign up
w-full flex items-center justify-center gap-x-3 py-2.5 border rounded-lg text-sm font-medium ${isSigningIn ? 'cursor-not-allowed' : 'hover:bg-gray-100 transition duration-300 active:bg-gray-100'}
}>
{isSigningIn ? 'Signing In...' : 'Continue with Google'}
) }
Emma can now log into her account, whether through her email/password or Google, and view her order status instantly!
---
### Step 7: Register Component
Now, let's add a **Register** component for new user who want to sign up for the app, like Emma’s friend John, who’s trying to write his first note.
1. **Create** `index.js` inside `src/components/auth/register/`:
```javascript
import React,{useState} from 'react'
import { Navigate, Link, useNavigate } from 'react-router-dom'
import { useAuth } from '../../../contexts/authContexts'
import { doCreateUserWithEmailandPassword } from '../../../firebase/auth'
export default function Register() {
const navigate = useNavigate()
const [email, setEmail] = useState('')
const [password, setPassword] = useState('')
const [confirmPassword, setconfirmPassword] = useState('')
const [isRegistering, setIsRegistering] = useState(false)
const [errorMessage, setErrorMessage] = useState('')
const { userLoggedIn } = useAuth()
const onSubmit = async (e) => {
e.preventDefault()
if(!isRegistering) {
setIsRegistering(true)
await doCreateUserWithEmailandPassword(email, password)
}
}
return (
<div>
{userLoggedIn && (<Navigate to={'/home'} replace={true} />)}
<main className="w-full h-screen flex self-center place-content-center place-items-center">
<div className="w-96 bg-white text-gray-600 space-y-5 p-4 shadow-xl border rounded-xl">
<div className="text-center mb-6">
<div className="mt-2">
<h3 className="text-gray-800 text-xl font-semibold sm:text-2xl">Create a New Account</h3>
</div>
</div>
<form onSubmit={onSubmit} className="space-y-4">
<div>
<label className="text-sm text-gray-600 font-bold">
Email
</label>
<input
type="email"
autoComplete='email'
required
value={email} onChange={(e) => { setEmail(e.target.value) }}
className="w-full mt-2 px-3 py-2 text-gray-500 bg-transparent outline-none border focus:indigo-600 shadow-sm rounded-lg transition duration-300"
/>
</div>
<div>
<label className="text-sm text-gray-600 font-bold">
Password
</label>
<input
disabled={isRegistering}
type="password"
autoComplete='new-password'
required
value={password} onChange={(e) => { setPassword(e.target.value) }}
className="w-full mt-2 px-3 py-2 text-gray-500 bg-transparent outline-none border focus:border-indigo-600 shadow-sm rounded-lg transition duration-300"
/>
</div>
<div>
<label className="text-sm text-gray-600 font-bold">
Confirm Password
</label>
<input
disabled={isRegistering}
type="password"
autoComplete='off'
required
value={confirmPassword} onChange={(e) => { setconfirmPassword(e.target.value) }}
className="w-full mt-2 px-3 py-2 text-gray-500 bg-transparent outline-none border focus:border-indigo-600 shadow-sm rounded-lg transition duration-300"
/>
</div>
{errorMessage && (
<span className='text-red-600 font-bold'>{errorMessage}</span>
)}
<button
type="submit"
disabled={isRegistering}
className={`w-full px-4 py-2 text-white font-medium rounded-lg ${isRegistering ? 'bg-gray-300 cursor-not-allowed' : 'bg-indigo-600 hover:bg-indigo-700 hover:shadow-xl transition duration-300'}`}
>
{isRegistering ? 'Signing Up...' : 'Sign Up'}
</button>
<div className="text-sm text-center">
Already have an account? {' '}
<Link to={'/login'} className="text-center text-sm hover:underline font-bold">Continue</Link>
</div>
</form>
</div>
</main>
</div>
)
}
With this component, John can quickly create an account, order his meal, and start tracking it right away.
Step 8: Header Component
You'll need to call this actions in header. Set up an index.js
file inside src/header
folder:
import React from 'react'
import { Link, useNavigate } from 'react-router-dom';
import { useAuth } from '../../contexts/authContexts';
import { doSignOut } from '../../firebase/auth';
const Header = () => {
const navigate = useNavigate()
const { userLoggedIn } = useAuth()
return (
<nav className='flex backdrop-blur-xl flex-row gap-x-2 w-full z-20 fixed top-0 left-0 h-12 border-b place-content-center items-center'>
{
userLoggedIn
?
<>
<button onClick={() => { doSignOut().then(() => { navigate('/login') }) }} className='text-md text-white font-semibold underline'>Logout</button>
</>
:
<>
<Link className='text-md text-white font-semibold underline' to={'/login'}>Login</Link>
<Link className='text-md text-white font-semibold underline' to={'/register'}>Register New Account</Link>
</>
}
</nav>
)
}
export default Header
This way, now you can add this component in your main app file for routing and Voila! Your Authentication is done.
Try out and experiment with your own project and drop me a comment if this sounds helpful and interesting!
Stay tuned for chapter 2 called "Storing the data in MongoDB"!
Find me on this socials. I would love to connect with you:
Have a wonderful day🌻!