1
0
Fork 0
mirror of https://github.com/dani-garcia/vaultwarden.git synced 2025-06-11 21:40:08 +00:00

add basic migration impl

This commit is contained in:
zUnixorn 2025-06-06 05:30:16 +02:00
parent cbf9a2a519
commit 17385f0fd9
No known key found for this signature in database
GPG key ID: 0BE3A9CAE3E8D0DA
2 changed files with 59 additions and 9 deletions

View file

@ -133,12 +133,15 @@ async fn generate_webauthn_challenge(data: Json<PasswordOrOtpData>, headers: Hea
Some(registrations),
)?;
// TODO is there a nicer way to do this?
// this is done since `start_passkey_registration()` always sets this to `Required` which shouldn't be needed for 2FA
challenge.public_key.authenticator_selection = challenge.public_key.authenticator_selection.map(|mut a| {
a.user_verification = UserVerificationPolicy::Discouraged_DO_NOT_USE;
a
});
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?;
@ -368,10 +371,12 @@ pub async fn generate_webauthn_login(user_id: &UserId, webauthn: Webauthn2FaConf
// Generate a challenge based on the credentials
let (mut response, state) = webauthn.start_passkey_authentication(&creds)?;
// Modify to discourage user verification
let mut state = serde_json::to_value(&state)?;
state["ast"]["policy"] = Value::String("discouraged".to_string());
state["ast"]["appid"] = Value::String(format!("{}/app-id.json", &CONFIG.domain()));
response.public_key.user_verification = UserVerificationPolicy::Discouraged_DO_NOT_USE;
// TODO does the appid extension matter? As far as I understand, this was only put into the authentication state anyway
// let ext = RequestAuthenticationExtensions::builder().appid(format!("{}/app-id.json", &CONFIG.domain())).build();
// Save the challenge state for later validation
TwoFactor::new(user_id.clone(), TwoFactorType::WebauthnLoginChallenge, serde_json::to_string(&state)?)

View file

@ -3,6 +3,7 @@ use webauthn_rs::prelude::{Credential, ParsedAttestation};
use webauthn_rs_proto::{AttestationFormat, RegisteredExtensions};
use super::UserId;
use crate::{api::EmptyResult, db::DbConn, error::MapResult};
use crate::api::core::two_factor::webauthn::WebauthnRegistration;
db_object! {
#[derive(Identifiable, Queryable, Insertable, AsChangeset)]
@ -45,7 +46,27 @@ mod webauthn_0_3 {
use webauthn_rs::prelude::ParsedAttestation;
use webauthn_rs_proto::{AttestationFormat, RegisteredExtensions};
#[derive(Deserialize)]
pub struct WebauthnRegistration {
pub id: i32,
pub name: String,
pub migrated: bool,
pub credential: Credential,
}
impl From<WebauthnRegistration> for crate::api::core::two_factor::webauthn::WebauthnRegistration {
fn from(value: WebauthnRegistration) -> Self {
Self {
id: value.id,
name: value.name,
migrated: value.migrated,
credential: webauthn_rs::prelude::Credential::from(value.credential).into(),
}
}
}
// Copied from https://docs.rs/webauthn-rs/0.3.2/src/webauthn_rs/proto.rs.html#316-339
#[derive(Deserialize)]
pub struct Credential {
pub cred_id: Vec<u8>,
pub cred: COSEKey,
@ -329,7 +350,31 @@ impl TwoFactor {
}
pub async fn migrate_credential_to_passkey(conn: &mut DbConn) -> EmptyResult {
todo!()
let webauthn_factors = db_run! { conn: {
twofactor::table
.filter(twofactor::atype.eq(TwoFactorType::Webauthn as i32))
.load::<TwoFactorDb>(conn)
.expect("Error loading twofactor")
.from_db()
}};
for webauthn_factor in webauthn_factors {
// assume that a failure to parse into the old struct, means that it was already converted
// alternatively this could also be checked via an extra field in the db
let Ok(regs) = serde_json::from_str::<Vec<webauthn_0_3::WebauthnRegistration>>(&webauthn_factor.data) else {
continue;
};
let regs = regs.into_iter()
.map(|r| r.into())
.collect::<Vec<WebauthnRegistration>>();
TwoFactor::new(webauthn_factor.user_uuid.clone(), TwoFactorType::Webauthn, serde_json::to_string(&regs)?)
.save(conn)
.await?;
}
Ok(())
}
}