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:
parent
cbf9a2a519
commit
17385f0fd9
2 changed files with 59 additions and 9 deletions
|
@ -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)?)
|
||||
|
|
|
@ -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(®s)?)
|
||||
.save(conn)
|
||||
.await?;
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue