pub mod authentication;
pub mod credential;
pub mod registration;
pub use credential::{
CredentialStore, CredentialStoreError, InMemoryCredentialStore, StoredCredential,
};
pub use registration::{
InMemoryRegistrationStateStore, RegistrationConfig, RegistrationError, RegistrationManager,
RegistrationState, RegistrationStateStore, UserVerification,
};
pub use authentication::{
AuthenticationConfig, AuthenticationError, AuthenticationManager, AuthenticationState,
AuthenticationStateStore, InMemoryAuthenticationStateStore, WebAuthnAuthenticationResult,
};
pub use url::Url;
pub use webauthn_rs::prelude::{
AuthenticatorAttachment, CreationChallengeResponse, Passkey, PublicKeyCredential,
RegisterPublicKeyCredential, RequestChallengeResponse, Uuid, Webauthn, WebauthnBuilder,
};
pub struct WebAuthnService {
webauthn: Webauthn,
rp_id: String,
rp_origin: Url,
rp_name: String,
}
impl WebAuthnService {
pub fn new(
rp_id: impl Into<String>,
rp_origin: impl AsRef<str>,
rp_name: impl Into<String>,
) -> Result<Self, WebAuthnServiceError> {
let rp_id = rp_id.into();
let rp_name = rp_name.into();
let rp_origin = Url::parse(rp_origin.as_ref())
.map_err(|e| WebAuthnServiceError::InvalidOrigin(e.to_string()))?;
let webauthn = WebauthnBuilder::new(&rp_id, &rp_origin)
.map_err(|e| WebAuthnServiceError::ConfigurationError(e.to_string()))?
.rp_name(&rp_name)
.build()
.map_err(|e| WebAuthnServiceError::ConfigurationError(e.to_string()))?;
Ok(Self {
webauthn,
rp_id,
rp_origin,
rp_name,
})
}
pub fn webauthn(&self) -> &Webauthn {
&self.webauthn
}
pub fn rp_id(&self) -> &str {
&self.rp_id
}
pub fn rp_origin(&self) -> &Url {
&self.rp_origin
}
pub fn rp_name(&self) -> &str {
&self.rp_name
}
pub fn registration_manager(&self) -> RegistrationManager<'_> {
RegistrationManager::new(&self.webauthn)
}
pub fn registration_manager_with_config(
&self,
config: RegistrationConfig,
) -> RegistrationManager<'_> {
RegistrationManager::with_config(&self.webauthn, config)
}
pub fn authentication_manager(&self) -> AuthenticationManager<'_> {
AuthenticationManager::new(&self.webauthn)
}
pub fn authentication_manager_with_config(
&self,
config: AuthenticationConfig,
) -> AuthenticationManager<'_> {
AuthenticationManager::with_config(&self.webauthn, config)
}
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum WebAuthnServiceError {
InvalidOrigin(String),
ConfigurationError(String),
}
impl std::fmt::Display for WebAuthnServiceError {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
Self::InvalidOrigin(e) => write!(f, "无效的 Origin URL: {}", e),
Self::ConfigurationError(e) => write!(f, "WebAuthn 配置错误: {}", e),
}
}
}
impl std::error::Error for WebAuthnServiceError {}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_webauthn_service_creation() {
let service = WebAuthnService::new("example.com", "https://example.com", "Test App");
assert!(service.is_ok());
let service = service.unwrap();
assert_eq!(service.rp_id(), "example.com");
assert_eq!(service.rp_name(), "Test App");
}
#[test]
fn test_webauthn_service_invalid_origin() {
let result = WebAuthnService::new("example.com", "not a valid url", "Test App");
assert!(result.is_err());
match result {
Err(WebAuthnServiceError::InvalidOrigin(_)) => {}
_ => panic!("应该返回 InvalidOrigin 错误"),
}
}
#[test]
fn test_webauthn_service_managers() {
let service =
WebAuthnService::new("example.com", "https://example.com", "Test App").unwrap();
let _reg_manager = service.registration_manager();
let _auth_manager = service.authentication_manager();
let _reg_manager =
service.registration_manager_with_config(RegistrationConfig::high_security());
let _auth_manager =
service.authentication_manager_with_config(AuthenticationConfig::high_security());
}
#[test]
fn test_webauthn_service_error_display() {
let error = WebAuthnServiceError::InvalidOrigin("test".to_string());
assert!(error.to_string().contains("无效的 Origin URL"));
let error = WebAuthnServiceError::ConfigurationError("test".to_string());
assert!(error.to_string().contains("配置错误"));
}
}