Two-Factor Authentication Login in Node.js
00:00 Let's start in our login route and right up here we need to create a handle verification function. It's actually going to look a lot like our onboarding handle verification function, so I'm going to copy that to get us a little bit of a head start here. We're not going to do this bit though, so we'll get rid of that.
00:18 And we're also not going to redirect to onboarding either, but we'll get to that here in a bit. So let's bring in invariants, let's bring in the verify function args, and then let's call this handle verification over in the verify route. So right here we have this case for two-factor authentication. We're just throwing this error right here.
00:36 So instead we want to return handle verification from login, and inside of here I'm going to alias that to handle 2FA verification, and then we can pass the request in body. Now here's a little bit of a trick or a challenge that we've got here. With our previous verifications, they were just temporary,
00:56 and we would delete them as soon as we verified that the user was who they say they were, or owned what they said they owned. That was just the default. But this two-factor auth app, this is a thing that we want to keep around for as long as the user has two-factor authentication enabled,
01:15 and so we don't want to do this. So what we're going to do is have a function called delete verification, and then we can move all this inside of that. And with that done, we can call these in these three functions. We'll await each one of those. And with that, we can be a little bit more explicit about
01:37 when we want the verification to be deleted, and we won't delete it after the user is verified only once. So that way they can do two-factor authentication over and over again because we're not deleting the verification. Okay, great. So now we've got to fill this handle verification in. We've got the request.
01:57 We've got the submission. We're going to get the session storage as well as the verified session because we need to swap the place of our unverified session ID. So let's grab our cookie session. Let's see if Copilot can do this for me. Thank you, Copilot. Great.
02:16 And then we need to get the session ID that the user is verifying with. So let's do that session or unverified session ID. There we go. That works. And you know what? I actually am not a huge fan of these magic strings right here.
02:35 Yeah, I'm not jazzed about that. So let's actually take this, and we'll extract this to a constant in the module scope, and we'll call it unverified session ID, and we'll do the same thing for this as well. Module scope, remember key,
02:56 and here let's actually have key on that. There you go. And then that way, here we'll move those up kind of toward the top. That's where I want to put these types of constants. That way we can reuse these same things and communicate that intent that these two things are exactly the same. Unverified session ID key and remember me key.
03:19 Remember me key. There we go. Okay, great. So we got those values. So now we're going to look in the database to make sure that that session actually does exist. Maybe it took them a while. It somehow got deleted. They were maybe in a different app or a different tab, and they said delete all the sessions as possible. So let's just make sure that session still exists.
03:40 So let's say session equals await Prisma session find unique, and we actually not only do we need to make sure that the session exists, but we also want to get the expiration date for the session in the case that the user wanted to remember the session.
03:59 So we need to get the expiration date as well. So let's just select that. That's all that we really need. And oh, that's our select. So then our where is going to be the ID is equal to the unverified session ID. And then if there's not a session, then we'll want to let the user know and they need to try again.
04:19 So we're going to throw a await redirect with toast. And this will send them over to login. And the title will be invalid session. And let's give it a type of error, just to give that a proper look and feel and all that.
04:43 And the description can be something like, could that could not find a session to verify, please try again. And then the user can go ahead and try again. OK, so with this now we can.
04:59 And yeah, what we need to do is swap the unverified session ID or add that to a verified session inside of our cookie session. So we'll say cookie session that set. And that'll be session key to that unverified session ID. It has now been verified.
05:20 And with that, now we can get the redirect to where we want the user to go to from the submission value. And that is where we're going to send them. But this is a safe redirect because the user is submitting that. We don't want them to go anywhere they don't. They really shouldn't be. And then we just need to set a couple of headers.
05:41 We need to commit the verify session storage. We actually want to destroy it. Destroy, because we're done with this verification process. But we also need to set the cookie session. And so we could do our combined headers if we wanted to. I've tried both and I like the way that the headers object works
06:00 or looks for this particular situation. So we're going to use the append API so we can set multiples of these. And for our session storage, for our cookie session, we're going to set the expires based on if they said, I want to remember, I want you to remember me, then this will expire when the session expires.
06:21 And then it will also have headers append set cookie. And that will just be this right here. And then our headers can look like that. It looks lovely. All right, let's just try it out now.
06:39 So pull this up node OTP, generate that one time password and submit. Oh, my word. It's and it even redirected me where I wanted to go. That's exactly right. Wow, that is awesome. And we have two factor authentication enabled. I can go through the whole process again. We can log out. We can go log in.
06:58 Cody, Cody loves you. And then check my two factor authentication app. Copy that, paste it in. Boom. There we go. That's that's actually really awesome that the whole flow works. Two factor authentication. Amazing. So let's really quickly we'll kind of go through what we just did.
07:18 So in the verify route, we had it or added or handled this two of a case with this handle to a verification. We also made sure that we only delete the verification on a case by case basis for the situations where we do want to delete those.
07:36 And then in a login, we have this handle verification that gets our two different sessions that we've got. We get the unverified session ID as well as the remember me preference. And then we look for that session to get the expiration date. And if that session does not exist, we'll let them know that there's a problem.
07:55 This is unlikely to happen, but definitely could. And again, at scale, every edge case will happen a lot. So then with our cookie session, we'll set the session key to that unverified session ID and then grab the redirect to value from the submission. Then we commit all of our cookie changes and stuff.
08:14 We destroy the verify session as well because we're done doing the verification process. And then we redirect to the redirect to that the user wanted to go to. And that is the full two factor login authentication process. Good job.