Remix.run Logo
Things I learnt about passkeys when building passkeybot(enzom.dev)
88 points by emadda 7 hours ago | 58 comments
godelski 2 hours ago | parent | next [-]

A few weeks ago I had a bug with bitwarden where every passkey wanted to load from the macbook instead of bitwarden. I ended up being locked out of a few accounts that didn't have OTPs as a fallback. Mostly inconsequential stuff like Twitter.

I love passkeys, but they're still kinda hard to use. There's several sites that wont let you enroll multiple ones and it's easy for systems to step on each other like the aforementioned experience.

The problem is fallback. All my banking apps have SMS OTP fallbacks and that's no better than having only SMS OTP. If you're building these systems make sure you have good fallbacks. What matters in design is not so much how well it works when things go right but how well it works when things go wrong. With security you really cannot ignore edge cases

awesome_dude an hour ago | parent [-]

I read this thinking "The BEST security is the WORST usability, and vice versa"

The easier it is to do things, like use another channel, the harder it is to keep secure.

The easier it is to keep secure, the harder it is to use.

jeroenhd an hour ago | parent [-]

I don't think this is a security vs usability thing. A lot of UIs are intentionally confusing.

Apple wants you to use iCloud passkeys, Microsoft wants you to use Microsoft Account passkeys, Google wants you to use Google passkeys. Even if you have a dedicated USB device plugged in, browsers keep defaulting to the cloud accounts.

Bitwarden's approach is to simply hijack the passkey request before the browser can respond and throw itself front and center. It's a terrible hack but it works on every browser at the very least.

If these companies cared about their users more than they cared about throwing up walled gardens, they wouldn't put a USB key behind "Choose another method" -> "Dedicated device" -> "Security key" -> "Confirm" while offering one-click login with their cloud account. And they would offer a proper API for third party applications to integrate into the native passkey storage.

BoppreH 5 hours ago | parent | prev | next [-]

Love these "lessons learned" posts, keep the coming!

My only feedback is about the Quickstart of passkeybot, "feed this example into a good LLM with these instructions". I undeerstand the idea, but I was a bit shocked that the first time I see these sort of instructions is for an auth framework.

quantummagic an hour ago | parent | prev | next [-]

The scariest thing is the casual mention of the Digital Credentials API[1]. Forget passkeys, when you need government issued credentials to surf the net, the good times are over.

[1] https://developer.chrome.com/blog/digital-credentials-api-sh...

jeroenhd an hour ago | parent [-]

There are plenty of websites and services already where you need to prove your identity to use them. The digital credentials API is an attempt to standardise that which is already legally required in the US, the UK, Australia, and the EU, except without having to upload a picture of your ID to a shady third party website.

ChrisMarshallNY 5 hours ago | parent | prev | next [-]

Thanks for that!

I am in the middle of writing a passkey-driven server dashboard app (native SwiftUI app, with a small server component).

In the future, I would like to use passkeys as much as possible, but they do present a bit more friction to users than Sign in with Apple. When I was initially learning them I wrote this up: https://littlegreenviper.com/series/passkeys/

smallnix 4 hours ago | parent | prev | next [-]

In oauth2: when I /1 associate a random uuidv4 for each new flow with my user (server side), /2 stick that uuid into the state parameter, and then /3 look up my user with this on callback-endpoint execution. Isn't PKCE in that case redundant?

gethly 2 hours ago | parent | next [-]

Oauth's PKCE verifies the continuity of the flow as it is essentially a saga(multi-step process). For example you can initiate oauth access grant request multiple times with the same data, but PKCE ensures that each of those initiations can be individually identified. Do not confuse PKCE with state field, which is for XSS and has no obfuscation.

Just to be clear, the PKCE secret can be the same for each initiation, but in the end its goal is to ensure that the first request matches with the last one. And yes, there is "plain" PKCE method but that is just for testing. SHA256 is the default one used to obfuscate the secret.

SahAssar 3 hours ago | parent | prev | next [-]

I think one point of PKCE is that the oauth token is never sent to the client (it is exchanged on the backchannel), so it theoretically is more protected.

Of course if you trust the client (no bad browser extensions, updated browser) and have good TLS settings and no MITM risk and make sure the your IDs are single-use then it seems like that should be fine.

esseph 3 hours ago | parent | prev [-]

