Firebase Fundamentals Enabling Firebase Authentication
Transcript from the "Enabling Firebase Authentication" Lesson
>> So Firebase authentication, what is Firebase authentication? It's a serverless authentication provider that integrates into security rules. So we haven't secured anything so far. We've seen how we could query things. We've seen all these things that we can do with the database, but it's been largely left insecure.
[00:00:16] And that's because we don't know how to secure it. We don't have anything to secure it against. So with Firebase authentication, you can log users in with just a plethora of providers, email password ,anonymous, which we saw earlier. A bunch of social authentication, so Google, Twitter, Facebook, Apple, Microsoft, Yahoo.
[00:00:35] There's a really cool one that I really like to use, which is email link, which is, instead of user entering their password, they enter in their email and then we send them a link. And when they click on that, we log them in, so passwordless authentication. You can use SMS, your own custom authentication system.
[00:00:52] So if you have your own back end that can integrate it in with Firebase authentication, we just use that to sign tokens. And then also for native apps, there's Game Center, so for Android Game Center, or sorry, Apple Game Center and then Android's Play Games. But that's mostly for Android and iOS development.
[00:01:12] So I'm not gonna painstakingly, like I made you all go through for Firestore, I'm not gonna go through every possible thing it can do. I'm just going to talk about these six things for the most part. I'm gonna talk about how the general sign in process works, so how I can log a user in.
[00:01:26] How I can observe their authentication state. How I can use multiple providers to merge an account, because no one likes it where they can't remember if they signed in with Google for that one, or do I sign in with Twitter? Did I provide an email or password? With Firebase authentication, you can actually, if a user attempts to sign in with a different provider and they have the same email address.
[00:01:48] We can flag that for you and say, hey, this user exists in your system with this email, would you like to link them in? And that's a really nice way, so users aren't accidentally creating too many accounts. Then we're gonna briefly talk about auth ties into security. And while we're gonna be using the Auth Emulator, and we're gonna dive a little bit into how you can script out users and stuff like that with the Admin SDK.
[00:02:12] And I think it's like almost the law that if you give a talk about authorization, you have to include a slide that says, authentication is not authorization. And in this section, we are just worried about authentication, who the user is. And in the next section, we're gonna worry about what they can do.
[00:02:32] And the most important thing to look out for in this whole process is the uid key, cuz that's gonna tell us who they are, and it's also going to, in the next section, allow us to write what they can do. So let's get into it. So I'm going to go into the authentication section, which is 4-firebase-auth.
[00:02:57] I'm gonna open yet another VS Code. So just to show what's going on here in my Firebase file, I am still connected out to the emulators. That is important to note, so I'm not gonna be working against production. And then now inside of app, I pre-imported all these things that I needed, just so we can write it a little quicker.
[00:03:20] And the way this works is, what I'm gonna use, I'm gonna do npm run dev I'm sorry, I need to be inside of start. That's why it's mad at me. Okay, so now, npm run dev. Great, so now we're back on local host 3,000, and we have this beautiful UI.
[00:03:43] And we're gonna do a couple things. We're gonna do talk about creating accounts, signing in, signing in with social, and there's a couple hidden ones over here, we'll see here in just a second, once a user is logged in. So each one of these functions is bound to a button or an event handler or something like that.
[00:04:00] And it calls this getFormDetails function, which is really great, because what it does is, if you've used form data before, it's awesome, you pass in form elements. And it allows you to use this map-like API to get the values within the form. It's one of my most favorite things that is in modern browser development.
[00:04:21] And so now, since I can get their email and password, I'm gonna call a method called signInWithEmailAndPassword. And actually, before I do sign in, let's keep that, I wanna do createAccounts first. So I'm gonna say createUserWith, we first pass in auth, and then email, and then password. Now, what I can do is, and I'll do this for now, even though I said this is not the way to do it, but just for now.
[00:04:50] I'll say user, and we'll see why, actually, results we'll await this, and this will give back our user. So now, if I go into the emulator, I'm gonna enter a new one. So email@example.com, enter in a really secret password, which is 1234FIREBASE, 1234FIREBASE. And actually, no, it'll trigger for me.
[00:05:21] So if I create, now, we have other things. So a user exists, so I'll wanna be able to sign out. I might wanna be able to link, and then I might wanna get their expenses by their UID. But if I refresh, now that's gone, has the user logged out?
[00:05:37] No, user is still actively logged in. The issue is, and this is one of the big reasons why I say we don't do this, is because when we refresh the page, in order to get this user again, we'd have to run this function again to get that output.
[00:05:52] So instead of having to get this user returned back, what I'd rather do is just call it in the same way I'd call setDoc or updateDoc or something like that. And then what I can do is I can call a method called onAuthStateChanged. I'll pass in authentication and then this will give me a potential user.
[00:06:17] So this is a lot like on Snapshot, and it's a lot on Snapshot in a way we're gonna log a user in, this is where I'm going to see their state results. And if they are already logged in, then when the page runs, it will fire this off with that logged in user.
[00:06:34] And this will be null if there is no logged in user. So now, all I have to do is say state.user = user, and everything's on the page no matter how many times I refresh it. So that is why we usually, in many cases, just like this, we do not await the results.
[00:06:56] We always dispatch the option with signing in, same thing with documents. And we listen to that in the listener, so in this case onAuthStateChanged. So now that we have that, let's work on signing in a user. So it's very similar, sign in, auth, email, password. And I'm actually gonna implement sign out because it's always good to be able to sign people out quickly.
[00:07:28] And so sign out, you just call sign out with Auth, that's it, and it will trigger the onAuthStateChanged, just make that null. So everything is gone. So I can sign in, but let's say I wanna create this user. This user exists, and so because this is email, there's error that says email already in use.
[00:07:49] And that's because in Firebase authentication, we have a one account per email address policy, unless you don't want that policy, and which you can opt out of it for whatever reason. But we put it by default, because it's really important to have emails as sort of unique identifiers of users.
[00:08:08] Because that way, it's easy to pick up when to link users across separate accounts. So if I try to log in with email and password, and my email is firstname.lastname@example.org, and then I log in with Google and my email is email@example.com, Firebase Auth will let me know, hey, this is a duplicate user, likely.
[00:08:27] And then I can prompt the user to link those two accounts together, so it's less confusing for the user, that is. So I can do that with linking. So instead of worrying about whether they're creating multiple accounts, I can call this method, you just call linkWithRedirect. And then here, we pass in the currently authenticated user, which we can synchronously get auth.getCurrentUser.
[00:08:57] And so auth.currentUser is a synchronously provided value that is populated after the user has been logged in. And then it says, well, what provider? So we can call a new GoogleAuthProvider, which is imported up top. And then this will go out to link that user with Google. And what's interesting here is that if I try to await this, This TypeScript will tell me, cuz VS Code is awesome and shows me cool stuff like that, will give me the result as never, so it will never get back here.
[00:09:37] And that's because we are doing what is referred to as a redirect flow. So when you try to log in with a social provider, you're not logging in straight through your site. It has to redirect out to that social provider or whatever Auth provider, login with their system, then there's sort of a dance back and forth, and then it comes back.
[00:09:57] So if we're gonna redirect out to a whole different page, we aren't going to be able to run any code that happens after that redirect call, because we're no longer operating on that page. And then when we come back, we want to know, were there any problems with this process?
[00:10:14] So to do that, we can actually call a method called getRedirectResults. This takes in a promise and we can actually await it. So we can try it in here. And then catch any errors that might have happened when logging in with a social provider or linking in the account.
[00:10:36] So did anything bad happen? So we are logged in still. And so let's say we wanna sign in or link with Google. Now, we're running locally. So one of the conveniences of the Authentication Emulator is that instead of actually going out to Google, Apple, or whatever Auth provider, we intercept that process when you're running on the emulator.
[00:11:06] And we say, hey, why don't we just create this manually or from anything that exists? So I can click, add a new account. I can auto-generate this information. Orange olive, I don't know if I like that name. What's a good one? Peach grass, I like that one. And I can then come in here, sign in with Google.
[00:11:26] And then now, I have these accounts linked in. So let me go look for it. Where is it? I wanna call it, sorry, I need to refresh. Just like that, here, we logged in with this user and we can see that has the G right there. And edit user, and I guess, it just shows that one email.
[00:11:51] But we can see that we have this user was only originally email and password, but now they're linked in with a Google account. All right, so we did link, we kinda went through, social login works very similar, I skipped over that one. But with social login, it's the same thing, you would just say signInWithRedirect, and we'd pass an Auth container, and then we'd say new GoogleAuthProvider.
[00:12:18] And this does the same thing, it's gonna go out to Google, it's going to do the whole OAuth dance. And then it will come back with getRedirectResult to sign the user in. And now, from there, if I wanted to get the user's data, I can easily do that.
[00:12:37] I can get from my Firestore instance, and do I have Firestore import? I do, so I can do, inside of here, I can have my user data, and I could say, what is this, state. State.expenses is what we're going to sign into. And so now, I can say, all right, well, let's go out and get this user's expenses.
[00:13:08] So state.expenses is a collection of passing the Firestore instance. And I'll say expenses/, showing off this one as a generalized data. We're gonna say expenses, my gosh, big bucks. I'll write a query in this case for that. And I'll say where uid ==, User.uid. Wait, no, I need to run this first.
[00:13:49] I'll just create the query, I think it const userQuery = this. And then we can say const snapshot is getDocs of the userQuery. We're gonna await it in this case. And then now, I can say snapshot.docs.map(d => d.data()). So we're getting this, we're probably binding something wrong here.
[00:14:23] What is it looking for? Expense.text, that's right, I got too clever with this. What I'll do is, I'll just say, return a value of text, that is the d.data.cost, we'll just it's d.cost. And then, all right, here are some expenses that belong to that user. So that's really authentication in a nutshell.
[00:14:53] We can sign users in with multiple providers, we can link them in. We can, also with that UID, begin to assign ownership of that data. You can see how there'll be a process saying, hey, when I create a new document, let me either create a hierarchy of users/uid/expenses.
[00:15:16] Or expenses and attach the UID, whatever you'll end up needing to do. But that UID is how we get that user's data associated with it, and that all spits out with Firebase authentication. And that still doesn't get us anywhere with security. So that is where we're gonna move on to next.