Facilitating the Spread of Knowledge and Innovation in Professional Software Development

Write for InfoQ


Choose your language

InfoQ Homepage Presentations Implementing Passwordless Logins with WebAuthn Protocol

Implementing Passwordless Logins with WebAuthn Protocol



Adib Saikali overviews the Web Authentication protocol which enables secure user-friendly authentication processes. He is using a sample Spring Security-based application.


Adib Saikali is passionate about technology and entrepreneurship from assembly to JavaScript, from pitching venture capitalists to advising senior IT executives at Fortune 500. Adib is currently a Principal Platform Architect at VMware.

About the conference

InfoQ Live is a virtual event designed for you, the modern software practitioner. Take part in facilitated sessions with world-class practitioners. Hear from software leaders at our optional InfoQ Roundtables.


Saikali: My name is Adib Saikali. I've been a software developer since 1995. I've been a code janitor since 2014. I'm currently a Global Field Principal at VMware Tanzu, focused on Kubernetes, Cloud Foundry, and Spring. I spend a lot of my time working with customers on cloud native application architectures, both modular monoliths and microservices. I spend a lot of time also on application modernization and refactoring patterns. For the last little while, I've been doing a lot of stuff around application security. I'm also currently authoring a book called, "Securing Cloud Applications" for Manning publications.

Thought Experiment

I want to start off with a thought experiment that I want you to think about. Imagine that you've built an application, which is following all the secure application coding practices. You've even hired a security consulting firm to audit your code and check that you're not making any of the typical mistakes. You've trained your users to use all of the strong password best practices and management practices for passwords. Your application actually has two-factor authentication, so when users log in, they have to give a password, and they have to give a second factor, like a one-time token from one of the authenticator apps. The question is, how do you break into this application, a super secure application? The answer is, if the application is very secure, what you attack is the human that's using the application. The way we do that is typically called phishing. The hackers are going to build a website with a login screen that looks just like the real site that the user wants to log in to. Then they trick that person into going to that fake website where the person enters in their actual username, password, and the one-time token credential. Then once the hacker website has it, they use those credentials to access the real site and steal money or do unauthorized things, or steal the user's data.

The key thing, it doesn't matter how strong the technology is, humans can be fixed. Humans can be socially engineered into doing something they're not supposed to do. We've all seen these action movies, heist movies, where there's some really sophisticated security system with guards and all this type of stuff. Part of how they break in and pull off the heist in the movies, is they pretend to be someone they're not. They socially engineer the front gate security guard, or something like that. With humans that can happen. What we need is we need a phishing resistant authenticator that can validate that the user is accessing the real website and not ever complete an authentication against the phishing site. This is a fairly well developed technology. It's been around for a while. There are lots of choices on how to do it. The authenticators that are phishing resistant, you can see on the left-hand side my personal collection of authentication keys from three YubiKey keys, the blue one and the two black ones. The right one is the Google Titan key. All of these keys work, and you can program them and add them into your application using a fairly standard protocol, called the web authentication API. It's not really a protocol, it's an API. The other group of authenticators are called platform authenticators. These are practically built into all the user's devices. They are here for free. The user doesn't have to go out and purchase a physical device. For example, your iPhone's face ID, the thumbprint scanner in your laptop. All operating systems pretty much support them: macOS does, iOS does, Android does, and Windows. Windows Hello supports this stuff. The point here that I'm trying to get at is this is widely available, you can start using it.

Demo - WebAuthn User Experience

I'm going to start with a demo of what does the web authentication protocol experience look like from the point of view of a user. Then I'm going to go into explaining how that demo works underneath the hood, like what's happening. Then we're going to dive into a demo application that's designed to teach you how all this stuff works. We're going to look at what's happening in the code of that application. I'm going to head over to this website here. Let me just click on the link, What I'm going to do is I'm going to use this website twice. First, I'm going to use it for my laptop, and I also have my iPhone here. Let's pull up my iPhone. We're going to have two browsers, the one on the left is on my Mac, and the one on the right is on my iPhone.

We'll start off here, and let's pick a name. I'm going to call this one, adib2, that's the username. First, I have to register. When I click on Register, that's when my browser pops up this nice dialog box, and it says, I see you would like to associate a hardware, some authenticator with this website that you're trying to visit. Here are your choices, would you like to use a USB security key or would you like to use this particular laptop? I'm going to use in this case a USB security key. You can't see it on the camera, but there's that blue YubiKey that you saw on the photo earlier, it's plugged into my laptop. It's flashing. I got to go and press the button, and success. It says now I can log in. Let's actually see what happens if I do that. If I click on login, I get this dialog box again. This time, it's basically saying, verify your identity through How would you like to do that? I'm going to use a security key. It's flashing again, I got to go and press that. Press that, I'm in. You can see that the backend application, what it knows about me, it knows the public key. What's happened is that there's a public-private key pair that was generated inside of the authenticator. It knows the public portion of that.

Let's actually continue on. This time here, let's actually create the same user. I can't type that, I have to use my phone. I'm going to go here, and I'm going to type adib2. I'm going to click on Register. You'll notice that my phone is giving me a choice. It's giving me a choice to use the face ID, which is built into the iPhone or use my security USB key. I'm going to opt in to using face ID. As you can see, that worked. Let's try logging in now. I got to click on my phone. Again, when I tap my phone for login, I'm asked if I want to use a security key or face ID. I'm going to go for face ID because that's what I used. I'm in. This time, if we scroll down here, what we're going to see is now there are two keys that are registered with the adib2 user ID. One that I did earlier, which is the YubiKey, and the other one that I just did on my iPhone, which is related to face ID. If I refresh this page, now I've got the two public keys that it knows about me.

Let's actually go and try this again. We're going to now do adib2, let's try register. It pops up. This time, I'm going to register the actual laptop. You can see it popped up a standard macOS, please scan your thumb. Now I can do a login. Because I have my thumbprint, now it's asking me for that. If I log in, I should see now there are three public keys that are known about me. There is the public key that's associated with the YubiKey that I did first. There's a public key that's associated with the face ID on the iPhone. There's a public key that's associated with the actual macOS thumb scanner. That's a pretty neat trick here because I was never asked for a password. We're using public key cryptography which is well known and quite secure for doing these kinds of operations.

How Do Phishing Resistant Authenticators Work?

Let's go back and just look at what we actually did and what happened. The way that these phishing authenticators work, is the authenticator is going to generate a unique public-private key pair to identify a user on a specific website. The private key is never going to leave the authenticator, it's going to be stored in its secure storage facilities. The public key is what you share with the website that you're logging into. When you come back to the website, you want to log in without a password or you want to log in using the key as a second factor. The authenticator is going to be asked to complete the authentication process to complete the login process. What the authenticator is going to be given by the web browser, the web browser is going to tell it this is the actual URL, domain name, host, port number that this user is on right now. I want you to try to login to this URL with this particular user ID with this set of matching public keys. The authenticator is going to search through its internal database, and if you are on a fake website, it's not going to find anything. It's going to come back saying, I don't know how to log into this, because I have no public-private key pair that matches the fake website. If you are on the real site, it'll say, I know exactly how to complete the login and solve this cryptographic challenge. The bottom line is, you can fool the human into thinking they're on the real website, but you can't fool the actual web browser, because the web browser knows where you are. It knows the URL. The authenticator is checking with the web browser, it's not checking with the human to validate where you're at.

Industry Standards that Power the Phishing Resistant Authenticators

Underneath all of this are really two standards. One is the CTAP2, which is the client to authenticator protocol. This is an actual protocol that defines how these hardware keys are going to interact with an actual device, with a phone or with a laptop or a desktop. The specification is fairly low level, it's defined by an industry consortium called FIDO Alliance. It's what allows me to use the Google Titan key, the YubiKey, or any other manufacturer of these keys. They're all interoperable because they all in the hardware implement the CTAP2 protocol. You don't write your application code against CTAP2, what you do is you write your application code against the web authentication protocol. The web authentication protocol is a browser based JavaScript API. As of today, 90.4% of browsers support the web authentication protocol. Your code calls the JavaScript API. The JavaScript API, in turn, turns around through the browser and calls the underlying authenticator, the other CTAP2 protocol and the OS libraries.

Here's more of an architecture diagram that shows how this works. On the client side, you're going to have the web browser. It's going to download some JavaScript from the server side application, known as the relying party in the terminology of the protocol. What will happen is that you're going to do two things. One is you're going to first register the authenticator, which is what I did, just one flow. The other one is authenticating. You'll see here the web authentication API in the browser, when your application calls it, it's going to call the platform authentication API, like your macOS, your Windows Hello, whatever it is. That is then in turn going to talk to any one of these external hardware authenticators the client's platform wants using the CTAP protocol.

The main message here is, this is widely available. This is widely deployed. Your users already own phones and devices that are capable of doing this, all you got to do is add web authentication to your web apps. Some famous ones that currently support it, for example, GitHub, you can actually register these keys and use it. Facebook, you can use this with it. Gmail, you can use it with it. Of course, with all your cloud consoles, your AWS, your GCP, or your Azure, you can do the same thing.

Demo Code for Adding WebAuthn to Spring Boot

What I want to show you now is get a little bit deeper on this, and go into the code. I've written a sample educational application using Spring for the backend and the Yubico Java library. On the frontend, what I'm doing is I'm using plain old jQuery with raw JavaScript, so you can wrap your head around how this protocol works, so you can actually add it to your own application. For the backend, if you're a Java developer, the Yubico library is wonderful. On the frontend, GitHub publishes a really good JavaScript client library. I didn't want to use it in the sample app, because I want it to be very educational. It was easier to be educational by just using jQuery.

If you go through to the Git repo, you'll see that it's going to tell you how to run the application. It also will point you to certain pieces of code and say, you go check out how this code works, go check out how that code works, so you can try and wrap your head around it. Let's actually see what this sample application does and see this protocol in action. Let me switch here to my IDE. What's happening here is the following. I have the application running. Like I said, it's just a simple Spring Boot application. I can go to localhost 8083 to access it. Let's go here. Let's refresh this page. The thing you'll notice here is it's choosing HTTPS, even though it's running on localhost. The reason why is that these web authenticators, the protocol requires it. The WebAuthn requires it. If you are on an HTTP address, and you try to interact with things, it's going to say, "That's insecure, I'm not going to do any registration or logins with that. Please use HTTPS."

The first thing we have to do is we create an account. I have on the right-hand side, breakpoints in the JavaScript so you can see what's going on. Let's click this. We're just going to do We'll register with that. Let's do submit. What's happening here is the following. I have a jQuery listener, which is when you hit the submit button, it intercepts the request. It pulls out what was typed into the form. It's going to make an HTTP POST to the server. The way that this app works is that in order to be educational, it prints out to the screen, the JSON that it is sending to the server, and this JSON that the Spring Boot application is sending back to it. What's happening here now is we're going to say, $ajax. We're going to do an HTTP POST to this particular URL, which is just a Spring MVC Controller that I wrote on the backend. The Spring MVC Controller, in turn, is going to call the Yubico libraries for web authentication to make this work. I don't have to implement the tricky logic for doing what the backend is supposed to do. There's a nice library that you can use. What I like about the Yubico library, it has a lot of safeguards to try and force you to use this the right way.

What we're going to do is we're going to send this to the server. Let's see what the server sends back. We sent this to the server, the server got back a response. Let's see what that response is. You can see it right here. There's two elements to this response. One part here, this flow ID is just a correlation ID that I am creating to track requests and responses, record them in a database, so I can debug it, and we can learn how it works. The second part here, the credential creation options is pretty interesting. This JSON object was generated by the Yubico libraries on the backend. What it's designed to do is to tell the JavaScript and the frontend, how it should call the web authentication API. What options it should pass to the web authentication API. We're telling it that it should identify the backend application as the example application that the application is on localhost. It should specify the user. This is the name of the user that's registering. This is the display name. This is the ID on the server of the user. There's a cryptographic challenge that is supposed to be solved with this request-response flow. These public key cred parameters are identifying the types of public key algorithms that the server supports. For example, RSA or elliptic curve. There's a bunch of other options. That's the create credentials options.

