Crate webauthn_rs

source ·
Expand description

§Webauthn-rs - Webauthn for Rust Server Applications

Webauthn is a standard allowing communication between servers, browsers and authenticators to allow strong, passwordless, cryptographic authentication to be performed. Webauthn is able to operate with many authenticator types, such as U2F, TouchID, Windows Hello and many more.

This library aims to provide a secure Webauthn implementation that you can plug into your application, so that you can provide Webauthn to your users.

There are a number of focused use cases that this library provides, which are described in the WebauthnBuilder and Webauthn struct.

§Getting started

In the simplest case where you just want to replace passwords with strong self contained multifactor authentication, you should use our passkey flow.

Remember, no other authentication factors are needed. A passkey combines inbuilt user verification (pin, biometrics, etc) with a hardware cryptographic authenticator.

use webauthn_rs::prelude::*;

let rp_id = "example.com";
let rp_origin = Url::parse("https://idm.example.com")
    .expect("Invalid URL");
let mut builder = WebauthnBuilder::new(rp_id, &rp_origin)
    .expect("Invalid configuration");
let webauthn = builder.build()
    .expect("Invalid configuration");

// Initiate a basic registration flow to enroll a cryptographic authenticator
let (ccr, skr) = webauthn
    .start_passkey_registration(
        Uuid::new_v4(),
        "claire",
        "Claire",
        None,
    )
    .expect("Failed to start registration.");

After this point you then need to use finish_passkey_registration, followed by start_passkey_authentication and finish_passkey_authentication

§Tutorial

Tutorials and examples on how to use this library in your website project is on the project github https://github.com/kanidm/webauthn-rs/tree/master/tutorial

§What is a “Passkey”?

Like all good things - “it depends”. Mostly it depends who you ask, and at what time they adopted the terminology. There are at least four definitions that we are aware of. A passkey is:

  • any possible webauthn authenticator - security key, tpm, touch id, etc
  • a platform authenticator - built into a device such as touch id, tpm, etc. This excludes security keys.
  • a synchronised credential - backed by a cloud keychain like Apple iCloud or Bitwarden.
  • a resident key (RK) - a stored, discoverable credential allowing usernameless flows

Each of these definitions have different pros and cons, and different usability implications. For example, passkeys as resident keys means you can accidentally brick many ctap2.0 devices by exhausting their storage (forcing them to require a full reset, wiping all credentials). Passkeys as platform authenticators means only certain laptops and phones can use them. Passkeys as synced credentials means only certain devices with specific browser combinations or extensions can use passkeys.

In this library we chose to define passkeys as “any possible authenticator”. This definition aligns with the W3C WebAuthn Working Group’s mission of enabling strong authentication without compromising end-user experience, regardless of their authenticator’s modality.

We may look to enable (but not require) usernameless flows in the future for on devices which opportunistically create resident keys (such as Apple’s iCloud Keychain). However, the platform and browser user experience is not good enough to justify enabling these flows at present.

§Features

This library supports some optional features that you may wish to use. These are all disabled by default as they have risks associated that you need to be aware of as an authentication provider.

§Allow Serialising Registration and Authentication State

During a webauthn registration or authentication ceremony, a random challenge is produced and provided to the client. The full content of what is needed for the server to validate this challenge is stored in the associated registration or authentication state types. This value MUST be persisted on the server. If you store this in a cookie or some other form of client side stored value, the client can replay a previous authentication state and signature without possession of, or interaction with the authenticator, bypassing pretty much all of the security guarantees of webauthn. Because of this risk by default these states are not allowed to be serialised which prevents them from accidentally being placed into a cookie.

However there are some safe cases of serialising these values. This includes serialising to a database, or using a cookie “memory store” where the client side cookie is a key into a server-side map or similar. Any of these prevent the replay attack threat.

An alternate but “less good” method to mitigate replay attacks is to associate a very short expiry window to the cookie if you need full client side state, but this may still allow some forms of real time replay attacks to occur. We do not recommend this.

Enabling the feature danger-allow-state-serialisation allows you to re-enable serialisation of these types, provided you accept and understand the handling risks associated.

§Credential Internals and Type Changes

By default the type wrappers around the keys are opaque. However in some cases you may wish to migrate a key between types (security key to passkey, attested_passkey to passkey) for example. Alternately, you may wish to access the internals of a credential to implement an alternate serialisation or storage mechanism. In these cases you can access the underlying Credential type via Into and From by enabling the feature danger-credential-internals. The Credential type is exposed via the prelude when this feature is enabled.

However, you should be aware that manipulating the internals of a Credential may affect the usage any security properties of that Credential in certain cases. You should be careful when enabling this feature that you do not change internal Credential values without understanding the implications.

§User-Presence only SecurityKeys

By default, SecurityKeys will opportunistically enforce User Verification (Such as a PIN or Biometric). This prevent UV bypass attacks and allows upgrade of the SecurityKey to a Passkey.

Enabling the feature danger-user-presence-only-security-keys changes these keys to prevent User Verification if possible. However, newer keys will confusingly force a User Verification on registration, but will then not prompt for this during usage. Some user surveys have shown this to confuse users to why the UV is not requested, and it can lower trust in these tokens when they are elevated to be self-contained MFA (passkey) as the user believes these UV prompts to be unreliable and not verified correctly - in other words it trains users to believe that these prompts do nothing and have no effect. In these cases you MUST communicate to the user that the UV may occur on registration and then will not occur again, and that is by design.

If in doubt, do not enable this feature.

§‘Google Passkey stored in Google Password Manager’ Specific Workarounds

Android (with GMS Core) has a number of issues in the dialogs they present to users for authenticator selection. Instead of allowing the user to choose what kind of passkey they want to use and create (security key, device screen unlock or ‘Google Passkey stored in Google Password Manager’), Android expects every website to implement their own selection UI’s ahead of time so that the RP sends the specific options to trigger each of these flows. This adds complexity to RP implementations and a large surface area for mistakes, confusion and inconsistent workflows.

By default for maximum compatibility and the most accessible user experience this library sends the options to trigger security keys and the device screen unlock as choices. As RPs must provide methods to allow users to enroll multiple independent devices, we consider that this is a reasonable trade since we allow the widest possible sets of authenticators and Android devices (including devices without GMS Core) to operate.

To enable the registration call that triggers the ‘Google Passkey stored in Google Password Manager’ key flow, you can enable the feature workaround-google-passkey-specific-issues. This flow can only be used on Android devices with GMS Core, and you must have a way to detect this ahead of time. This flow must NEVER be triggered on any other class of device or browser.

§Conditional UI / Username Autocompletion

Some passkey devices will create a resident key opportunistically during registration. These keys in some cases allow the device to autocomplete the username and authenticate in a single step.

Not all devices support this, nor do all browsers. As a result you must always support the full passkey flow, and conditional-ui is only opportunistic in itself.

User testing has shown that these conditional UI flows in most browsers are hard to activate and may be confusing to users, as they attempt to force users to use caBLE/hybrid. We don’t recommend conditional UI as a result.

If you still wish to experiment with conditional UI, then enabling the feature conditional-ui will expose the needed methods to create conditional-ui mediated challenges and expose the functions to extract the users uuid from the authentication request.

Modules§

Structs§

  • An instance of a Webauthn site. This is the main point of interaction for registering and authenticating credentials for users. Depending on your needs, you’ll want to allow users to register and authenticate with different kinds of authenticators.
  • A constructor for a new Webauthn instance. This accepts and configures a number of site-wide properties that apply to all webauthn operations of this service.

Constants§