feat(auth): add WebAuthn passkey support with migrations, routes, and tests

This commit is contained in:
2025-10-29 17:50:14 +01:00
parent ec6e7f04f2
commit ca30b20873
23 changed files with 1511 additions and 83 deletions
+27
View File
@@ -58,6 +58,12 @@ pub struct AppConfig {
pub default_tenant_slug: String,
#[serde(default = "default_worker_max_document_bytes")]
pub worker_max_document_bytes: u64,
#[serde(default)]
pub webauthn_rp_id: Option<String>,
#[serde(default)]
pub webauthn_origin: Option<String>,
#[serde(default = "default_webauthn_rp_name")]
pub webauthn_rp_name: String,
}
impl AppConfig {
@@ -69,6 +75,7 @@ impl AppConfig {
database_url = %config.redacted_database_url(),
pool_size = config.database_max_pool_size,
quickwit_enabled = config.quickwit_endpoint.is_some(),
passkeys_enabled = config.webauthn_origin.is_some(),
s3_bucket = %config.s3_bucket,
worker_max_document_bytes = config.worker_max_document_bytes,
"loaded backend configuration"
@@ -92,6 +99,22 @@ impl AppConfig {
if self.webdav_host.is_empty() {
self.webdav_host = self.server_host.clone();
}
if self.webauthn_rp_id.is_none() {
self.webauthn_rp_id = Some(self.server_host.clone());
}
if self.webauthn_origin.is_none() {
let scheme = if self.server_host == "127.0.0.1" || self.server_host == "localhost" {
"http"
} else {
"https"
};
self.webauthn_origin = Some(format!(
"{scheme}://{}:{}",
self.server_host, self.server_port
));
}
self
}
}
@@ -156,6 +179,10 @@ fn default_worker_max_document_bytes() -> u64 {
200 * 1024 * 1024
}
fn default_webauthn_rp_name() -> String {
"Papercrate".to_string()
}
fn redact_database_url(raw: &str) -> String {
match Url::parse(raw) {
Ok(mut parsed) => {