Passwordless authentication using your phone's fingerprint sensor, or face recognition using your webcam is now possible in the browser thanks to the WebAuthn protocol. It is not only more comfortable for users but also more secure since it is two-factor authentication in a single step.
In this demo, we will use the free Passwordless.ID API. This is a public identity provider, and it will take care of the heavy lifting for us. It's free and you don't even need to register an account or download anything to use it.
The following picture describes what we are about to do.
Our demo website will simply redirect to passwordless.id to authenticate "passwordlessly" (no e-mail required either) and authorize reading the profile, then return to the original demo page.
Thanks to it, we will obtain the user's avatar for that demo and display it, that's it! What is also obtained, besides the user's profile, is a signed Json Web Token. This token can later be sent to your APIs as proof of the user's identity. More on that later.
Live Demo
Please note that the authentication will be blocked within the IFrame for security reasons, click on the upper right to open the CodeSandbox in its own tab!
If somehow the CodeSandbox is not shown properly or does not work, you can also see the live demo here and the source code here.
The Passwordless.ID API follows the OAuth2 / OpenID standards, just like google or facebook logins. It is nevertheless simpler and easier to integrate with since it is a public API, not requiring any kind of special authorization.
Step by step tutorial
Although it is compatible with most other "Sign in with..." client libraries as a generic OpenID provider, the easiest way to integrate with it though is to use the @passwordless-id/connect library.
Let us go through the code, starting with the "Sign In" button.
<button id="sign-in" hidden class="btn btn-light" onclick="app.signIn()">Sign In</button>
Now, this is very basic, and depending on the frontend framework you use it will look different, but this is the gist of it.
The app.signIn()
looks as follows.
import passwordless from 'https://unpkg.com/@passwordless-id/connect'
function signIn() {
// redirects to the authentication/authorization page and back
passwordless.auth({scope: 'openid avatar email'})
}
// a global `app` object just for the purpose of the demo
window.app = {
signIn
}
Once the passwordless.auth()
method is called, the user will be redirected to the Passwordless.ID UI which will prompt it to create an account or sign in and then to authorize your app to read the openid
, avatar
and email
of your profile. In case the user is already signed in and has granted access, the redirect would come back directly to the app.
When redirecting back, it will append the id_token
to the URL hash value (https://example.com/mypage#id_token=...
) . The profile can then be extracted from the URL using the following piece of code.
const user = await passwordless.id({scope: 'openid avatar email'})
if(user.signedIn && user.scopeGranted) {
// great, user.profile contains the information
// and user.id_token can be sent to APIs
}
// the user is not signed in or has refused
// to grant authorization
}
This chuck of code is typically executed when loading the page. The id()
function will first look at the current URL and try to parse the id_token
in the hash if present. Otherwise, a request will directly be sent to the Passwordless.ID API to obtain it (if the user is signed in and has previously granted the scope of course).
The function always returns. If the user is signed in and has granted the scope, the resulting user
would look as follows.
{
"signedIn": true,
"scopeGranted": true,
"id_token": "eyJ0eXAiOiJK...",
"profile": {
"nickname": "Johny",
"picture": "https://ui.passwordless.id/avatars/sam.svg",
"preferred_username": "johndoe",
"...": "...more attributes depending on requested scope"
}
}
That's it! You have signed in a user and read its profile!
What you might also need is a way to sign out the user. Since Passwordless.ID is a centralized authentication system, being signed in or out affects all websites using it. As such, you cannot forcibly sign out users. You can ask them nicely "do you want to sign out?" using the following line.
// redirects the user to the sign out page
passwordless.logout()
This will redirect the user to a sign out page, and once done (or not), back to the current URL.
Everything is now there to sign in, sign out and retrieve the user. Congrats! For the sake of brevity, we will skip the part of displaying the profile and other UI interactions. That is up to you to freely customize according to your website's style.
What about the servers?
The example before shows browser side interaction, with the profile information being directly available. But how could you trust that information server side? That's what the id_token
in the response is for! It is a "Json Web Token" (JWT), a signed proof of the user's identity.
The id_token
is signed usings Passwordless.ID's secret private key and can be verified using its public key.
There are two ways to validate the token:
Locally, by using one of the many JWT libraries available.
By calling /openid/validate
That way, your servers and APIs can be sure of the user's identity. Each id_token
will also possess a sub
field according to the OpenID standard, which is a unique user identifier. You may also request the email
as scope, but please be aware that an email is optional in Passwordless.ID, so you might not always obtain one even if you request it.
It's only an "Alpha Version"!
Passwordless.ID is not yet fully ready. It's at a proof-of-concept stage. Although it can already be used for testing and integration purposes, it is not yet quite ready for production.
If you think this is a meaningful public service and if you liked the tutorial, please like it and share it! The best way to make it succeed is to spread the word and encourage us. I hope you enjoyed it, I would be glad to hear your feedback!
Top comments (25)
the problem i see with this when user will loose their device, whole account gets nuked and becomes inaccessible.
trust me they will loose the device, 9/10 times they will change phone in span of few months.
also users are stupid, they wont setup a recovery option on account creation, they just want to use the website.
so you have to force them to link like google account or Facebook or any major social media account on account creation as recovery options, there is no way around this.
Users just want to click a button and be done with it, thats the maximum effort they are willing to do.
This is not a bad idea, it just has flaws that need to be worked out.
You very accurately pin-pointed a weakness of the current prototype. As it is now, losing your device means locking yourself out.
Instead of associating the account with Google, Facebook, etc, the roadmap planned has another approach based on three points.
I think these 3 aspects, hand in hand, would make it both convenient and safe, while leaving enough freedom to the user. At least, I hope so, even if it's a couple more clicks to secure an account.
It's true that Passwordless.ID is not yet ready for production. Nevertheless, I'm glad to have this sort of discussion as it is better to receive due critique than no interest at all.
most people do not know how to use a QR-Code, my mother doesn't even know what QR-Code is, she uses facebook, amazon, and google services, its a nice feature to have for younger generation, but no one will use it that is older.
your mentioning per email, its a nice feature to have, means you have to type your email, people are lazy, they wont do it unless its something they really want or need, if they have to type email out they already left your website and you lost a customer.
instead of typing your email you can click 2 buttons to link your google account, and it dose same thing.
so we are are right back to what I said about social media accounts.
I know your smart, but I work with older people, and they really have no clue how to use this stuff other then clicking buttons and maybe writing their email down
even thats difficult becouse most of the time they don't even remember what their email is unless they look it up on their device.
why do you think there is social account login on mostly every website?
So yeah, you need to make this as dumb as possible if you want to reach wide audience.
Indeed, the simpler it is, the better. I've also considered a "link google/microsoft/... account" kind of button, it was just lower on the priority list than a plain link sent per e-mail which covers a broader use case for both recovery and registering another device. The QR code is just a bonus sine it is just another representation of the URL.
Also, users are diverse. Even if we want to cater for older people too, there is no reason to neglect the younger generation. After all, scanning a QR code to add a device is simply convinient.
I hope that all the "clueless people" you talk about have a little sense for security though. Otherwise, they will fall victim to phishing, social engineering, password reuse or other account tokeover attempts. Especially if they are lazy and did not register a phone number, they are vulnerable. So isn't it a good thing for "not technology affine" people to offer them secure accounts by default, at the price of having them add another device or register a phone number?
Lastly, regarding convinience, isn't it annoying to have to redifine your profile on every website you visit, and put in your address again if you buy something on a new website or travel somewhere. With passwordless.ID, you'd have a central place and websites just ask for your data instead of filling forms each time again.
So, yes for convinience, and yes for making it as simple as possible. I'm actually putting some efforts and thoughts into that, without sacrifying security either. The "link it to other accounts" also makes sense in the future, even if it's just to grab the email and some profile information.
Well, the first step in multi-device is now available ;)
Dunno if it's very intuitive to find though.
i wouldn't worry about the looks, but functionality, looks can always be changed in shortest amount of time.
Its a good start for 1st pass :P
I hate logging with social network. Like, if you use password manager, you can't just log in the account, without logging in Google. On one-time devices, Google auth is really overloaded, you need to verify yourself in the phone plus later delete email about your login. I want just enter password from my password manager (and 2fa of course), and get access to all my accounts without any additional login-hell.
It's good to have such a login method, but you shouldn't force it. Someone like simple one click auth, other hate it because it's not one click in many other scenarios.
I'm not really understanding the issue you have. Logging in (if you are not already) is just click - fingerprint (or other) - click. To me it looks comfortable, and it's 2FA in a single step. I also don't know what you mean with "one time device", I guess it's like using a device as a guest. In that case, there is the "Sign in with roaming device" option, which connects to your phone through NFC or Bluetooth to let you authenticate. This sounds way more convenient to me than saying "Sorry grandma, could you please let me install my password manager because without it I'm busted and let me type my super complex master password". But, everyone their preference. No worries. ...or perhaps you missed that option, there is certainly some UI improvements I could do on my side to make it more intuitive.
I didn't mean using mobiles here at all. I can't imagine a situation when you need to log in from someone else's phone. Here, I talked about computers, and with them, I always use just anonymous mode in browser, log in into my password manager, and have access to all my accounts everywhere.
Password manager has web version... And I said it's good to have log in with social network, but not to force it. Same as you shouldn't force login only by password and email.
When I log out and retry to login, I select my avatar ... then it throws an error.
I like the technique btw :-)
Oh, that's interesting. Do you mind sharing the device / OS / browser you are using and the error it throws? That would be very helpful. I noticed Firefox is not yet properly supported yet for example.
OS : 13.1 (22C65) (Ventura 13.1)
Browser: Brave
Macbook Pro M1
The biggest issue is that I have no Mac device right now at hand, so I cannot even investigte properly. I just verified that it works with Brave on Windows, so I wonder if the issue might be with the Brave / Mac OS combo or some other more generic Mac OS issue. You haven't told me the exact error either. The only thing I could do is open a ticket for now and investigate once I get my hands on a Mac.
Perhaps you should log errors when they are shows like this, so you never have to ask ;-)
That helps a lot. Apparently the combo MacOS/Brave does not behave strictly as the specification dictates. That's good to know. I'll add an exception for them until they fix their stuff I guess.
Regarding the logs, I agree. It's on the TODO list. Since it is not a single server but code distributed at many nodes running on-demand, it is slightly more tricky than just dumping it to a file. It's on the way though.
Thanks a lot for the report
This is absolutely great and a very good beginning to solving login problems.
I'm going to read over this again later, but this is great work and very creative way to solve this problem. Thanks for sharing. One of the best ideas I've stumbled upon in a long time.
It's still slightly experimental, so if you encounter any issue or something is unclear in the tutorial, please tell me! Thanks.
sounds interesting, i'll try it soon.
Nice intro to your service. I'm still waiting for passwordless user authentication for my firebase project.
Webauthn could be such a game changer..
Hi, I know it's a bit late and I'm not very familiar with Firebase authentication mechanisms, but can you use either a generic OpenID provider? In that case, pointing to api.passwordless.id should be enough, with your domain name as
client_id
and theimplicit
flow (the auth code flow is still on the TODO list).Nice article dude!
Great article. Loved the demo.
Thanks, glad you liked it
This is dope
This also has own minuses, for example when you use PM (aka password manager), login with such method is just long. You can't do one click in PM, and all data will be filled in one second. This is especially awful if I'm not on the main device, where I am logged in everywhere, but I want to fast login, find some info, and delete all login credentials.
Such authentication, that was proposed here, must give an alternative - TOTP (aka 2FA). It is really fast to fill with password manager, and this is what I want to use on every website.