We're going to go to the section that says public key. On the public key here, when we get into the create credential option, we need to call the API, but the API for web authentication expects certain things to be JavaScript byte arrays, or as the server gives us URL encoded Base64. We need to do some data conversion. That's what I'm doing on line 88 to line 101. Let's just skip over that and continue on. At this point here, we're going to get to navigator.credentials.create. This is actually where we're going to call the web authentication API. Let's see what happens when we do that. We got a dialog box. What our dialog box is telling us is telling us to say, how would you like to identify yourself to this app? I'm going to click on the USB security key, and my YubiKey is flashing. I'm going to go and press that. When I press that, you can see here that I'm in the success handler for this. What that means is that a public-private key pair has been generated. We need to communicate the public key to the Spring Boot application, so it can register it in the database and associate it with the user.

What I'm going to do now is I'm going to just hit continue. We're going to end up in the finish registration step. What we're doing in the finish registration step is we're doing two things. One is, we're creating a JavaScript object to send to the server. Let's actually see what that JavaScript object looks like. This is the JavaScript we're going to send back. You can see here the credential, the ID, this raw ID, this is information that came back from web authentication API. We're packaging it up in this credential object, because that's what the Yubico library wants the JSON to look like, and it's easier to deserialize it. I'm going to then continue this. Now that it sent it to the server, the server is going to come back and say, everything is good. Now there should be a user that can login. If I go here to the logins main, and I can type Click on login. What's going to happen is the same thing that I did before, jQuery is going to intercept the call. We're going to start the login process.

You can see here, what I'm going to do is I'm going to do an HTTP POST to this URL, which is a standard Spring MVC Controller, that's going to kick off the, "I want to log in." We're telling the website that we want to log in. The website is going to come back and say, if you want to log in, what I want you to do is I want you to use the authenticator. Specifically, this is a cryptographic challenge I want you to solve. This is the ID of the site. This is the public key that I have about this user. Authenticator, can you go into your database as the authenticator, find the private key that belongs to this public key. Solve the cryptographic challenge, and give me back the response. If you succeed in solving the cryptographic challenge, then I know that this is the user, and therefore I will let them in. Let's actually see what happens when create credential is called. This is the create credential function. What we're going to do is we're going to need to generate a very specific object to give to it, we need to do some data munging. We're just going to skip over the data munging piece here. We're going to get to the part where we're going to say navigator.credentials.get. We're not calling ..create, only .get. What we're saying here is, can you please take this information about the public key and create an assertion out of it. Let's watch that happen.

Now we got the little popup, we're going to use the USB security key. I'm going to go and press the YubiKey. Now we are in success. Yes, we've been able to successfully log in, or the authenticator has been able to successfully solve the cryptographic challenge. What we're going to do is we're going to say we need to report to the server the result of interacting with the user on the authenticator. I'm just creating a JavaScript object to send to the user here. That's what the login finish request is all about. It's like, I'm done with the login. I'm going to post this now to the Spring security login endpoint. You can see down here. Here's the username, here's the finish. Spring security is going to intercept that. I have to add a custom filter that handles this. You can see it in the backend Java code. If this works, it's going to say, you're logged in now. We know who you are. What that allows me to do is to go here. I can go to this page, and I can actually see the pitch and I can see what my application is doing.


We had to write a bunch of JavaScript to interact with the web authentication protocol in order to register a hardware authenticator key, in this case, a YubiKey with the Spring Boot backend. Then when we came back to log in, we use that as part of the login process. We were not asked for a password because I didn't configure this thing to use passwords. You can use these web authentication to do passwordless login, or as a second factor. Realistically speaking, if you lose the key, too bad, you lose access to the site. You probably want to use it typically as a second factor, rather than completely passwordless registration and passwordless login. Depends on your application, what you're doing. On the backend, it is a Java application. Pretty standard vanilla Spring security and Spring Boot.

Questions and Answers

