From a4b480dc9ff7647c06de8f6d1edf6a281b3cc977 Mon Sep 17 00:00:00 2001 From: zUnixorn Date: Wed, 4 Jun 2025 04:03:06 +0200 Subject: [PATCH] implement webauthn login deletion (untested) --- src/api/core/mod.rs | 19 +++++++++++++++---- src/db/models/mod.rs | 2 +- src/db/models/web_authn_credential.rs | 12 +++++++++++- 3 files changed, 27 insertions(+), 6 deletions(-) diff --git a/src/api/core/mod.rs b/src/api/core/mod.rs index 73c3d813..0b46dfa8 100644 --- a/src/api/core/mod.rs +++ b/src/api/core/mod.rs @@ -10,7 +10,7 @@ pub mod two_factor; use std::collections::HashMap; use std::sync::{Mutex, OnceLock}; -use crate::db::models::WebAuthnCredential; +use crate::db::models::{WebAuthnCredential, WebAuthnCredentialId}; pub use accounts::purge_auth_requests; pub use ciphers::{purge_trashed_ciphers, CipherData, CipherSyncData, CipherSyncType}; pub use emergency_access::{emergency_notification_reminder_job, emergency_request_timeout_job}; @@ -21,7 +21,7 @@ pub use sends::purge_sends; pub fn routes() -> Vec { let mut eq_domains_routes = routes![get_eq_domains, post_eq_domains, put_eq_domains]; let mut hibp_routes = routes![hibp_breach]; - let mut meta_routes = routes![alive, now, version, config, get_api_webauthn, post_api_webauthn, post_api_webauthn_attestation_options]; + let mut meta_routes = routes![alive, now, version, config, get_api_webauthn, post_api_webauthn, post_api_webauthn_attestation_options, post_api_webauthn_delete]; let mut routes = Vec::new(); routes.append(&mut accounts::routes()); @@ -192,14 +192,25 @@ fn version() -> Json<&'static str> { Json(crate::VERSION.unwrap_or_default()) } +#[post("/webauthn//delete", data = "")] +async fn post_api_webauthn_delete(data: Json, uuid: String, headers: Headers, mut conn: DbConn) -> ApiResult { + let data: PasswordOrOtpData = data.into_inner(); + let user = headers.user; + + data.validate(&user, false, &mut conn).await?; + + WebAuthnCredential::delete_by_uuid_and_user(&WebAuthnCredentialId(uuid), &user.uuid, &mut conn).await?; + + Ok(Status::Ok) +} + static WEBAUTHN_STATES: OnceLock>> = OnceLock::new(); #[post("/webauthn/attestation-options", data = "")] async fn post_api_webauthn_attestation_options(data: Json, headers: Headers, mut conn: DbConn) -> JsonResult { let data: PasswordOrOtpData = data.into_inner(); let user = headers.user; - - // TODO what does delete_if_valid do? + data.validate(&user, false, &mut conn).await?; // C# does this check as well diff --git a/src/db/models/mod.rs b/src/db/models/mod.rs index d33a0477..5f31a26c 100644 --- a/src/db/models/mod.rs +++ b/src/db/models/mod.rs @@ -40,4 +40,4 @@ pub use self::two_factor::{TwoFactor, TwoFactorType}; pub use self::two_factor_duo_context::TwoFactorDuoContext; pub use self::two_factor_incomplete::TwoFactorIncomplete; pub use self::user::{Invitation, User, UserId, UserKdfType, UserStampException}; -pub use self::web_authn_credential::WebAuthnCredential; +pub use self::web_authn_credential::{WebAuthnCredential, WebAuthnCredentialId}; diff --git a/src/db/models/web_authn_credential.rs b/src/db/models/web_authn_credential.rs index 5e8c1a06..a548aaaf 100644 --- a/src/db/models/web_authn_credential.rs +++ b/src/db/models/web_authn_credential.rs @@ -2,6 +2,7 @@ use derive_more::{AsRef, Deref, Display, From}; use macros::UuidFromParam; use crate::api::EmptyResult; use crate::db::DbConn; +use crate::MapResult; use super::UserId; db_object! { @@ -68,6 +69,15 @@ impl WebAuthnCredential { // TODO do not unwrap }}.unwrap() } + + pub async fn delete_by_uuid_and_user(uuid: &WebAuthnCredentialId, user_uuid: &UserId, conn: &mut DbConn) -> EmptyResult { + db_run! { conn: { + diesel::delete(web_authn_credentials::table + .filter(web_authn_credentials::uuid.eq(uuid)) + .filter(web_authn_credentials::user_uuid.eq(user_uuid)) + ).execute(conn).map_res("Error removing web_authn_credential for user") + }} + } } #[derive( @@ -86,4 +96,4 @@ impl WebAuthnCredential { Deserialize, UuidFromParam, )] -pub struct WebAuthnCredentialId(String); +pub struct WebAuthnCredentialId(pub String);