1. Set up
Get the source code
In this codelab, you start with a version of the Friendly Chat sample app that is nearly complete, so the first thing you need to do is clone the source code:
$ git clone https://github.com/firebase/codelab-friendlychat-web --branch security
Then, move into the security-start
directory, where you will work for the remainder of this codelab:
$ cd codelab-friendlychat-web/security-start
Now, install the dependencies so you can run the code. If you're on a slower internet connection this may take a minute or two:
$ npm install && (cd functions && npm install)
Get to know this repo
The security-solution/
directory contains the complete code for the sample app. The security-start
directory is where you'll work through the codelab, and is missing a few important parts of the authentication implementation. The key files and features in security-start/
and security-solution/
are:
-
functions/index.jscontains Cloud Functions code, and it's where you will write auth blocking functions. -
public/- contains the static files for your chat app -
public/scripts/main.js- where your chat app JS code (src/index.js) is compiled to -
src/firebase-config.js- contains the Firebase configuration object that is used to initialize your chat app -
src/index.js- your chat app JS code
Get the Firebase CLI
The Emulator Suite is part of the Firebase CLI (command-line interface), which can be installed on your machine with the following command:
$ npm install -g firebase-tools@latest
Build the javascript with webpack, which will create main.js inside the public/scripts/ directory.
webpack build
Next, confirm that you have the latest version of the CLI. This codelab works with version 11.14 or higher.
$ firebase --version 11.14.2
Connect to your Firebase project
Create a new Firebase project
- Sign into the Firebase console using your Google Account.
- Click the button to create a new project, and then enter a project name (for example,
Authentication MFA Codelab).
- Click Continue.
- If prompted, review and accept the Firebase terms , and then click Continue.
- (Optional) Enable AI assistance in the Firebase console (called "Gemini in Firebase").
- For this codelab, you do not need Google Analytics, so toggle off the Google Analytics option.
- Click Create project, wait for your project to provision, and then click Continue.
Connect your code to your Firebase project
Now you need to connect this code to your Firebase project. First run the following command to log in to the Firebase CLI:
$ firebase login
Next run the following command to create a project alias. Replace $YOUR_PROJECT_ID
with the ID of your Firebase project.
$ firebase use $YOUR_PROJECT_ID
Now you're ready to run the app!
2. Run the emulators
In this section, you'll run the app locally. This means it is time to boot up the Emulator Suite.
Start the Emulators
From inside the codelab source directory, run the following command to start the emulators:
$ firebase emulators:start
This will serve your app at http://127.0.0.1:5170 and continually rebuild your source code as you make changes. You'll only need to hard refresh (ctrl-shift-r) locally in your browser to see your changes.
You should see some output like this:
i emulators: Starting emulators: auth, functions, firestore, hosting, storage ✔ functions: Using node@16 from host. i firestore: Firestore Emulator logging to firestore-debug.log ✔ firestore: Firestore Emulator UI websocket is running on 9150. i hosting[demo-example]: Serving hosting files from: ./public ✔ hosting[demo-example]: Local server: http://127.0.0.1:5170 i ui: Emulator UI logging to ui-debug.log i functions: Watching "[...]" for Cloud Functions... ✔ functions: Loaded functions definitions from source: beforecreated. ✔ functions[us-central1-beforecreated]: providers/cloud.auth/eventTypes/user.beforeCreate function initialized (http://127.0.0.1:5011/[...]/us-central1/beforecreated). i Running script: npm start > security@1.0.0 start > webpack --watch --progress [...] webpack 5.50.0 compiled with 1 warning in 990 ms
Once you see the All emulators readymessage, the app is ready to use.
3. Implementing MFA
MFA has been partially implemented in this repo. You'll add the code to first enroll a user in MFA and then to prompt users enrolled in MFA for a second factor.
In your editor, open the src/index.js
file and find the startEnrollMultiFactor()
method. Add the following code to set up the reCAPTCHA verifier that will prevent phone abuse (the reCAPTCHA verifier is set to invisible and won't be visible to users):
async
function
startEnrollMultiFactor
(
phoneNumber
)
{
const
recaptchaVerifier
=
new
RecaptchaVerifier
(
"recaptcha"
,
{
size
:
"invisible"
},
getAuth
()
);
Then, find the finishEnrollMultiFactor()
method and add the following to enroll the second factor:
//
Completes
MFA
enrollment
once
a
verification
code
is
obtained
.
async
function
finishEnrollMultiFactor
(
verificationCode
)
{
//
Ask
user
for
the
verification
code
.
Then
:
const
cred
=
PhoneAuthProvider
.
credential
(
verificationId
,
verificationCode
);
const
multiFactorAssertion
=
PhoneMultiFactorGenerator
.
assertion
(
cred
);
//
Complete
enrollment
.
await
multiFactor
(
getAuth
()
.
currentUser
)
.
enroll
(
multiFactorAssertion
)
.
catch
(
function
(
error
)
{
alert
(
`
Error
finishing
second
factor
enrollment
.
$
{
error
}
`
);
throw
error
;
});
verificationId
=
null
;
}
Next, find the signIn
function and add the following control flow that prompts users enrolled in MFA to enter their second factor:
async
function
signIn
()
{
//
Sign
in
Firebase
using
popup
auth
and
Google
as
the
identity
provider
.
var
provider
=
new
GoogleAuthProvider
();
await
signInWithPopup
(
getAuth
(),
provider
)
.
then
(
function
(
userCredential
)
{
//
User
successfully
signed
in
and
is
not
enrolled
with
a
second
factor
.
})
.
catch
(
function
(
error
)
{
if
(
error
.
code
==
"auth/multi-factor-auth-required"
)
{
multiFactorResolver
=
getMultiFactorResolver
(
getAuth
(),
error
);
displaySecondFactor
(
multiFactorResolver
.
hints
);
}
else
{
alert
(
`
Error
signing
in
user
.
$
{
error
}
`
);
}
});
}
The rest of the implementation, including the functions invoked here, is already complete. To see how they work, browse through the rest of the file.
4. Try out signing in with MFA in the emulators
Now try out the MFA implementation! Make sure your emulators are still running and visit the locally-hosted app at localhost:5170
. Try signing in, and when you're prompted to provide the MFA code, you'll see the MFA code in your terminal window.
Since the emulators fully support Multi-Factor Auth your development environment can be entirely self-contained.
To learn more about implementing MFA, see our reference docs .
5. Create a blocking function
Some applications are meant to be used only by a specific group of users. For those cases, you want to be able to create custom requirements for a user to sign up or sign in to your app.
That's what blocking functions provide: a way to create custom authentication requirements. They're Cloud Functions, but unlike most functions, they run synchronously when a user attempts to sign up or sign in.
To create a blocking function, open functions/index.js
in your editor and find the commented out beforecreated
function.
Replace it with this code that allows only users with a domain of example.com to create an account:
exports
.
beforecreated
=
beforeUserCreated
((
event
)
=
>
{
const
user
=
event
.
data
;
//
Only
users
of
a
specific
domain
can
sign
up
.
if
(
!
user
.
email
||
!
user
.
email
.
endsWith
(
"@example.com"
))
{
throw
new
HttpsError
(
"invalid-argument"
,
"Unauthorized email"
);
}
});
6. Try out blocking function in the emulators
To try out the blocking function, make sure your emulators are running, and in the web app at localhost:5170
, sign out.
Then, try to create an account with an email address that doesn't end in example.com
. The blocking function will prevent the operation from succeeding.
Now, try again with an email address that does end in example.com
. The account will be created successfully.
With blocking functions, you can create any restrictions you need around authentication. To learn more, see the reference docs .
Recap
Great job! You added Multi-Factor Authentication to a web app to help users keep their account secure, and then you created custom requirements for users to sign up using blocking functions. You've definitely earned a gif!