Losio: The way you started talking about attack the human, I remember me a bit when I was young reading "The Art of Deception" and the entire concept of social engineering coming from basically, at the beginning, hack the phone, then hack the computer. Anyway, you try always to get on the weak link of the chain. What is going to be the weak link with passwordless technology? It's still the person with the YubiKey in his hand? Is the goal then going to be to protect the YubiKey, or your phone, or whatever?

Saikali: The goal is to help the human not fall for the fake website by having the YubiKey, the hardware authenticator like check that this is in fact the correct website that the user intended to be on. Because usually, you're being attacked during login and not during registration, more often than not. If you get sent to a fake website, and you register there, that's a different type of attack that this thing is going to help you with. If you already have an account, and you just went to the fake looking site, and you provide your credentials, that's what this is stopping you from doing. The way that it stops you from doing it is that when you get asked to log in, you press the key. GitHub does this really well. Because what happens with a GitHub scenario, with a GitHub app is you still have a password with GitHub. It's when you go back and you're like, I need to create a developer key, I need to do something sensitive. My expectation now is I don't have to type in my password, because it's going to ask me just touch the key. Which makes my life easier because I don't have to fumble with my password manager to go copy and paste it in. That's pretty neat.

Losio: It's still a second layer in that scenario of GitHub. It's not the first and single layer. You are in the scenario you describe as, when you still use two different paths.

Saikali: Yes, you still use it like a secondary with a password. Say you're building an application that's aimed at human users like B2C. How many people just abandon, including myself, think of when you just abandoned a website because they want you to create an account. Like, I have to think of a password and all this type of stuff. In that scenario, you're more like evaluating whether you even want to use the product or not. I think it's pretty handy in that case, because what you can do is, you can type in your email address, or user ID, use your phone, or whatever device you're using to finish creating the access to the account. Start using the application, and if you like it, the application when you come back can insist you set up a password, and then it becomes a second factor with the web authentication.

Losio: Of course, usually with any two-factor authentication, any hardware device, you have that feeling of, it's going to make the usability worse. It's going to be good for my sysadmin because it's ok, a higher level of security needs to do it. For the general user, it's going to be harder. At the same time you're right, there's one side of that, that is I don't need to generate password, and the sign-up can be much smoother as you did in your examples.

Saikali: It actually is making the user experience much better. What I find that's interesting about it is because it works on the phone, and it works on the Mac T chip, and the Windows Hello. All of these sites always, like GitHub, they allow you to register multiple keys. If you happen to be getting into GitHub from your phone, and you use face ID, it'll just ask for that. If you happen to be in something else, then it will ask for it. Because what's happening is that the backend application will know which public keys identify the user, and then the authenticator can select the right matching private key. This also allows you to have multiple physical keys in case you lose one.

Losio: That actually addressed part of the problem I had with a single key. Actually, it raised a very interesting question, that is, how do you deal when we have multiple devices? The common scenario is, if I have either only the phone or only the laptop, so you have multiple devices. It's quite a common scenario. It's good to have the YubiKey in my laptop, apart from GitHub that may be still a developer tool, but I might want to access the website from multiple devices. How do you handle that?

Saikali: You have to register multiple authenticators because you have a private-public key pair that identifies you, and the private key is always going to be in the authenticator. It never leaves the authenticator. Currently, there isn't, unfortunately, a way for you to synchronize all of your keys. The FIDO Alliance just put out a white paper in March talking about an approach that they're proposing to allow for the replication of your private keys from these things. For example, you can imagine something like Apple already lets me cut and paste between my iOS device and my Mac, why can't they replicate the key? It's available in some secure way that only they can do for their technology. I can imagine that those will come as this becomes more popular. My goal with the talk was to really just raise awareness that this thing is available. Browsers support it. What are you waiting for? You could add this to your application today, and they'll improve the usability of the app and security.

Losio: What you say basically, is in the example you made of they're using their iPhone with face recognition, for example, and a YubiKey on my laptop. Technically, they have two private keys, each one independent and each one sitting in the device and never leaving the device.

