mirror of
https://github.com/dani-garcia/vaultwarden.git
synced 2025-08-04 21:14:37 -04:00
Use securitykey methods
This commit is contained in:
parent
a83ac40741
commit
a90f24f703
@ -122,7 +122,8 @@ yubico = { package = "yubico_ng", version = "0.13.0", features = ["online-tokio"
|
||||
# WebAuthn libraries
|
||||
# danger-allow-state-serialisation is needed to save the state in the db
|
||||
# danger-credential-internals is needed to support U2F to Webauthn migration
|
||||
webauthn-rs = { version = "0.5.2", features = ["danger-allow-state-serialisation", "danger-credential-internals"] }
|
||||
# danger-user-presence-only-security-keys is needed to disable UV
|
||||
webauthn-rs = { version = "0.5.2", features = ["danger-allow-state-serialisation", "danger-credential-internals", "danger-user-presence-only-security-keys"] }
|
||||
webauthn-rs-proto = "0.5.2"
|
||||
|
||||
# Handling of URL's for WebAuthn and favicons
|
||||
|
@ -20,12 +20,12 @@ use std::sync::{Arc, LazyLock};
|
||||
use std::time::Duration;
|
||||
use url::Url;
|
||||
use uuid::Uuid;
|
||||
use webauthn_rs::prelude::{Base64UrlSafeData, Passkey, PasskeyAuthentication, PasskeyRegistration};
|
||||
use webauthn_rs::prelude::{Base64UrlSafeData, SecurityKey, SecurityKeyAuthentication, SecurityKeyRegistration};
|
||||
use webauthn_rs::{Webauthn, WebauthnBuilder};
|
||||
use webauthn_rs_proto::{
|
||||
AuthenticationExtensionsClientOutputs, AuthenticatorAssertionResponseRaw, AuthenticatorAttestationResponseRaw,
|
||||
PublicKeyCredential, RegisterPublicKeyCredential, RegistrationExtensionsClientOutputs,
|
||||
RequestAuthenticationExtensions, UserVerificationPolicy,
|
||||
RequestAuthenticationExtensions,
|
||||
};
|
||||
|
||||
pub static WEBAUTHN_2FA_CONFIG: LazyLock<Arc<Webauthn>> = LazyLock::new(|| {
|
||||
@ -37,7 +37,8 @@ pub static WEBAUTHN_2FA_CONFIG: LazyLock<Arc<Webauthn>> = LazyLock::new(|| {
|
||||
let webauthn = WebauthnBuilder::new(&rp_id, &rp_origin)
|
||||
.expect("Creating WebauthnBuilder failed")
|
||||
.rp_name(&domain)
|
||||
.timeout(Duration::from_millis(60000));
|
||||
.timeout(Duration::from_millis(60000))
|
||||
.danger_set_user_presence_only_security_keys(true);
|
||||
|
||||
Arc::new(webauthn.build().expect("Building Webauthn failed"))
|
||||
});
|
||||
@ -76,7 +77,7 @@ pub struct WebauthnRegistration {
|
||||
pub name: String,
|
||||
pub migrated: bool,
|
||||
|
||||
pub credential: Passkey,
|
||||
pub credential: SecurityKey,
|
||||
}
|
||||
|
||||
impl WebauthnRegistration {
|
||||
@ -129,23 +130,15 @@ async fn generate_webauthn_challenge(
|
||||
.map(|r| r.credential.cred_id().to_owned()) // We return the credentialIds to the clients to avoid double registering
|
||||
.collect();
|
||||
|
||||
let (mut challenge, state) = webauthn.start_passkey_registration(
|
||||
let (challenge, state) = webauthn.start_securitykey_registration(
|
||||
Uuid::from_str(&user.uuid).expect("Failed to parse UUID"), // Should never fail
|
||||
&user.email,
|
||||
&user.name,
|
||||
Some(registrations),
|
||||
None,
|
||||
None,
|
||||
)?;
|
||||
|
||||
// this is done since `start_passkey_registration()` always sets this to `Required` which shouldn't be needed for 2FA
|
||||
challenge.public_key.extensions = None;
|
||||
if let Some(asc) = challenge.public_key.authenticator_selection.as_mut() {
|
||||
asc.user_verification = UserVerificationPolicy::Discouraged_DO_NOT_USE;
|
||||
}
|
||||
|
||||
let mut state = serde_json::to_value(&state)?;
|
||||
state["rs"]["policy"] = Value::String("discouraged".to_string());
|
||||
state["rs"]["extensions"].as_object_mut().unwrap().clear();
|
||||
|
||||
let type_ = TwoFactorType::WebauthnRegisterChallenge;
|
||||
TwoFactor::new(user.uuid.clone(), type_, serde_json::to_string(&state)?).save(&mut conn).await?;
|
||||
|
||||
@ -259,7 +252,7 @@ async fn activate_webauthn(
|
||||
let type_ = TwoFactorType::WebauthnRegisterChallenge as i32;
|
||||
let state = match TwoFactor::find_by_user_and_type(&user.uuid, type_, &mut conn).await {
|
||||
Some(tf) => {
|
||||
let state: PasskeyRegistration = serde_json::from_str(&tf.data)?;
|
||||
let state: SecurityKeyRegistration = serde_json::from_str(&tf.data)?;
|
||||
tf.delete(&mut conn).await?;
|
||||
state
|
||||
}
|
||||
@ -267,7 +260,7 @@ async fn activate_webauthn(
|
||||
};
|
||||
|
||||
// Verify the credentials with the saved state
|
||||
let credential = webauthn.finish_passkey_registration(&data.device_response.into(), &state)?;
|
||||
let credential = webauthn.finish_securitykey_registration(&data.device_response.into(), &state)?;
|
||||
|
||||
let mut registrations: Vec<_> = get_webauthn_registrations(&user.uuid, &mut conn).await?.1;
|
||||
// TODO: Check for repeated ID's
|
||||
@ -385,14 +378,12 @@ pub async fn generate_webauthn_login(
|
||||
}
|
||||
|
||||
// Generate a challenge based on the credentials
|
||||
let (mut response, state) = webauthn.start_passkey_authentication(&creds)?;
|
||||
let (mut response, state) = webauthn.start_securitykey_authentication(&creds)?;
|
||||
|
||||
// Modify to discourage user verification
|
||||
let mut state = serde_json::to_value(&state)?;
|
||||
state["ast"]["policy"] = Value::String("discouraged".to_string());
|
||||
response.public_key.user_verification = UserVerificationPolicy::Discouraged_DO_NOT_USE;
|
||||
|
||||
// Add appid
|
||||
// Add appid, this is only needed for U2F compatibility, so maybe it can be removed as well
|
||||
let app_id = format!("{}/app-id.json", &CONFIG.domain());
|
||||
state["ast"]["appid"] = Value::String(app_id.clone());
|
||||
response
|
||||
@ -423,7 +414,7 @@ pub async fn validate_webauthn_login(
|
||||
let type_ = TwoFactorType::WebauthnLoginChallenge as i32;
|
||||
let state = match TwoFactor::find_by_user_and_type(user_id, type_, conn).await {
|
||||
Some(tf) => {
|
||||
let state: PasskeyAuthentication = serde_json::from_str(&tf.data)?;
|
||||
let state: SecurityKeyAuthentication = serde_json::from_str(&tf.data)?;
|
||||
tf.delete(conn).await?;
|
||||
state
|
||||
}
|
||||
@ -440,7 +431,7 @@ pub async fn validate_webauthn_login(
|
||||
|
||||
let mut registrations = get_webauthn_registrations(user_id, conn).await?.1;
|
||||
|
||||
let authentication_result = webauthn.finish_passkey_authentication(&rsp, &state)?;
|
||||
let authentication_result = webauthn.finish_securitykey_authentication(&rsp, &state)?;
|
||||
|
||||
for reg in &mut registrations {
|
||||
if reg.credential.cred_id() == authentication_result.cred_id() && authentication_result.needs_update() {
|
||||
|
Loading…
x
Reference in New Issue
Block a user