Web Authentication APIs

Identifier-First Flow

Maximiliano Firtman

Maximiliano Firtman

Independent Consultant
Web Authentication APIs

Check out a free preview of the full Web Authentication APIs course

The "Identifier-First Flow" Lesson is part of the full, Web Authentication APIs course featured in this preview video. Here's what you'd learn in this lesson:

Max implements an identifier-first workflow to the login form that determines the method of authentication based on the user's email. This allows multiple forms of authentication to be supported without cluttering up the login UI.


Transcript from the "Identifier-First Flow" Lesson

>> Something that to apply this to our website, so how to apply this to our project. So we have our project here, so where we should add web authentication login, like face ID login? Well, we need to change the flow on this login form. We need to make a change, like an architectural UX change.

That is actually a pattern that we should be doing these days on every project. That change means it has a name. It's called Identifier-First Flow. So, so far, we have a login form, username and password, okay? We need to change that. So we are going from the login standard form, and we are going first to a first step that will just ask for identity.

The username, frame identifier, a username, your email, whatever. So not the password yet, okay? So, remember the website, webauthn.io, that we have just been playing with? It was asking me for a username, not the password. So, we ask for a username or an email address, whatever is the ID for your system.

Then we ask the browser and the browser may be asking, or our project may be asking also the backend about the login option for that user. Okay, so we can check, okay, for that user, for m@m.com, what do we have available? And maybe it's a password, maybe it's, no, touch ID, that user saved touch ID before.

So then we offer the user to login directly with touch ID. Does it make sense? So we need to change the flow. So then as a third option, we offer the user to enter a password, or login with other options. So we do change how we design our screens, okay?

So that's how typically this flow looks like, Identifier-First Login Forms looks like. First, we ask for your email address or your username, then you hit Continue. And then the password appears or another option appears. And in the future, if we get into the top of our diagram passwordless, no one will get the password text field.

People will just get different options to log in that are actually not password-based. So far, we have the standard login with username and password, we can also login with a federated Google account, so I'm logging, I can log in, I can log out. But I need to first to adjust the flow to this idea of Identifier-First, I need to first hide the password.

So I don't wanna see the password first, okay? I wanna see that as a second step. So for that, let's make some changes in our code, and then we can add web authentication. So going back to our code, I will start with the HTML. So if you look at the formLogin, what I'm going to do is I'm going to wrap the password, both the label and the input, with a section that will start hidden by default.

And let's call this section login_section_password. So this is the section for the password. And just because I'm here, let's add another section that will be hidden also by default. That will be for webauthn, later, we will add something there. So login_section_webauthn, okay? We will add something there. So what's the idea?

The idea is that our login form, we have different steps now. By the way, this is the hidden Boolean HTML attribute that will also hidden it by CSS. So they won't appear. And now, we need to add some logic to make that work. So yeah, now if I refresh, I'm not seeing the password, okay?

But yeah, I need that Continue button to do something different, okay? So I need to add the idea of final step. So I will go to my auth.js file, and it will look for the init function that, it was there empty. So I'm going to create a property of my auth object.

Let's call the loginStep. So we are in step 1, we start in step 1. And the idea here is that we are going to hide and do this step based on where we are. So what we can do is at init, we could take document.getElementById, the two elements that we have just created, so login_section_password and also hide them.

Just in case you are coming back to the login section later, remember this is not React programming, I don't have any library, any framework. But I have a routing system, a single page application routing system. So I should do this the classic old fashioned way. Okay, so the next step now is to actually change what we're doing when you log in, okay?

So when we log in, we need to check, first, in which step are we, okay? Because yeah, now when we are logging in, okay, this one, I need to check the step, okay? If we are in step 1, I don't need to do this. I don't need to go and try to log in because I don't have a password yet.

Make sense? So I will still keep the same button, the same Continue button that we have. So I don't need to create two buttons. Of course, you can redesign this as much as you want. I try to keep it as simple as possible. So what we can check if verifying here, what's the current step?

The loginStep. So if the loginStep is 1, then I don't need to do this. I will do this only if it's a step 2. So far we have only two steps. So this is step 2. And if this is a step 1, okay, then what I need is a new process.

So what do I need to do? I need to ask the server, hey, server, which options do I have to log in this user? And the server will tell me, well, Google password and also face ID, that's the idea. So then I can render different parts of the second step based on what the server is saying, okay?