If you can, switch to uuid v7 if you're indexing by that id. Performance improvement while still not being sequential IDs.

SahAssar 3 hours ago | parent [-]

For this sort of use-case v4 might be better. It has more randomness and you will probably delete the old ids as soon as they are used anyway, so the indexed space will probably be small.

coldpie 2 hours ago | parent | prev | next [-]

The passkey spec authors think websites should be able to ban clients which allow users to manage their own data[1,2]. It makes me really hesitant to adopt passkeys if my client could get banned because it's open source and lets me control my client how I want to. It appears to be more useful for vendor lock-in than anything else[3]. A shame, since it could've been a cool tech if they had built it to be resilient to this kind of abuse, but it's clear they think vendor lock-in is actually a core feature of the protocol.

[1] Spec author quote: "To be very honest here, you risk having KeePassXC blocked by relying parties." https://github.com/keepassxreboot/keepassxc/issues/10407#iss...

[2] https://www.smokingonabike.com/2025/01/04/passkey-marketing-...

[3] https://fy.blackhats.net.au/blog/2024-04-26-passkeys-a-shatt...

jeroenhd an hour ago | parent | next [-]

The point of passkeys is that they're unexportable. Software implementations like Bitwarden/KeepassXC/etc. making them exportable go right against the point of the protocols.

I personally think the ability to export+import passkeys is a good thing from a backup point of view, but he's not wrong in suggesting that companies actually using the high security features of passkeys will eventually block software implementations like these.

This isn't about vendor lock-in. Nobody is asking for KeepassXC to remove passkey support. This is about security software implementing an API and not fulfilling the expectations that come with such an implementation. To quote the comment you linked:

> That's fine. Let determined people do that, but don't make it easy for a user to be tricked into handing over all of their credentials in clear text.

coldpie an hour ago | parent | next [-]

It's fine for them to make suggestions for projects to improve their software. The problem is threatening clients with being banned because they don't agree with those suggestions. If a website is able to ban me because of the passkey client I'm using, then I'm just not going to use passkeys. It's too unreliable.

> personally think the ability to export+import passkeys is a good thing from a backup point of view

It's not a "good thing," it's absolutely critical. If I can't back up my credentials in a location that I trust, then it's not an acceptable login method. What happens if my PC goes down and I couldn't export my data? I just can't log in anywhere? KeePassXC lets me do that, but the spec authors think it's appropriate to ban me for using it because it lets me manage my own data. That's bonkers.

jeroenhd an hour ago | parent [-]

I don't see where he is threatening anybody? He's just stating the obvious. If you promise to store a key in a non-exportable format and then create a big export button, websites won't trust your software.

> What happens if my PC goes down and I couldn't export my data? I just can't log in anywhere?

Then you follow the procedure you would follow for when you'd forget your password. Probably a password reset through email, maybe calling customer support. Or if you have it set up, you could use the passkey set up on your phone or Yubikey or whatever to log in and create a new password on your new PC.

Passkeys aren't passwords, that's the whole point. It's modelled after the "something you have" factor, not "something you know". If you're finding workarounds to violate the security design, you're not gaining any advantage by using passkeys. Just use a password if you want to use a password.

coldpie 42 minutes ago | parent [-]

> If you're finding workarounds to violate the security design, you're not gaining any advantage by using passkeys.

The trouble is, if websites are allowed/encouraged to ban clients, then the advantages you're talking about come with the downside of hard-tying yourself to one of 3 US-based Big Tech companies, because those will be the only ones who will ship clients declared "secure." That's not a trade-off I'm willing to make for something as critical as my service logins. You can already see this happening, almost every article talking about passkeys assumes you're logging in with an Apple, Google, or Microsoft device.

> Then you follow the procedure you would follow for when you'd forget your password. Probably a password reset through email, maybe calling customer support.

This is a downgrade from passwords (and exportable passkeys), where I can just restore it from a backup.

> Just use a password if you want to use a password.

Yeah, that's what I plan to keep doing, unfortunately. What I'm worried about is a password-less future where that's no longer an option and we all have to submit to using one of Android, iOS, or Windows to log in to everything because those are the only clients that can be trusted(TM) to handle the user's data as the big tech companies and governments desire it to be handled. This is a dark future.

Magnusmaster 30 minutes ago | parent [-]

