Skip to main content

Adding Firebase Auth

Why use Firebase Authentication?

For Milestone 2 - we used cookie-based authentication with our backend server, but we didn't have to manage the user's password.

Rather than implementing a system for storing usernames & passwords, all of the functionality can be delegated to free or other 3rd party services.

Firebase lets you have upto 50k monthly users for FREE. The only thing they charge you for is authentication via Phone Number

Get Started

Follow this guide for adding Firebase Auth with a pre-built UI for your application.

For the purpose of this guide, you can use a new create-next-app to get login with Firebase working.

Create a new NextJS app

Run npx create-next-app nextjs-firebase to create a new NextJS app for this exercise.

There's a few more edits we have to make to get this working correctly with Firebase.

Setup Firebase

1. Set up your Firebase Account on the console

  • Navigate to Firebase Authentication and click Get Started
  • Select Email/Password as a Sign-in Method.
caution

If you have errors during sign in, double check that you have ENABLED Email/Password as a sign in method

2. Create a Firebase Web Client

  • Navigate to the dashboard in your Firebase Console, and select Add App
  • Give your app a name, and then copy the config that it shows you.

3. Configure Firebaseui

Run npm install --save firebaseui

The package we'll be using for this is called firebaseui from https://www.npmjs.com/package/firebaseui. This comes with a fully implemented sign-up/sign-in flow linked to Firebase Authentication that we can use.

Add a StyledFirebaseAuth component

Add a file called src/components/StyledFirebaseAuth.js and copy/paste the contents below. It is ok to copy/paste this component for your assignment.

import { useEffect, useRef, useState } from 'react';
import { onAuthStateChanged } from 'firebase/auth';
import 'firebaseui/dist/firebaseui.css';

const StyledFirebaseAuth = ({uiConfig, firebaseAuth, className, uiCallback}) => {
const [firebaseui, setFirebaseui] = useState(null);
const elementRef = useRef(null);

useEffect(() => {
// Firebase UI only works on the Client. So we're loading the package only after
// the component has mounted, so that this works when doing server-side rendering.
setFirebaseui(require('firebaseui'));
}, []);


useEffect(() => {
if (firebaseui === null )
return;

// Get or Create a firebaseUI instance.
const firebaseUiWidget = firebaseui.auth.AuthUI.getInstance() || new firebaseui.auth.AuthUI(firebaseAuth);
if (uiConfig.signInFlow === 'popup')
firebaseUiWidget.reset();

// We track the auth state to reset firebaseUi if the user signs out.
const unregisterAuthObserver = onAuthStateChanged(firebaseAuth, user => {
if (!user) {
firebaseUiWidget.reset();
}
});

// Trigger the callback if any was set.
if (uiCallback) {
uiCallback(firebaseUiWidget);
}

// Render the firebaseUi Widget.
// @ts-ignore
firebaseUiWidget.start(elementRef.current, uiConfig);

return () => {
unregisterAuthObserver();
firebaseUiWidget.reset();
};
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [firebaseui, uiConfig]);

return <div className={className} ref={elementRef} />;
};

export default StyledFirebaseAuth;

This component will be our Login component, everything we need for sign in is there. Now, hook it into the rest of your app.

Create a firebase.js file to hold your Firebase App

Create src/firebase.js and this will hold your Firebase configuration & a Firebase App instance (so you can use the rest of the Firebase suite).

src/firebase.js
// Import the functions you need from the SDKs you need
import { initializeApp, getApps } from "firebase/app";

// Your web app's Firebase configuration
const firebaseConfig = {
apiKey: "replaceme",
authDomain: "replaceme",
projectId: "replaceme",
storageBucket: "replaceme",
messagingSenderId: "replaceme",
appId: "replaceme"
};

let firebaseApp
if (!getApps().length) {
firebaseApp = initializeApp(firebaseConfig);
} else {
firebaseApp = getApps()[0];
}

export default firebaseApp;
  • firebaseConfig contains a bunch of keys and IDs that we grabbed from Firebase. It is OK for these to be available to your frontend. There are some security risks that come with it but Firebase is designed to be used safely on the frontend.

Create a Login page

Add a page - src/pages/login.js. This page will ONLY include the StyledFirebaseAuth component along with the configuration

src/pages/login.js
import firebaseApp from './firebase';
import StyledFirebaseAuth from './../components/StyledFirebaseAuth'
import { getAuth } from 'firebase/auth'
import firebase from 'firebase/compat/app';
import 'firebase/compat/auth';

// Configure FirebaseUI.
const uiConfig = {
// Popup signin flow rather than redirect flow.
signInFlow: 'popup',
// Redirect to /signedIn after sign in is successful. Alternatively you can provide a callbacks.signInSuccess function.
signInSuccessUrl: '/',
// We will display Google and Facebook as auth providers.
signInOptions: [
firebase.auth.GoogleAuthProvider.PROVIDER_ID,
firebase.auth.FacebookAuthProvider.PROVIDER_ID,
firebase.auth.EmailAuthProvider.PROVIDER_ID
],
};

export default function LoginPage() {
return <div>
<StyledFirebaseAuth uiConfig={uiConfig} firebaseAuth={getAuth(firebaseApp)} uiCallback={() => console.log('Logged in!')} />
</div>
}

At this point, if you navigate to localhost:3000/login, you should be able to see some styled login buttons but signing in will not completely work yet.

Update _app.js to include Firebase Auth

We also need to tell our NextJS application about the currently signed in user. We can manage this at the top-most level of our app - src/_app.js.

Import the following components at the top of your App.js

src/App.js
import { useState, useEffect} from 'react'
import { onAuthStateChanged, getAuth } from 'firebase/auth'
import firebaseApp from '../firebase'
import "@/styles/globals.css";

export default function App({ Component, pageProps }) {
const [user, setUser] = useState(null)

useEffect(() => {
// We track the auth state to reset firebaseUi if the user signs out.
return onAuthStateChanged(getAuth(firebaseApp), user => {
if (!user) {
firebaseUiWidget.reset();
}
setUser(user)
});
}, [])

return <Component user={user} {...pageProps} />;
}
  • We add a useEffect block here, along with onAuthStateChanged so we can listen for sign-in changes at the application level, and once we detect a user, we can pass that along to our app as a prop.
info

Firebase made updates to their v9 SDK which changes the way you call their functions. You may see scattered documentation online that uses one style vs another. Prefer to use the V9 syntax below (although there are some instances where we may want the V8 syntax).

V9 looks like:

import { initalizeApp } from 'firebase'
initializeApp(config)

V8 looks like:

import firebase from 'firebase/compat/app'
firebase.initializeApp(config)

The code we're using for this guide is their v9 SDK.

To see more of the Firebase docs around Auth, look at their Web > Get Started page.

Some things left for you to do:

  • Display the user's name & email address
  • Add a button to sign the user out

Hint: See the Firebase SDK Reference to see everything you can do with the Auth object from getAuth(firebaseApp)

Login

You can now login. This pre-built form lets you register as a new user or sign in as an existing user. And you can see the info of everyone who logs in via your Firebase dashboard