Make sense? That's kind of how this two-step process work. So identifier first, and after that, we see what we can do, what we can offer to that user. So here I'm going to create a new process that we don't have right now, that we are going to call this checkAuthnOptions.

You can add the n or not, it's up to you. So then I need to create that. Right now, We're gonna start empty, because to understand the idea, maybe we should go to the server first and try to bribe that server part to understand the flow, okay? So then in the server, we're going to create a new endpoint post, all of them are post.

And we can call this auth/auth-options or options because we are already at the auth, anyway. And what do we need to do? Well, first, we need to find the user. So we're going to foundUser, we're going to find the user based on the body's email. So that means that we need to receive the email of the current user that we have the email input type.

Okay, so we have that moment. And if it's found, we're going to do something, and if not, there is no user, so maybe we have a problem. So we don't wanna say to the client that user does not exist. Why? We have already covered that problem. We don't wanna give a possible attacker more information that they need.

So that's why I'm going to say, you know what, yeah, you can put me a password, okay? Yeah, I accept passwords. Make sense? And then if I found the user, I'm going to send an object, for example, saying, okay, do we have a password or not? Well, let's see.

So we can pass if password. Remember for logging Google, we were saying password false. So don't send the password to the client, that will be very wrong. So don't do that, even if it's hash. But we can say if it's not false, Okay? At least that means we do have a password, and well, we're sending, hey, it's okay.

And then we can also say, hey, if the user has a Google account, a Google federated login account, register so we can check on the found user if the federated object exists. And if that's okay, I will check if the federated.google value exists. And then for webauthn, we can add something here, okay?

So far, we're going to check if the property exists. Let's add the n here because that's the name of the API. Okay, we haven't seen yet how to make webauthn into our app yet, but at least we can already check if there is a property with that name in that user.

That for now, it will be false for all our users. Makes sense? So again, this is for the client to ask the server about the possible login options to increase user experience. So then when the user is trying to log in, we're going to offer, hey, you can log in with your USB key, yeah, I have my key.

So then I can put my key on the phone, or I can put my key here through NFC and log in with my key. Okay, so now that that's ready, we also need to add these to the API. So we go to API.js, and I'm going to add checkAuthOptions.

That it's an async function that receives a user. And we are going to return the await of making a PostRequest to the endpoint + auth-options. And we are gonna pass the user that we receive as an argument. So most of the API codes are pretty similar, okay? Make sense?

So we have now the server endpoint. We have that endpoint register in our API client side object. What we need to do is something here, when we are calling checkAuthOptions. And here I'm in the first step, the first step is to make that call to the server. So I'm going to get a response awaiting for the API checkAuthOptions.

And I need to pass an object with an email inside. Where do I'm going to get the email from? From the login form. Remember the user is typing an email. And remember, please remember this, if not, you will call me later, we are not validating here. So you need to validate everything, every entry before sending this into production.

But yeah, we are just writing code in one day workshop. getElementById, this is login_email, and we are going to get the value. And because now we have await, we should change this function to async. So what do we need to do here? Well, first, I should pass these to loginStep 2.

So now we will say, hey, we are in step 2 now, okay? So everyone knows. And also, you should start checking, okay, if the server says that password login is accepted. So if password login is accepted, I'm going to change the hidden property of the section. So okay, the password will appear, that's kind of the idea.

So I'm going to get an ElementByID, the ID is login_section_password. That's the one that we have just created a few minutes ago. And I'm going to change, we're going to say, hidden = false, that is double negative is going to be visible, okay? And something similar happens in the case the response is sending us webauthn.

I could do that something similar with Google, actually, but I think I had the div before making the request to the server. So that's fine, okay? Something like that. So again, what is the flow now? I'm asking the user only for her email or his email. And then after that, after the email, we'll go to the server, and the server will check which login options do we have for that email.

And it's returning an object with that information, then the client will actually show part of the login form according to that. That's the idea. Okay, makes sense? So for now, because we don't have webauthn, actually, if I use any of the email addresses to register, password will appear.

So now I can log in with password as before, as a second step, okay? That's kind of the idea for now.

Learn Straight from the Experts Who Shape the Modern Web

  • In-depth Courses
  • Industry Leading Experts
  • Learning Paths
  • Live Interactive Workshops
Get Unlimited Access Now