You already need to submit to iOS or stock Android for a myriad of banking or government apps that use remote attestation to verify that you are running "untampered" software.

Remote attestation is evil.

coldpie 16 minutes ago | parent [-]

FWIW this has not been my experience in the US, I've always been able to use websites for these things. I use my phone for almost nothing important since I don't trust it. But yes, I fear we are heading in that direction too.

stubish 20 minutes ago | parent | prev | next [-]

But the natural result is vendor lock in. To stop exports of keys, sites will need a whitelist and secure method to verify the hardware or software implementation has not been tampered with. If an implementation is banned, the obvious solution is to allow it to pretend to be a non-banned implementation. Or admin level processes smuggling keys out of approved implementations. I don't think anyone wants an arms race, thus the vague threats to remove features that users are demanding before they will consider buying into the ecosystem.

nabogh an hour ago | parent | prev [-]

I agree with you but the whole thing makes me uncomfortable. We're definitely making it easier for these security conscious companies to do vendor lock in if we encourage passkey use.

yawaramin 2 hours ago | parent | prev [-]

Apple doesn't do attestation, so effectively this feature is dead in the water.

lelandbatey an hour ago | parent [-]

Per the article, Apple does do attestation. By default attestation is off unless you have enterprise management turned on.

But the existence of attestation means Apple could at any time in the future make attestation on by default and suddenly our devices control our secrets more than we do.

xg15 4 hours ago | parent | prev | next [-]

> generateKey is a JS API that allows you to create new key pairs, where the private key cannot be extracted similar to passkeys.

Is that "cannot be extracted" from JS only, or is this an actual device-locked, TPM/SEP-bound key like passkeys?

If it is, it seems kind of like the buried lede to me that there is a browser API that lets any website built its own completely unstandardized quasi-passkey system and lock the key to the current device.

ajross 4 hours ago | parent [-]

Yes, where practical. Though recognize that by their very nature web apps aren't part of the trust network. The browser and security stack can make a key for them to use, but it's not possible to be sure that the user of that key is not subject to attack at the backend (or even front end, really the best you can do there is XSS protection, which is hardly at the standard of "crytographically secure").

And likewise you as the app vendor can know the key was generated, and that it works, but you can't[1] know that it's actually locked to a device or that it's non-exportable. You could be running in a virtualized environment that logged everything.

Basically it's not really that useful. Which is sort of true for security hardware in general. It's great for the stuff the device vendors have wired up (which amounts to "secured boot", "identifying specific known devices" and "validating human user biometrics on a secured device"), but not really extensible in the way you'd want it to be.

[1] Within the bounds of this particular API, anyway. There may be some form of vendor signing you can use to e.g. verify that it was done on iOS or ChromeOS or some other fully-secured platform. I honestly don't know.

machinationu 3 hours ago | parent [-]

it's possible with CPU secure attestation, but it's not something you will encounter on regular personal computers.

the capability is there, but it would he massively inconvenient, since it requires a lot of lockdown

might be the next generation of anti-cheats though

jeroenhd an hour ago | parent [-]

Apple is already shipping remote attestation in Safari in the form of Private Access Tokens (https://developer.apple.com/news/?id=huqjyh7k), though Cloudflare's trial for that has ended. Safari authenticates and attests itself against Apple, who hands out tokens to your browser, which in turn get used to bypass CAPTCHAs and other anti spam filters.

There's no direct remote attestation implementation for passkeys yet, but remote attestation for web browsers has been around for a few years now.

loloquwowndueo 5 hours ago | parent | prev | next [-]

How to add passkeybot support to your site, according to their official guide:

start

(1) Copy / paste example_http_server into your LLM of choice (use a paid/good model). (2) Prompt: Implement the HTTP handlers here for my project,..

Um, no? How about you give me real instructions on how to do it? I’m not going to delegate a security-critical task to an LLM. And since I need to review it carefully myself anyway, I might as well write it all by hand, right? Like, the whole premise is I just need to implement a couple of webhooks.

gear54rus 5 hours ago | parent [-]

It's absolutely hilarious that someone would think that this passes for API docs nowdays. Still it's good to know what to avoid on the very first glance.

jiggawatts 4 hours ago | parent | next [-]

It's also a bit of a "bootstrapping" issue. How does anyone expect the AIs to learn to do things correctly if the instructions are not published for them to pick up during training?

This is like those "contact your system admin" error messages. I am the system admin!

the_mitsuhiko 4 hours ago | parent | prev [-]

I think it's good. Quite frankly, it's the better experience to be given the right prompts to onboard into something than having to guess that the inputs are the right for the LLM.

stephenr an hour ago | parent [-]

If someone is writing authentication code and they think it's smart to outsource that to spicy autocomplete, the only "prompt" they need is:

"Hey chat bot friendo, where's the nearest hand-written 'help wanted' sign in the door of a coffee shop? I need a new career path"

boombapoom 5 hours ago | parent | prev | next [-]

i wish passkeys could replace passwords, not suppliment them

spockz 4 hours ago | parent | next [-]

Why? Passwords can be remembered and entered on other devices for recovery. The plethora of passkeys out there cannot.

A bit the same why although I love the keychain in macOS, it also makes me uncomfortable. Lose your phone and laptop in a theft or fire and you are locked out from your Apple account. Goodbye online presence.

AlotOfReading 4 hours ago | parent | next [-]

The "standard" answer is that you should either use synced passkeys, or enroll multiple passkeys with the provider. The problem is that some providers (e.g. Paypal, some banks) only support one passkey, and synced passkeys aren't supposed to be trusted for attestation (unless they're synced by Apple/Google/Microsoft).

