//! Invalid responses cannot cancel valid challenges. Pending ceremonies have a fixed bound. use sausage::cred::webauthn::{Binding, Passkeys}; use webauthn_authenticator_rs::{WebauthnAuthenticator, softpasskey::SoftPasskey}; use webauthn_rs::prelude::*; #[test] fn failed_response_preserves_challenge_and_oldest_challenge_is_evicted() -> anyhow::Result<()> { let db = rusqlite::Connection::open_in_memory()?; let origin = Url::parse("http://localhost:8000")?; let mut host = Passkeys::new(&db, "localhost", origin.as_str())?; let mut auth = WebauthnAuthenticator::new(SoftPasskey::new(true)); let registration = host.start_registration("test".into(), None)?; let response = auth.do_registration(origin.clone(), registration.options)?; host.finish_registration(®istration.id, response)?; let binding = Binding::Enrollment { label: "next".into(), }; let valid = host.start_assertion(binding.clone())?; let wrong = host.start_assertion(binding.clone())?; let invalid_response = auth.do_authentication(origin.clone(), wrong.options)?; assert!(host.finish_assertion(&valid.id, invalid_response).is_err()); let response = auth.do_authentication(origin.clone(), valid.options)?; host.finish_assertion(&valid.id, response)?; let oldest = host.start_assertion(binding.clone())?; for _ in 0..32 { host.start_assertion(binding.clone())?; } let response = auth.do_authentication(origin, oldest.options)?; assert!(host.finish_assertion(&oldest.id, response).is_err()); Ok(()) }