From 0bf30d34757e7c89d6e00f82a302ac44094ae5ea Mon Sep 17 00:00:00 2001 From: Jon Seager Date: Fri, 13 Feb 2026 13:05:14 +0000 Subject: [PATCH] fix: verify authenticated user in passkey add finish Add AuthenticatedUser extractor to passkey_add_finish and verify the session user matches the challenge owner, preventing one user from completing another user's passkey registration. --- src/application/routes/api/auth/webauthn.rs | 17 +++++++++++++---- 1 file changed, 13 insertions(+), 4 deletions(-) diff --git a/src/application/routes/api/auth/webauthn.rs b/src/application/routes/api/auth/webauthn.rs index 9d1fcc3..ef229df 100644 --- a/src/application/routes/api/auth/webauthn.rs +++ b/src/application/routes/api/auth/webauthn.rs @@ -8,7 +8,7 @@ use tracing::{error, info, warn}; use uuid::Uuid; use webauthn_rs::prelude::*; -use crate::application::auth::AuthenticatedUser; +use crate::application::auth::{AuthenticatedUser, SESSION_COOKIE_NAME}; use crate::application::state::AppState; use crate::domain::passkey_credentials::NewPasskeyCredential; use crate::domain::sessions::NewSession; @@ -17,8 +17,6 @@ use crate::domain::users::NewUser; use crate::infrastructure::auth::{generate_session_token, generate_token, hash_token}; use crate::infrastructure::webauthn::CliCallbackInfo; -const SESSION_COOKIE_NAME: &str = "brewlog_session"; - // --- Request/Response types --- #[derive(Deserialize)] @@ -432,9 +430,10 @@ pub(crate) async fn passkey_add_start( })) } -#[tracing::instrument(skip(state, payload))] +#[tracing::instrument(skip(state, auth_user, payload))] pub(crate) async fn passkey_add_finish( State(state): State, + auth_user: AuthenticatedUser, Json(payload): Json, ) -> Result { let (user_id, reg_state) = state @@ -443,6 +442,16 @@ pub(crate) async fn passkey_add_finish( .await .ok_or(StatusCode::BAD_REQUEST)?; + // Verify the authenticated user matches the challenge owner + if auth_user.0.id != user_id { + warn!( + auth_user_id = %auth_user.0.id, + challenge_user_id = %user_id, + "passkey add finish: user mismatch" + ); + return Err(StatusCode::FORBIDDEN); + } + let passkey = state .webauthn .finish_passkey_registration(&payload.credential, ®_state)