epaulson 3 hours ago | parent [-]

And every couple of days we see a post or a tweet about "Google/Apple/Microsoft just nuked my account with no notice and no recourse" so trusting them to sync passkeys rightfully makes some people nervous.

stavros 3 hours ago | parent [-]

Whereas we never see a horror story involving passwords.

AlotOfReading 2 hours ago | parent [-]

There are two problems with passwords. Reuse, and site breaches. The solution to the former is the same as passkeys: credential managers. Passkeys genuinely solve the second, in exchange for a vastly less comprehensible system (see all the uncertainty people have even here on HN) that doesn't support many of the ways people want to use authentication tokens.

stavros 2 hours ago | parent [-]

No, the biggest issue with passwords is phishing. You can't phish a passkey.

AlotOfReading 2 hours ago | parent [-]

Are there any credential managers that don't validate the domain with passwords? Sure, there are issues with PSL subdomain matching, but at the end of the day it's good enough in the real world. All the other stuff (MITM, malicious site, etc) falls under the other case I already mentioned.

stavros 2 hours ago | parent [-]

There's a big difference between "generally doesn't get phished" and "it's impossible to be phished".

AlotOfReading 2 hours ago | parent [-]

It's security, so we're not discussing impossibility. You can still phish a passkey, we're just hoping the cryptography is good enough that it remains astronomically unlikely to succeed. Since we're all reasonable people, that chance is low enough that we're fine accepting it. What I'm saying is that the chance with passwords is still low enough that I'm fine accepting, even though it's much higher than the cryptographic security of passkeys. We're simply disagreeing about where we draw the line of "good enough".

stavros an hour ago | parent [-]

How can you phish a passkey?

AlotOfReading an hour ago | parent [-]

You crack the private key and forge the challenge? Maybe the other IDs sent alongside it are hard to get for some reason, but the security of passkeys comes down to the cryptography. Cryptography can always be broken, but a good cryptosystem makes the probability low enough that any reasonable person considers it good enough.

wkat4242 4 hours ago | parent | prev | next [-]

That's exactly the issue I have with passkeys. All that lockin to big tech. I tried bit warden but most sites with passkeys didn't work with it (like Amazon and PayPal). And on android it only wants to use the Google version (I don't use a Google account on my phone so that's not possible).

izacus 3 hours ago | parent | next [-]

None of what you wrote is true though, is it?

Amazon, PayPal work just fine on my 3rd party 1Password extension. And it works just fine on Android as a default passkey provider as well.

jeroenhd an hour ago | parent | prev [-]

Bitwarden works just fine for Amazon. Works on my phone too. Even when supplying passkeys over QR code+Bluetooth to another computer, Bitwarden's Android integration works flawlessly.

I do believe you need Android 14 for that, though, so if your phone has been abandoned by its manufacturer/your ROM of choice, it'll break.

If Bitwarden is bugged out on your computer/phone for whatever reason, there are also alternatives like 1Password.

ianburrell 3 hours ago | parent | prev [-]

