diff --git a/Cargo.lock b/Cargo.lock
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -7405,7 +7405,7 @@
[[package]]
name = "tranquil-api"
-version = "0.5.0"
+version = "0.5.1"
dependencies = [
"anyhow",
"axum",
@@ -7456,7 +7456,7 @@
[[package]]
name = "tranquil-auth"
-version = "0.5.0"
+version = "0.5.1"
dependencies = [
"anyhow",
"base32",
@@ -7479,7 +7479,7 @@
[[package]]
name = "tranquil-cache"
-version = "0.5.0"
+version = "0.5.1"
dependencies = [
"async-trait",
"base64 0.22.1",
@@ -7493,7 +7493,7 @@
[[package]]
name = "tranquil-comms"
-version = "0.5.0"
+version = "0.5.1"
dependencies = [
"async-trait",
"base64 0.22.1",
@@ -7511,7 +7511,7 @@
[[package]]
name = "tranquil-config"
-version = "0.5.0"
+version = "0.5.1"
dependencies = [
"confique",
"serde",
@@ -7519,7 +7519,7 @@
[[package]]
name = "tranquil-crypto"
-version = "0.5.0"
+version = "0.5.1"
dependencies = [
"aes-gcm",
"base64 0.22.1",
@@ -7535,7 +7535,7 @@
[[package]]
name = "tranquil-db"
-version = "0.5.0"
+version = "0.5.1"
dependencies = [
"async-trait",
"chrono",
@@ -7552,7 +7552,7 @@
[[package]]
name = "tranquil-db-traits"
-version = "0.5.0"
+version = "0.5.1"
dependencies = [
"async-trait",
"base64 0.22.1",
@@ -7568,7 +7568,7 @@
[[package]]
name = "tranquil-infra"
-version = "0.5.0"
+version = "0.5.1"
dependencies = [
"async-trait",
"bytes",
@@ -7579,7 +7579,7 @@
[[package]]
name = "tranquil-lexicon"
-version = "0.5.0"
+version = "0.5.1"
dependencies = [
"chrono",
"hickory-resolver",
@@ -7597,7 +7597,7 @@
[[package]]
name = "tranquil-oauth"
-version = "0.5.0"
+version = "0.5.1"
dependencies = [
"anyhow",
"axum",
@@ -7620,7 +7620,7 @@
[[package]]
name = "tranquil-oauth-server"
-version = "0.5.0"
+version = "0.5.1"
dependencies = [
"axum",
"base64 0.22.1",
@@ -7653,7 +7653,7 @@
[[package]]
name = "tranquil-pds"
-version = "0.5.0"
+version = "0.5.1"
dependencies = [
"aes-gcm",
"anyhow",
@@ -7738,13 +7738,14 @@
"urlencoding",
"uuid",
"webauthn-rs",
+ "webauthn-rs-proto",
"wiremock",
"zip",
]
[[package]]
name = "tranquil-repo"
-version = "0.5.0"
+version = "0.5.1"
dependencies = [
"bytes",
"cid",
@@ -7756,7 +7757,7 @@
[[package]]
name = "tranquil-ripple"
-version = "0.5.0"
+version = "0.5.1"
dependencies = [
"async-trait",
"backon",
@@ -7781,7 +7782,7 @@
[[package]]
name = "tranquil-scopes"
-version = "0.5.0"
+version = "0.5.1"
dependencies = [
"axum",
"futures",
@@ -7797,7 +7798,7 @@
[[package]]
name = "tranquil-server"
-version = "0.5.0"
+version = "0.5.1"
dependencies = [
"axum",
"clap",
@@ -7818,7 +7819,7 @@
[[package]]
name = "tranquil-signal"
-version = "0.5.0"
+version = "0.5.1"
dependencies = [
"async-trait",
"chrono",
@@ -7841,7 +7842,7 @@
[[package]]
name = "tranquil-storage"
-version = "0.5.0"
+version = "0.5.1"
dependencies = [
"async-trait",
"aws-config",
@@ -7858,7 +7859,7 @@
[[package]]
name = "tranquil-store"
-version = "0.5.0"
+version = "0.5.1"
dependencies = [
"async-trait",
"bytes",
@@ -7904,7 +7905,7 @@
[[package]]
name = "tranquil-sync"
-version = "0.5.0"
+version = "0.5.1"
dependencies = [
"anyhow",
"axum",
@@ -7926,7 +7927,7 @@
[[package]]
name = "tranquil-types"
-version = "0.5.0"
+version = "0.5.1"
dependencies = [
"chrono",
"cid",
diff --git a/Cargo.toml b/Cargo.toml
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -26,7 +26,7 @@
]
[workspace.package]
-version = "0.5.0"
+version = "0.5.1"
edition = "2024"
license = "AGPL-3.0-or-later"
@@ -126,7 +126,7 @@
tracing-subscriber = "0.3"
urlencoding = "2.1"
uuid = { version = "1.19", features = ["v4", "v5", "v7", "fast-rng", "serde"] }
-webauthn-rs = { version = "0.5", features = ["danger-allow-state-serialisation", "danger-user-presence-only-security-keys"] }
+webauthn-rs = { version = "0.5", features = ["danger-allow-state-serialisation", "danger-user-presence-only-security-keys", "conditional-ui"] }
webauthn-rs-proto = "0.5"
zip = { version = "7.0", default-features = false, features = ["deflate"] }
diff --git a/.sqlx/query-053c971024b0d29a441c3597d760b3e21db2383442c3e6f09de4eb49ea437e7c.json b/.sqlx/query-053c971024b0d29a441c3597d760b3e21db2383442c3e6f09de4eb49ea437e7c.json
new file mode 100644
--- /dev/null
+++ b/.sqlx/query-053c971024b0d29a441c3597d760b3e21db2383442c3e6f09de4eb49ea437e7c.json
@@ -0,0 +1,28 @@
+{
+ "db_name": "PostgreSQL",
+ "query": "SELECT did, password_hash FROM users WHERE handle = $1 OR did = $1",
+ "describe": {
+ "columns": [
+ {
+ "ordinal": 0,
+ "name": "did",
+ "type_info": "Text"
+ },
+ {
+ "ordinal": 1,
+ "name": "password_hash",
+ "type_info": "Text"
+ }
+ ],
+ "parameters": {
+ "Left": [
+ "Text"
+ ]
+ },
+ "nullable": [
+ false,
+ true
+ ]
+ },
+ "hash": "053c971024b0d29a441c3597d760b3e21db2383442c3e6f09de4eb49ea437e7c"
+}
diff --git a/.sqlx/query-060c285c93a05252aab7d474df0186e7b5083fafedc582b8eac9916983e8fc2d.json b/.sqlx/query-060c285c93a05252aab7d474df0186e7b5083fafedc582b8eac9916983e8fc2d.json
new file mode 100644
--- /dev/null
+++ b/.sqlx/query-060c285c93a05252aab7d474df0186e7b5083fafedc582b8eac9916983e8fc2d.json
@@ -0,0 +1,122 @@
+{
+ "db_name": "PostgreSQL",
+ "query": "\n SELECT id, did, email, password_hash, password_required, two_factor_enabled,\n preferred_comms_channel as \"preferred_comms_channel!: CommsChannel\",\n deactivated_at, takedown_ref,\n email_verified, discord_verified, telegram_verified, signal_verified,\n account_type as \"account_type!: AccountType\"\n FROM users\n WHERE handle = $1 OR did = $1\n ",
+ "describe": {
+ "columns": [
+ {
+ "ordinal": 0,
+ "name": "id",
+ "type_info": "Uuid"
+ },
+ {
+ "ordinal": 1,
+ "name": "did",
+ "type_info": "Text"
+ },
+ {
+ "ordinal": 2,
+ "name": "email",
+ "type_info": "Text"
+ },
+ {
+ "ordinal": 3,
+ "name": "password_hash",
+ "type_info": "Text"
+ },
+ {
+ "ordinal": 4,
+ "name": "password_required",
+ "type_info": "Bool"
+ },
+ {
+ "ordinal": 5,
+ "name": "two_factor_enabled",
+ "type_info": "Bool"
+ },
+ {
+ "ordinal": 6,
+ "name": "preferred_comms_channel!: CommsChannel",
+ "type_info": {
+ "Custom": {
+ "name": "comms_channel",
+ "kind": {
+ "Enum": [
+ "email",
+ "discord",
+ "telegram",
+ "signal"
+ ]
+ }
+ }
+ }
+ },
+ {
+ "ordinal": 7,
+ "name": "deactivated_at",
+ "type_info": "Timestamptz"
+ },
+ {
+ "ordinal": 8,
+ "name": "takedown_ref",
+ "type_info": "Text"
+ },
+ {
+ "ordinal": 9,
+ "name": "email_verified",
+ "type_info": "Bool"
+ },
+ {
+ "ordinal": 10,
+ "name": "discord_verified",
+ "type_info": "Bool"
+ },
+ {
+ "ordinal": 11,
+ "name": "telegram_verified",
+ "type_info": "Bool"
+ },
+ {
+ "ordinal": 12,
+ "name": "signal_verified",
+ "type_info": "Bool"
+ },
+ {
+ "ordinal": 13,
+ "name": "account_type!: AccountType",
+ "type_info": {
+ "Custom": {
+ "name": "account_type",
+ "kind": {
+ "Enum": [
+ "personal",
+ "delegated"
+ ]
+ }
+ }
+ }
+ }
+ ],
+ "parameters": {
+ "Left": [
+ "Text"
+ ]
+ },
+ "nullable": [
+ false,
+ false,
+ true,
+ true,
+ false,
+ false,
+ false,
+ true,
+ true,
+ false,
+ false,
+ false,
+ false,
+ false
+ ]
+ },
+ "hash": "060c285c93a05252aab7d474df0186e7b5083fafedc582b8eac9916983e8fc2d"
+}
diff --git a/.sqlx/query-3155ef4f35698a3fe6aa38d5d976fd51b7f6a0381c81c4907dad61d2f37992bd.json b/.sqlx/query-3155ef4f35698a3fe6aa38d5d976fd51b7f6a0381c81c4907dad61d2f37992bd.json
new file mode 100644
--- /dev/null
+++ b/.sqlx/query-3155ef4f35698a3fe6aa38d5d976fd51b7f6a0381c81c4907dad61d2f37992bd.json
@@ -0,0 +1,14 @@
+{
+ "db_name": "PostgreSQL",
+ "query": "DELETE FROM webauthn_challenges WHERE did = $1 AND challenge_type = 'discoverable'",
+ "describe": {
+ "columns": [],
+ "parameters": {
+ "Left": [
+ "Text"
+ ]
+ },
+ "nullable": []
+ },
+ "hash": "3155ef4f35698a3fe6aa38d5d976fd51b7f6a0381c81c4907dad61d2f37992bd"
+}
diff --git a/.sqlx/query-6969c478a0922bac4b79902313a0e28c94d6b8d6b16035474dd8f484e6171d60.json b/.sqlx/query-6969c478a0922bac4b79902313a0e28c94d6b8d6b16035474dd8f484e6171d60.json
new file mode 100644
--- /dev/null
+++ b/.sqlx/query-6969c478a0922bac4b79902313a0e28c94d6b8d6b16035474dd8f484e6171d60.json
@@ -0,0 +1,22 @@
+{
+ "db_name": "PostgreSQL",
+ "query": "SELECT state_json FROM webauthn_challenges\n WHERE did = $1 AND challenge_type = 'discoverable' AND expires_at > NOW()\n ORDER BY created_at DESC LIMIT 1",
+ "describe": {
+ "columns": [
+ {
+ "ordinal": 0,
+ "name": "state_json",
+ "type_info": "Text"
+ }
+ ],
+ "parameters": {
+ "Left": [
+ "Text"
+ ]
+ },
+ "nullable": [
+ false
+ ]
+ },
+ "hash": "6969c478a0922bac4b79902313a0e28c94d6b8d6b16035474dd8f484e6171d60"
+}
diff --git a/.sqlx/query-7061e8763ef7d91ff152ed0124f99e1820172fd06916d225ca6c5137a507b8fa.json b/.sqlx/query-7061e8763ef7d91ff152ed0124f99e1820172fd06916d225ca6c5137a507b8fa.json
deleted file mode 100644
--- a/.sqlx/query-7061e8763ef7d91ff152ed0124f99e1820172fd06916d225ca6c5137a507b8fa.json
+++ /dev/null
@@ -1,122 +0,0 @@
-{
- "db_name": "PostgreSQL",
- "query": "\n SELECT id, did, email, password_hash, password_required, two_factor_enabled,\n preferred_comms_channel as \"preferred_comms_channel!: CommsChannel\",\n deactivated_at, takedown_ref,\n email_verified, discord_verified, telegram_verified, signal_verified,\n account_type as \"account_type!: AccountType\"\n FROM users\n WHERE handle = $1 OR email = $1\n ",
- "describe": {
- "columns": [
- {
- "ordinal": 0,
- "name": "id",
- "type_info": "Uuid"
- },
- {
- "ordinal": 1,
- "name": "did",
- "type_info": "Text"
- },
- {
- "ordinal": 2,
- "name": "email",
- "type_info": "Text"
- },
- {
- "ordinal": 3,
- "name": "password_hash",
- "type_info": "Text"
- },
- {
- "ordinal": 4,
- "name": "password_required",
- "type_info": "Bool"
- },
- {
- "ordinal": 5,
- "name": "two_factor_enabled",
- "type_info": "Bool"
- },
- {
- "ordinal": 6,
- "name": "preferred_comms_channel!: CommsChannel",
- "type_info": {
- "Custom": {
- "name": "comms_channel",
- "kind": {
- "Enum": [
- "email",
- "discord",
- "telegram",
- "signal"
- ]
- }
- }
- }
- },
- {
- "ordinal": 7,
- "name": "deactivated_at",
- "type_info": "Timestamptz"
- },
- {
- "ordinal": 8,
- "name": "takedown_ref",
- "type_info": "Text"
- },
- {
- "ordinal": 9,
- "name": "email_verified",
- "type_info": "Bool"
- },
- {
- "ordinal": 10,
- "name": "discord_verified",
- "type_info": "Bool"
- },
- {
- "ordinal": 11,
- "name": "telegram_verified",
- "type_info": "Bool"
- },
- {
- "ordinal": 12,
- "name": "signal_verified",
- "type_info": "Bool"
- },
- {
- "ordinal": 13,
- "name": "account_type!: AccountType",
- "type_info": {
- "Custom": {
- "name": "account_type",
- "kind": {
- "Enum": [
- "personal",
- "delegated"
- ]
- }
- }
- }
- }
- ],
- "parameters": {
- "Left": [
- "Text"
- ]
- },
- "nullable": [
- false,
- false,
- true,
- true,
- false,
- false,
- false,
- true,
- true,
- false,
- false,
- false,
- false,
- false
- ]
- },
- "hash": "7061e8763ef7d91ff152ed0124f99e1820172fd06916d225ca6c5137a507b8fa"
-}
diff --git a/.sqlx/query-a960b981a146a0e422ef53601dfc31e29cf777aa194227c48c6ebc6905ea3249.json b/.sqlx/query-a960b981a146a0e422ef53601dfc31e29cf777aa194227c48c6ebc6905ea3249.json
deleted file mode 100644
--- a/.sqlx/query-a960b981a146a0e422ef53601dfc31e29cf777aa194227c48c6ebc6905ea3249.json
+++ /dev/null
@@ -1,136 +0,0 @@
-{
- "db_name": "PostgreSQL",
- "query": "SELECT\n u.id, u.did, u.handle, u.password_hash, u.email, u.deactivated_at, u.takedown_ref,\n u.email_verified, u.discord_verified, u.telegram_verified, u.signal_verified,\n u.allow_legacy_login, u.migrated_to_pds,\n u.preferred_comms_channel as \"preferred_comms_channel: CommsChannel\",\n k.key_bytes, k.encryption_version,\n (SELECT verified FROM user_totp WHERE did = u.did) as totp_enabled,\n COALESCE((SELECT (value_json)::boolean FROM account_preferences WHERE user_id = u.id AND name = 'email_auth_factor' ORDER BY created_at DESC LIMIT 1), false) as \"email_2fa_enabled!\"\n FROM users u\n JOIN user_keys k ON u.id = k.user_id\n WHERE u.handle = $1 OR u.email = $1 OR u.did = $1",
- "describe": {
- "columns": [
- {
- "ordinal": 0,
- "name": "id",
- "type_info": "Uuid"
- },
- {
- "ordinal": 1,
- "name": "did",
- "type_info": "Text"
- },
- {
- "ordinal": 2,
- "name": "handle",
- "type_info": "Text"
- },
- {
- "ordinal": 3,
- "name": "password_hash",
- "type_info": "Text"
- },
- {
- "ordinal": 4,
- "name": "email",
- "type_info": "Text"
- },
- {
- "ordinal": 5,
- "name": "deactivated_at",
- "type_info": "Timestamptz"
- },
- {
- "ordinal": 6,
- "name": "takedown_ref",
- "type_info": "Text"
- },
- {
- "ordinal": 7,
- "name": "email_verified",
- "type_info": "Bool"
- },
- {
- "ordinal": 8,
- "name": "discord_verified",
- "type_info": "Bool"
- },
- {
- "ordinal": 9,
- "name": "telegram_verified",
- "type_info": "Bool"
- },
- {
- "ordinal": 10,
- "name": "signal_verified",
- "type_info": "Bool"
- },
- {
- "ordinal": 11,
- "name": "allow_legacy_login",
- "type_info": "Bool"
- },
- {
- "ordinal": 12,
- "name": "migrated_to_pds",
- "type_info": "Text"
- },
- {
- "ordinal": 13,
- "name": "preferred_comms_channel: CommsChannel",
- "type_info": {
- "Custom": {
- "name": "comms_channel",
- "kind": {
- "Enum": [
- "email",
- "discord",
- "telegram",
- "signal"
- ]
- }
- }
- }
- },
- {
- "ordinal": 14,
- "name": "key_bytes",
- "type_info": "Bytea"
- },
- {
- "ordinal": 15,
- "name": "encryption_version",
- "type_info": "Int4"
- },
- {
- "ordinal": 16,
- "name": "totp_enabled",
- "type_info": "Bool"
- },
- {
- "ordinal": 17,
- "name": "email_2fa_enabled!",
- "type_info": "Bool"
- }
- ],
- "parameters": {
- "Left": [
- "Text"
- ]
- },
- "nullable": [
- false,
- false,
- false,
- true,
- true,
- true,
- true,
- false,
- false,
- false,
- false,
- false,
- true,
- false,
- false,
- true,
- null,
- null
- ]
- },
- "hash": "a960b981a146a0e422ef53601dfc31e29cf777aa194227c48c6ebc6905ea3249"
-}
diff --git a/.sqlx/query-aafc2a7e51200ca1e7071c63c13698bf34ef8b66758ca9ebab4ea706ffb62914.json b/.sqlx/query-aafc2a7e51200ca1e7071c63c13698bf34ef8b66758ca9ebab4ea706ffb62914.json
new file mode 100644
--- /dev/null
+++ b/.sqlx/query-aafc2a7e51200ca1e7071c63c13698bf34ef8b66758ca9ebab4ea706ffb62914.json
@@ -0,0 +1,136 @@
+{
+ "db_name": "PostgreSQL",
+ "query": "SELECT\n u.id, u.did, u.handle, u.password_hash, u.email, u.deactivated_at, u.takedown_ref,\n u.email_verified, u.discord_verified, u.telegram_verified, u.signal_verified,\n u.allow_legacy_login, u.migrated_to_pds,\n u.preferred_comms_channel as \"preferred_comms_channel: CommsChannel\",\n k.key_bytes, k.encryption_version,\n (SELECT verified FROM user_totp WHERE did = u.did) as totp_enabled,\n COALESCE((SELECT (value_json)::boolean FROM account_preferences WHERE user_id = u.id AND name = 'email_auth_factor' ORDER BY created_at DESC LIMIT 1), false) as \"email_2fa_enabled!\"\n FROM users u\n JOIN user_keys k ON u.id = k.user_id\n WHERE u.handle = $1 OR u.did = $1",
+ "describe": {
+ "columns": [
+ {
+ "ordinal": 0,
+ "name": "id",
+ "type_info": "Uuid"
+ },
+ {
+ "ordinal": 1,
+ "name": "did",
+ "type_info": "Text"
+ },
+ {
+ "ordinal": 2,
+ "name": "handle",
+ "type_info": "Text"
+ },
+ {
+ "ordinal": 3,
+ "name": "password_hash",
+ "type_info": "Text"
+ },
+ {
+ "ordinal": 4,
+ "name": "email",
+ "type_info": "Text"
+ },
+ {
+ "ordinal": 5,
+ "name": "deactivated_at",
+ "type_info": "Timestamptz"
+ },
+ {
+ "ordinal": 6,
+ "name": "takedown_ref",
+ "type_info": "Text"
+ },
+ {
+ "ordinal": 7,
+ "name": "email_verified",
+ "type_info": "Bool"
+ },
+ {
+ "ordinal": 8,
+ "name": "discord_verified",
+ "type_info": "Bool"
+ },
+ {
+ "ordinal": 9,
+ "name": "telegram_verified",
+ "type_info": "Bool"
+ },
+ {
+ "ordinal": 10,
+ "name": "signal_verified",
+ "type_info": "Bool"
+ },
+ {
+ "ordinal": 11,
+ "name": "allow_legacy_login",
+ "type_info": "Bool"
+ },
+ {
+ "ordinal": 12,
+ "name": "migrated_to_pds",
+ "type_info": "Text"
+ },
+ {
+ "ordinal": 13,
+ "name": "preferred_comms_channel: CommsChannel",
+ "type_info": {
+ "Custom": {
+ "name": "comms_channel",
+ "kind": {
+ "Enum": [
+ "email",
+ "discord",
+ "telegram",
+ "signal"
+ ]
+ }
+ }
+ }
+ },
+ {
+ "ordinal": 14,
+ "name": "key_bytes",
+ "type_info": "Bytea"
+ },
+ {
+ "ordinal": 15,
+ "name": "encryption_version",
+ "type_info": "Int4"
+ },
+ {
+ "ordinal": 16,
+ "name": "totp_enabled",
+ "type_info": "Bool"
+ },
+ {
+ "ordinal": 17,
+ "name": "email_2fa_enabled!",
+ "type_info": "Bool"
+ }
+ ],
+ "parameters": {
+ "Left": [
+ "Text"
+ ]
+ },
+ "nullable": [
+ false,
+ false,
+ false,
+ true,
+ true,
+ true,
+ true,
+ false,
+ false,
+ false,
+ false,
+ false,
+ true,
+ false,
+ false,
+ true,
+ null,
+ null
+ ]
+ },
+ "hash": "aafc2a7e51200ca1e7071c63c13698bf34ef8b66758ca9ebab4ea706ffb62914"
+}
diff --git a/.sqlx/query-c4621f6a8a1ab78a6355b09fdfc2bf8999d276564e93015792ec07cb05e79038.json b/.sqlx/query-c4621f6a8a1ab78a6355b09fdfc2bf8999d276564e93015792ec07cb05e79038.json
deleted file mode 100644
--- a/.sqlx/query-c4621f6a8a1ab78a6355b09fdfc2bf8999d276564e93015792ec07cb05e79038.json
+++ /dev/null
@@ -1,28 +0,0 @@
-{
- "db_name": "PostgreSQL",
- "query": "SELECT did, password_hash FROM users WHERE handle = $1 OR email = $1",
- "describe": {
- "columns": [
- {
- "ordinal": 0,
- "name": "did",
- "type_info": "Text"
- },
- {
- "ordinal": 1,
- "name": "password_hash",
- "type_info": "Text"
- }
- ],
- "parameters": {
- "Left": [
- "Text"
- ]
- },
- "nullable": [
- false,
- true
- ]
- },
- "hash": "c4621f6a8a1ab78a6355b09fdfc2bf8999d276564e93015792ec07cb05e79038"
-}
diff --git a/.sqlx/query-c6e3388fc39983f1787917606ba3194c72322d2d1ec54402c262194791a2b06a.json b/.sqlx/query-c6e3388fc39983f1787917606ba3194c72322d2d1ec54402c262194791a2b06a.json
new file mode 100644
--- /dev/null
+++ b/.sqlx/query-c6e3388fc39983f1787917606ba3194c72322d2d1ec54402c262194791a2b06a.json
@@ -0,0 +1,18 @@
+{
+ "db_name": "PostgreSQL",
+ "query": "INSERT INTO webauthn_challenges (id, did, challenge, challenge_type, state_json, expires_at)\n VALUES ($1, $2, $3, 'discoverable', $4, $5)",
+ "describe": {
+ "columns": [],
+ "parameters": {
+ "Left": [
+ "Uuid",
+ "Text",
+ "Bytea",
+ "Text",
+ "Timestamptz"
+ ]
+ },
+ "nullable": []
+ },
+ "hash": "c6e3388fc39983f1787917606ba3194c72322d2d1ec54402c262194791a2b06a"
+}
diff --git a/crates/tranquil-pds/Cargo.toml b/crates/tranquil-pds/Cargo.toml
--- a/crates/tranquil-pds/Cargo.toml
+++ b/crates/tranquil-pds/Cargo.toml
@@ -78,6 +78,7 @@
urlencoding = { workspace = true }
uuid = { workspace = true }
webauthn-rs = { workspace = true }
+webauthn-rs-proto = { workspace = true }
zip = { workspace = true }
aws-config = { workspace = true, optional = true }
aws-sdk-s3 = { workspace = true, optional = true }
diff --git a/crates/tranquil-db-traits/src/user.rs b/crates/tranquil-db-traits/src/user.rs
--- a/crates/tranquil-db-traits/src/user.rs
+++ b/crates/tranquil-db-traits/src/user.rs
@@ -144,12 +144,12 @@
async fn get_by_email(&self, email: &str) -> Result