[MUSIC PLAYING]
PETER FRIESE: Hi, everyone.
Welcome to this "Firebase Fundamentals" video
where I'll show you how to get up
and running quickly with Firebase Authentication
in your web application.
We are going to use the new version of the Firebase
JavaScript SDK and as David mentioned in his video,
version 9 of our JavaScript SDK makes
use of tree shaking to remove all the stuff you don't need.
This is particularly useful for Firebase Authentication.
You rarely want to use all of the authentication providers
in your app.
So with v9, tree shaking can remove all providers
you don't need, resulting in a much smaller download
size of your app.
So in this video, I'm assuming that you've already
gotten started with the initial Firebase setup,
using something like webpack or rollup in a web application
as described in David's video.
If you don't know what I'm talking about,
make sure you've watched that one first before continuing.
All right, let's take a look at the code for this project.
So I've gone ahead and created an empty starter project,
pretty much what David had at the end of his video.
I have a very simple HTML file, which
contains the code for a couple of UIs
I'm going to use to log into my app.
Over here in indexJS I have my code,
which gets compiled and bundled up with webpack.
I've configured webpack to watch my source folder.
And I've also installed HTML webpack plug-in.
This will copy my index HTML file and add a script tag
to load my bundled code.
I go ahead and run MPX webpack to start webpack.
Once it's finished, we can see the result in the dist folder.
And then in a new shell, I'll run
Firebase serve --only hosting to serve
the contents of the dist folder on my local machine.
Once the local server is up and running,
it will show a message saying that it runs
on port 5,000 on local host.
I can then Command click on the link
to open it in my browser and voila, there is my page.
To check everything is working as expected,
let me make a small change to index HTML and hit Save.
In the webpack output, we can see
that it's bundling my project, so
let me hit Refresh in the browser to reload the page.
OK, let's get coding.
First, let's go to indexJS and take
a look at what we've got so far.
At the top of the file, I've got the imports for my CSS files
and some UI elements from this other JavaScript file UI.JS.
This mostly contains code to connect to the UI elements
of my little application.
To keep things simple, I'm not using any framework.
This is all just plain JavaScript and straight
up dom coding.
Next, I import initialize app and call this function
to initialize Firebase using this configuration object.
Now, if you remember what David said in his video,
instead of using a namespace-based approach,
in v9 of the Firebase SDK for JavaScript,
we use a functional approach.
So what this means is that you need to hold on to the Firebase
instance and pass it to the initialization
function of the Firebase service you want to use.
The functions of all the Firebase services
follow the same approach.
Their first parameter requires a reference
to the service they operate on.
So you need to hold onto that service reference
and pass it into any Firebase function you call.
You can think of it a bit like a chain.
You pass the result of one call into the next call.
Before you can use Firebase Auth,
you need to import and initialize it.
To use any of Firebase's services,
you call the function that is named get
and then the name of the Firebase service
you want to use.
So getAuth for Firebase Auth, getFirestore for Firestore,
and so on.
And the name of the module to import from
follows the same naming scheme.
So let me add the import for getAuth up here.
And while I'm at it, I'm going to import connectAuthEmulator
and signInWithEmailAndPassword as well because these
will be the functions I'm going to use later.
Next, I can initialize Firebase Auth by calling getAuth
and pass in the Firebase app instance.
I'll keep the auth instance around,
since I'm going to need it a couple of times.
The first thing I want to do after initializing Firebase
Auth is to connect my app to the Firebase Emulator Suite.
You can think of the Emulator Suite
as kind of your own personal test instance of Firebase,
running locally on your machine.
And it's perfect for development and testing
as all calls happen locally.
So it is super fast.
Also, and this is one thing I personally
find really reassuring, you don't
need to worry about messing up your production data.
Right, so to connect Firebase Auth to the emulator,
all I have to do is call connectAuthEmulator
and provide a reference to the auth instance and the URL
I want the emulator to run on.
By default, this will be port 9099 on local host.
And with that, we're now ready to sign the user in.
For this video, I'm going to use email and password
authentication, since it is super easy to set up.
However, please bear in mind that coming up
with a strong password is challenging
and most people are not very good at this.
But not to worry, Firebase Auth supports a bunch
of other authentication providers
that are even more convenient for users,
such as email and link authentication, Google Sign-In,
Sign in with Apple, and more.
And we will be looking at them in later episodes
of the series.
I'm first going to create a function
that I can call when the user taps on the Login button.
I'm going to call it loginEmailPassword.
And now I can hook it up to the click event
listener of the Login button on the login form.
To sign the user in, I need to fetch their credentials
by reading the value of the email and password input fields
of the login form.
And now I've got everything in place
to call the signInWithEmailAndPassword
function.
This function takes three parameters, the Firebase Auth
instance, the email address, and the password.
The return value of the function is a promise,
so I can use await to call it and then assign the return
value to a new local variable.
When calling in a sync function using await,
the promise will be unwrapped.
So this variable is of type user credential.
User credential has three attributes, and one of them
is the user itself, so let's log this out to the console.
Great, let's now run this.
In the background, webpack has bundled up the code.
And I've got Firebase hosting running locally
to serve up my app on port 5000, So I can just
switch over to my browser and hit Refresh to run
the latest version of my app.
So let me try logging in as Me@AwesomeKittens.Test
with a password of Test1234.
But when I tap the Login button, the browser console
shows an error.
Error, connection refused.
Why is that?
So if you remember, right at the beginning,
after I set up Firebase Auth, I called connectAuthEmulator
and instructed it to connect to port 9099 on local host.
But I didn't start the emulator.
And this is why we get this error message.
It's an easy mistake to make, but it's easy to fix.
So let me start the emulator by running Firebase
emulators:start --onlyAuth from the project folder.
This will run just the Firebase authentication emulator.
And now my app should be able to communicate with the Emulator
Suite.
Let me try logging in with the same credentials.
And now when I tap on the Login button, I get another error.
This time it's Auth, user not found.
And that is because my Firebase project doesn't have
a user with that email address.
So one way to fix this is to create a new account using
the Firebase console.
Here in the console for the Firebase Auth emulator,
I can see that there aren't any uses for this project yet.
So I'll check the Add User button
to open a dialogue that lets me enter
the details for a new user account,
such as their display name, a photo URL,
and a bunch of other options.
So let me fill in the email and password fields.
Me@AwesomeKittens.Test and Test1234.
One thing to note is that I don't actually
own this email address.
The test top level domain is just for testing,
so this email is unverified.
If you're considering using email and password sign in,
you should definitely implement an email verification flow
to make sure users actually own the email address they
provided.
You can use the default verification flow
from your client or you can customize
it using the Admin SDK.
Another option is to use email and link authentication or one
of the OAuth2 providers Firebase supports.
OK, once I create the user account,
I can see the user and the console along with their UID.
The UID is a user ID that Firebase assigns to the user.
This ID is guaranteed to be unique,
no matter how they sign in.
So now that I've created a user account,
I can try signing in again.
And since I used console.log to print the user
details onto the console, I can now see the user details.
For example, here is their email address and there is the UID.
If I don't provide the correct password when signing in,
Firebase Auth will return an error of wrong password.
So before I show you how to create a new user account using
code, let me display this error on the UI
to make this a better user experience.
To do this, I'm going to wrap the call
to sign in with email and password in a try catch block.
And then inside the catch block, I'll
print the error to the console.
I'll also send it to the showLoginError function
that I defined in Ui.JS.
This function will check if the error code
equals to AuthErrorCodes.invalidpassword
and will return a human readable error message.
Let me quickly show what this looks like in the browser.
So when I try signing in with the wrong password,
I will now get an error message.
That's so much more user friendly
than displaying the error in the console.
OK, so I created a user account using the Firebase console.
But in a real application, you will
want to allow users to sign up themselves.
So let me show you how to create a new user account using code.
I first create an empty function and name,
create account, and hook it up to the click
handler of the Sign Up button.
To create a new user account, I can
use the createUserWithEmailAndPassword
function.
Let me first impart this function up here.
Now, the createUserWithEmailAndPassword
function has the same parameters as signInWithEmailAndPassword.
So I can go ahead and copy the code
I wrote for the Login button and then
paste it into the create account function.
Now, back down in create account,
I can replace this call with createUserWithEmailAndPassword.
Note how the code completion, however,
shows me the documentation for this function.
And sure enough, I can see that it does indeed
expect the same three parameters of email and password.
I'd also like to call out that this function will not only
create the user account, but will also
log in that particular user, which is exactly what I
want in this case.
Perfect, so I'll take this one then.
And now let's see this in action.
This time, I'll enter an email that is not yet in the system.
You@AwesomeKittens.Test.
And a password, [HUMMING].
And when I tap the Sign Up button,
I can see that I'm no sign in as You@AwesomeKittens.Test
over here in the console.
However, the UI doesn't reflect that I'm signed in.
So the user doesn't get any feedback,
which is a pretty terrible UX.
So in the next step, I'll show you
how to update the UI according to the authentication state.
To update the UI according to the authentication state,
we can make use of the onAuthStateChange listener.
This method allows us to register
a closure that will be called whenever the authentication
state changes.
For example, when the user signs in, or logs out.
The first thing is to add onAuthStateChange to the list
of imports.
Now let me create a function that I can call
as soon as the UI is loaded.
I'll call it monitorAuthState.
Inside, I'm going to call onAuthStateChanged, which
takes the closure that will receive
two parameters, the auth object and a user instance.
The user parameter can either contain a JavaScript object,
which represents the currently signed in user or 0,
which means the user has signed out.
So if the user has signed in, I want to hide the login form
and show the application window instead.
And if the user is 0, which means they are signed out,
I want to show the login form and hide the application window
instead.
And now when I refresh the browser,
the application window is hidden and I can only
see the login form.
When I sign in using one of the accounts
I created earlier, the login form disappears
and the application window appears instead, showing me
some details about the user.
Perfect, we're almost done.
Finally, let's take a look at how to log out.
Logging out is probably the simplest function call
in Firebase Auth.
Again, let me impart the function first
and then implement a simple function
and connect it to a Logout button.
Like all other Firebase Auth functions,
signOut is an async function, which
is why we call it using await.
And now, back in the browser, I can log out of the application.
And thanks to the authentication state
listener I implemented earlier, the UI updates automatically
and displays the login form again.
And with this, we're at the end of this quick introduction
to Firebase Authentication on the web using v9
of the Firebase JavaScript SDK.
A couple of things to keep in mind before I let you go.
One, keep in mind that picking a strong password is challenging,
so consider using an authentication mechanism
that doesn't require your users to deal with passwords at all.
For example, email and link authentication
works great, doesn't use any passwords,
and has the added benefit of verifying the user's email
address as part of the sign up flow.
Two, use the Firebase Emulator Suite
to develop locally and speed up your development cycles,
as you saw in this video it's easy to connect
your project to the emulators.
Literally just one line of code, and you
don't have to worry about messing up your production
data by accident.
And finally, check out all the other authentication providers
such as Google Sign-In, Facebook Login, or Sign in with Apple.
What's really cool about them is that users who are already
using one of these providers can sign
in to your app with just one tap.
It really doesn't get any easier than that.
Thanks for watching, and we'll see you soon
on another episode of "Firebase Fundamentals."
[MUSIC PLAYING]