Passkeys work well with password manager. The password manager also stores the long random password to get in without passkey. The advantage is that passkeys are immune to phishing. Sites also turn off 2FA for passkeys which reduces the hassle.

coldpie 2 hours ago | parent [-]

Unless the spec authors declare your password manager to be on the official naughty list[1] and relying-parties choose to block clients on that list.

[1] https://passkeys.dev/docs/reference/known-issues/

jeroenhd an hour ago | parent | next [-]

I think it's more than fair to document that some implementations lie about their intentional violation of the spec, even if that violation is done to make the login process smoother.

Still, I've never seen a website try to block Bitwarden's passkey management (though I've had plenty of issues because of its partial implementation of the API, especially in early versions) despite its spec violations.

For some of the implementations, user verification is a massive pain (as browser extensions often only have long and complicated passwords to authenticate) but for KeepassXC a quick and simple fingerprint/facial scan is an option, as it already offers integration into the native OS biometrics anyway.

coldpie an hour ago | parent [-]

> Still, I've never seen a website try to block Bitwarden's passkey management

Ideally it shouldn't be possible, or at least it should clearly be an ugly hack for a website to be doing something like this. Instead the spec authors explicitly endorse blocking clients that they feel are non-compliant. I'm not going to use a login spec that encourages websites to ban me because of the software I choose to use.

> for KeepassXC a quick and simple fingerprint/facial scan is an option, as it already offers integration into the native OS biometrics anyway.

Man don't get me started on the passkey environment's bizarre obsession with biometrics. My desktop computer doesn't have a fingerprint reader or a camera, and if my OS (Arch Linux) supports that junk I've certainly got no interest in doing the work to set it up just so I can log in to a website.

yawaramin an hour ago | parent | prev [-]

As I said earlier, this is functionally impossible because Apple devices don't offer device attestation data.

coldpie an hour ago | parent [-]

Then I look forward to them removing the anti-feature and no longer maintaining the naughty client list.

AlotOfReading 4 hours ago | parent | prev [-]

And I wish passkeys could cover all the use cases of passwords, yet here we are. Passwords are simple and well understood. Passkeys have all sorts of sharp edges that you won't discover until you're hurt by them.

yawaramin an hour ago | parent [-]

Passwords are also simply phished, and many people have discovered those sharp edges by getting their accounts hacked.

tptacek 5 hours ago | parent | prev | next [-]

Regarding PKCE, the way I remember it is that OAuth2 was deliberately designed to eliminate as much actual cryptography as possible, relying instead on same-origin and TLS security; PKCE is one of the few things that introduces an actual cryptography primitive.

EGreg 3 hours ago | parent | prev | next [-]

The authenticator GUI only shows “sign in to your_domain.com”. It never allows a more general “sign this content for your_domain.com”. E.g. “sign this transaction request to move £50 to Bob's account”.

That is why you should ship a pristine HTML+CSS+JS environment that can use subtle web crypto. YOU show what is being signed. And then the device can sign its hash using the secure enclave.

And you CAN do attestation even on consumer devices, by using the Device or AppAttest framework (I think that’s what it’s called). I did it myself in our app. It does show up 100% of the time but when it does it’s useful.

PS: being the web3 / blockchain geek that I am, I will tell you stuff that triggers anticryptobros on HN.

The Web3 ecosystem already has a standard called EIP712 for signing structured data. If you want to stick to standards, use that!

The secure enclaves all use P-256 (sometimes called R-256) while Bitcoin / Web3 uses K-256 (the Koeblitz curve, they distrust the NIST curve or whatever).

So that means you’re going to have to use Abstract Accounts and the new precompiled smart contracts to verify P256 signatures, which only Binance Smart Chain and a handful of other chains have deployed. Luckily BSC is the most widely used chain by volume and has plenty of money sloshing around so you can build your trustless programs there. If you want to be totally trustless — LET THE SMART CONTRACTS GENERATE THE CHALLENGE TO BE SIGNED BY THE AUTHENTICATOR. Then have it sign the temporary k256 public key (from the keypair) to use, as long as your session is open you can then add use your private key to sign transactions. As usual, do this for small amounts per day, transactions that move larger amounts should still require use of multisig keys etc.)

QGQBGdeZREunxLe 3 hours ago | parent | prev [-]

Computerphile added a passkey video today https://www.youtube.com/watch?v=xYfiOnufBSk