Saikali: Yes, which is pretty neat, because it also means you can invalidate this. There's a lot more about this that I didn't show, for example, corporations. They might basically say we'd like you to use this as a second factor, but there's a very specific brand of device we want you to use. We don't want you to just bring some random no-name knockoff authenticator that might be not secure. There's actually a capability for the backend application to insist on, I'm looking for this particular model of authenticator.

Losio: For example, it has to be a physical hardware authenticator, and not, for example, a phone.

Saikali: Yes, because your company may have given it to you. There's an option to basically request that the authenticator identify the user. For example, if I use face ID, it's only going to work on my face, in theory. Or if I use the thumbprint scanner, it's only going to work with my finger unless somebody cut off my finger. If the authenticator has biometrics on it, then you can ask for, can you identify and prove that it's that user? If you have a regular YubiKey that doesn't have a thumbprint scanner in it, and you require that, it'll actually prompt you to set a pin on the authenticator itself. In order to acknowledge and press the button to say please log in, you'd have to first know the PIN. This stops some random person finding an auth key to actually using it to log in.

Losio: That addresses the problem of the lost key.

Saikali: Yes. Think about it a little bit like you have a physical house key, and then somebody finds it, and they can get into your house if they know where you live, from this key.

Losio: You mentioned that it is widely available, for example, 90.4% of the browsers already support it. In terms of devices, as well, you made the example with the iPhone and face recognition. Do you need any specific device? It's supported on Android, any recent version?

Saikali: It's supported on Android. A couple weeks ago, I did a webinar with a customer, and we had 235 people show up for it. Then I asked people to try out using their devices, and pretty much 87% of those people were successful from a group of 200-something. The user interface is slightly different on Android. If you go to, just go with your various devices that you have, your old Android, your old iPhone. I'm really fascinated by how these things react, because it's just a piece of JavaScript that calls the browser and then something happened, but it's always platform specific, like the shape of the dialog box you get. If you try this with Safari, it works. If you try it with Firefox, it works. Firefox has got the worst UI for this. It only works with cross platform authenticators, it doesn't seem to work with a chip, like on a Mac. There's some limitations. It's definitely worth time for developers to start learning this.

Losio: I was wondering if it was widely available, but basically, mainly to the U.S. and European market, or if we can consider widely available everywhere, basically.

Saikali: It's widely available everywhere.

Losio: I was thinking mainly about mobile devices, because, of course, a YubiKey you can buy it everywhere, but the cost of YubiKey may as well be significant, not for an administrator, but for a user that maybe just wanted to release a simple website. It is not the main device you want to have. I was mainly thinking about mobile technology.

Saikali: Yes. The electrician that renovated my house, he logged into a Windows machine at a job site, and he was using Chrome and Gmail to manage his passwords. Within two hours, people were buying laptops. Something was buying laptops all over the world with his information, people were draining his bank account. He was able to put a stop to it, and only lost a few thousand dollars. At the end, Google basically came back to him and said, A, never log in to an untrusted computer, but, B, you should buy a Titan key to stop that. If you had a Titan key, you wouldn't have been able to authenticate into it.

Losio: I'm really fascinated with this tech, and I really would love to see them and add them. I was thinking that, if I had to start a project from scratch, I have no problem thinking about that. I was thinking about how I can sell and how you migrate to that. I have a project, I have something live. Management usually doesn't care too much about the authentication. You gave a good point about selling the concept that that's a new usability that may be harnessed. I was wondering if there's anything else and how you migrate to that, like support both.

Saikali: You want to be leveraging some single sign-on server of some kind. It's the single sign-on server, which is the right place to put it. If you have an application that today has a custom username and password flow that you've written your own thing, I would migrate that away to something like Okta, or Auth0, or Spring Authorization Server, or Keycloak, or PingFederate, or any one of those, as a first step. You're saying, in my code, I just want to provide a resource server using OpenID Connect. Then that resource, that login server that you create, that's where you do all this innovation with. I can let you log in with passwords. These are the one-time tokens, this is web authentication, so you can isolate it there. In the same Git repo, that somebody actually put it at the time. There's also a bunch of sample applications that show how to use the Spring Authorization Server, which allows you to build your own SSO service.


See more presentations with transcripts


Recorded at:

Oct 26, 2022