pypi / warehouse

The Python Package Index
https://pypi.org
Apache License 2.0
3.58k stars 963 forks source link

Support Passwordless Authentication with Passkeys #13764

Open dstufft opened 1 year ago

dstufft commented 1 year ago

Our current 2FA support is "Something you know" (password) and "something you have" (TOTP or WebAuthN).

There's a newer authentication method called Passkeys. Under the cover's Passkeys can be thought of as basically WebAuthN token, however used with slightly different options that requires the WebAuthN token to require the user to unlock the device. Typically when using Passkeys, you do not need to sign in with a password at all, the Passkey acts as both factors.

The other benefit that Passkeys provide over traditional MFA, is that typically traditional MFA the "something you have" is device bound, so you need to register multiple devices, deal with what happens if your phone gets broken, etc. With Passkeys the credentials are not device bound [^1], and are synced within the same ecosystem (so in the ICloud Keychain, etc) but still protected by the first factor (typically FaceID, Windows Hello, a Fingerprint, or a Pin).

These can be as secure, or more secure than Password + WebAuthN (and are more secure than Password + TOTP), but reduce user friction by eliminating the need to enter a password or switch to a different device, plus reduce the cases where the user is likely to get locked out of their account.

[^1]: The site can request that they be device bound I believe, for extra assurance.

dstufft commented 1 year ago

Note, this issue might be best thought of as also deciding if we want to support them.

The "problem" with Passkeys is their security model is a little "mushy". From the point of view of Warehouse directly, it's basically a single factor but that we know has really good, strong crypto behind it and that, in theory, the device requires positive identification of the user (biometrics, pin, etc). However, since Warehouse isn't involved in this 2FA, it has to trust that the device is doing that.

The risk then becomes that because this is happening on a device or application that the client controls, that it could be implemented poorly or be lying to us and really only be 1FA.

You can get around this using hardware attestation, which is basically using crypto to verify that the hardware is a specific type of hardware (an iOS device, etc). This tends to make people squeamish (for good reasons), and none of the vendors could agree on a format so it's not very easy to do.

So basically, in my understanding:

I could be wrong though! I'm not an expert on PassKeys.

[^1]: One of the things PassKeys are attempting to eliminate is passwords altogether, because users are awful at picking them securely anyways. We do a pretty good job at making sure our passwords are secure, so I'm not really too worried about the quality of passwords on PyPI, but it is something to keep in mind.

dstufft commented 1 year ago

To be clear, this is specifically about using these ideas to eliminate the need to have a password at all.

WebAuthN has "user presence", which strictly asks that a human is present, any human will do. This is typically done through some kind of button press or confirmation pop up.

WebAuthN also has "user verification", which requires that the device verify that the user is the right person. This is typically done through biometrics (fingerprint, facial recognition) or some kind of password-like thing (pin code, geometric pattern).

We currently are using "user presence" (because we're using this just to verify possession of the device, as a second factor to passwords), but we could allow people to turn on user verification, and login just through WebAuthN, trusting that their WebAuthN device has correctly verified that the user is the right person.

I don't feel strongly one way or the other about doing it, but figured it was worth discussing if we wanted to support this kind of passwordless authentication or not.

dstufft commented 1 year ago

Also when I say eliminate the need, the places I've seen this implemented typically still have users select a password, unless they're a niche site that can only accept passwordless.

If we enabled it, it would essentially be indicating in the database whether a particular WebAuthN device has user verification required or not. Then when someone logs in, instead of prompting them for just user/password like we do now, we'd prompt for username/password, or provide a button for passwordless login.

So any individual user would eventually be able to log in with any:

They'd be able to have all active at once, and login with any one of them.

If we did this, possibly at some point in the future we could allow a user to just not have a password at all.

dstufft commented 1 year ago

Note we explicitly set this to discouraged in https://github.com/pypi/warehouse/issues/7270, which makes sense because user verification is only intended for use in password-less schemes.

dstufft commented 1 year ago

More information

webknjaz commented 1 year ago

Looking forward to this! A few months ago, I've set up passkeys on a few major websites that recently started rolling this out more actively. Like GitHub and Microsoft. It's quite a delightful UX. Would love to see this enabled on PyPI.

webknjaz commented 1 year ago

Here's some extra info, by the way: https://docs.github.com/en/authentication/authenticating-with-a-passkey/about-passkeys / https://github.blog/changelog/2023-07-12-passkeys-public-beta/ / https://github.blog/2023-07-12-introducing-passwordless-authentication-on-github-com/.

And there's a dedicated website with the standard details @ https://passkeys.dev. I especially like that the Cross-Device Authentication is built into the protocol.

webknjaz commented 10 months ago

UPD: Bitwarden now supports passkeys, including their Free plan!

Refs: https://www.androidpolice.com/bitwarden-2fa-free-passkey/ / https://www.zdnet.com/article/bitwarden-rolls-out-passkeys-management-to-all-users-including-free-accounts/.

I tested this today, and it works very well on GitHub (although, I'm not on the free plan, but the info on their website confirms what those articles suggest).

I also learned that https://passkeys.directory is a thing, thought I'd share…

webknjaz commented 10 months ago

I was able to add a Bitwarden-generated passkey as a 2FA on TestPyPI. However, on a login attempt, Firefox still presented me with its built-in pop-up, having presented a Bitwarden extension's pop-up window that shortly disappeared, being unable to intercept the Webauthn API call, I suppose.

webknjaz commented 10 months ago

Ah, I think it was a glitch on my side. I take it back. Bitwarden-stored passkeys do work with PyPI on the login page.