Skip to content

Conversation

@zUnixorn
Copy link
Contributor

@zUnixorn zUnixorn commented Jun 4, 2025

Hello,

This PR implements the endpoints needed for passkey login into the Bitwarden webclient.

I marked this as a draft for now, since I'd like to first get some feedback on this PR before I start pursuing this further or if this is even a wanted feature for Vaultwarden.

Working Features

  • Login with passkey without using it for encryption
  • Login with passkey while using it for encryption
  • Adding a new passkey
  • Listing all registered passkeys
  • Deleting a passkey

Testing this Branch

To test this branch, the fronted needs to be build with this line appended, to allow for passkey registration.

Open TODOs

  • Cleanup the code
    • there is a bunch of duplicate code between _login_password() and _login_webauthn()
    • the webauthn stuff could be moved into its own file / module
    • Resolve the TODOs
  • Figure out how to handle the intermidate webauthn state:
    • Saved in the database (I think that's what's already done in the 2FA webauthn), but this probably would only work for registration, not login
    • Passed around as an encrypted string in the token field (I think this is what bitwarden does with this C# Api)
  • Decide what to do about the unimplemented endpoints that are implemented by upstream (they don't seem to be used currently by the webclient)
  • Figure out why the frontend doesn't work in firefox for passkeys

@BlackDex
Copy link
Collaborator

BlackDex commented Jun 4, 2025

First, thanks for contributing to Vaultwarden!

Second, if adding this, it might also be the best to update webauthn to the latest version. The only issue here is, that this needs some form of migration for the currently configured 2FA objects, which would become invalid. Luckily the webauthn crate has the old struct still available for this reason in their latest version, but it might be complex.

We already have a migration which is done from an even older format we used before. Though, I'm not sure if we would still need to support that as it has been a long long time ago already, and admins should have installed a newer version already for a long time now. (What do you think @dani-garcia ? )

The main reason for this is that there are several fixes and updates to better support passkeys via webauthn in the latest version.

@tessus
Copy link
Contributor

tessus commented Jun 4, 2025

I'm not sure if we would still need to support that

I don't think it is necessary, unless the migration is not a one-go approach, but only happens when an item with one or more 2FA objects is accessed (which would be a serious problem anyway).

Admins can migrate to the latest version of vw with the "old" migration path and then to the most recent vw version. Such migration paths are vey common: wikimedia, database engines, operating systems, ....

The only issue here is, that this needs some form of migration for the currently configured 2FA objects, which would become invalid.

Hasn't Stefan started a branch for upgrading webauthn at one point?

@BlackDex
Copy link
Collaborator

BlackDex commented Jun 4, 2025

I don't think it is necessary, unless the migration is not a one-go approach, but only happens when an item with one or more 2FA objects is accessed (which would be a serious problem anyway).

The current migration check/step is done during startup.
While i think most people should be running a version for which this step is ready done long time ago, it is still something to take into account, and skip items which are that old, and exclude those from the 2fa, and upon detection of such an old, unsupported version, maybe enable email 2fa when that was the only 2fa feature enabled. Simply to prevent no 2fa at all anymore.

That would make it much easier to migrate, and maybe even act upon invalid or unsupported webauthn objects.

Try to convert from old, if that also doesn't work, then disable.

@tessus
Copy link
Contributor

tessus commented Jun 4, 2025

The current migration check/step is done during startup.

In that case, it is very easy.

Rmove the old migration path from the code. Upon startup start transaction. Migrate to new webauthn objects. If it encounters an "old" object that cannot be migrated, roll back transaction and show the following message:

You have to upgrade to vaultwarden 1.34.1 first.

P.S.: In the release notes and maybe even in the .env.template, you can mention that if one was still using vaultawrden < x.y.z, they'd have to do an intermediary upgrade.

@zUnixorn
Copy link
Contributor Author

zUnixorn commented Jun 5, 2025

Just to be sure I understood you both correctly, there would be 3 versions for 2FA Webauthn Registration Structs:

The current migration at the start is from U2FRegistration to WebauthnRegistration.
When updating to webauthn-rs 0.5, the migration at the start should instead migrate from WebauthnRegistration with a Credential to a WebauthnRegistration version with a Passkey.
If it encounters an error because the Database still contains U2FRegistration instances, it should exit with a help message that the tells the user to upgrade to an intermediate version first, correct?

If that is the case, I think this should be done in other PR first, should it?

@BlackDex
Copy link
Collaborator

BlackDex commented Jun 5, 2025

When updating to webauthn-rs 0.5, the migration at the start should instead migrate from WebauthnRegistration with a Credential to a WebauthnRegistration version with a Passkey.
If it encounters an error because the Database still contains U2FRegistration instances, it should exit with a help message that the tells the user to upgrade to an intermediate version first, correct?

Kinda, the only thing I'm not sure about is what to do with the old U2FRegistration objects, which in my opinion shouldn't exists anymore since all clients currently do not support older server versions anymore. Unless users do not use any of these clients and still use older clients of course.

So... Do we want to support migration from U2FRegistration to the new Passkey if they exists if that is possible?
And thus migrate both U2FRegistration and WebauthnRegistration to Passkey, or, skip U2FRegistration migration at all now?

@zUnixorn
Copy link
Contributor Author

zUnixorn commented Jun 5, 2025

Another thing I just noticed is that the webauthn-rs 0.5 crate doesn't seem support resident keys without also forcing attestation. In the 0.3 version there was the option to implement get_require_resident_key() to ensures that a resident key is created on the security key of the user. This wasn't needed for 2FA but since the Bitwarden frontend decides to use Discoverable Credentials (That's why you don't need to supply a username when logging in with a Passkey), "Login with Passkey" can only be implemented with that feature to stay compatible with the Frontend.

The maintainers of webauthn-rs seem to have a strong opinion on residential keys (see here and here). Since there was a way to do this without attestation in the past, maybe this could be copied and done outside of the webauthn crate.

Alternatively I could try if the Frontend cares if registrations request includes attestation, if it doesn't, then the passkey support could still be implemented with the 0.5 release and the resident-key-support feature but would then be limited to attested Passkeys only. And if Bitwarden decides to port that feature to other Platforms this could break as well, if it would even work in the first place.

@zUnixorn
Copy link
Contributor Author

zUnixorn commented Jun 5, 2025

Ok, after looking through the webauthn crate, I found webauthn_rs_core, this should solve this problem as it exposes the internally used webauthn builders, so that could be used for the resident key case. My previous concern shouldn't matter therefore.

@lukasj98
Copy link

When will it be available in testing?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants