fuck WIP shit

This commit is contained in:
mortis-0 2024-03-14 00:24:04 +08:00
parent 8a900fe7a7
commit 75e616201b
17 changed files with 1031 additions and 130 deletions

View File

@ -0,0 +1,12 @@
{
"db_name": "SQLite",
"query": "INSERT INTO users(KEY, NAME) VALUES($1, $2);",
"describe": {
"columns": [],
"parameters": {
"Right": 2
},
"nullable": []
},
"hash": "0e032978b712ce5ad586ab15bdc4efe82935f5f681c3cf54cbeaf845b70c7ff6"
}

View File

@ -0,0 +1,20 @@
{
"db_name": "SQLite",
"query": "SELECT key FROM users WHERE name = $1;",
"describe": {
"columns": [
{
"name": "KEY",
"ordinal": 0,
"type_info": "Text"
}
],
"parameters": {
"Right": 1
},
"nullable": [
false
]
},
"hash": "1bb4a0ea9450533e5d08db156a7440258c22cc6d14848d247a322a1ff2c73810"
}

View File

@ -0,0 +1,20 @@
{
"db_name": "SQLite",
"query": "SELECT CREDENTIAL FROM CREDENTIALS WHERE USER_ID = $1;",
"describe": {
"columns": [
{
"name": "CREDENTIAL",
"ordinal": 0,
"type_info": "Text"
}
],
"parameters": {
"Right": 1
},
"nullable": [
false
]
},
"hash": "1ea5285096550f447c4ec8eed2aaf623fe2887015ea22f1579f477bc1aeb7a21"
}

View File

@ -0,0 +1,20 @@
{
"db_name": "SQLite",
"query": "SELECT credential FROM CREDENTIALS WHERE user_id = $1;",
"describe": {
"columns": [
{
"name": "CREDENTIAL",
"ordinal": 0,
"type_info": "Text"
}
],
"parameters": {
"Right": 1
},
"nullable": [
false
]
},
"hash": "390c853d2fe8777bebbbe3ed5dae7f25415da7e916934e3cf983a9f590804a41"
}

View File

@ -0,0 +1,12 @@
{
"db_name": "SQLite",
"query": "INSERT INTO CREDENTIALS(user_id, credential) VALUES($1, $2);",
"describe": {
"columns": [],
"parameters": {
"Right": 2
},
"nullable": []
},
"hash": "62612ca2817b12ef492a450be661ad58037c7d5b433cba9855fff21461b9e7fc"
}

View File

@ -1,26 +0,0 @@
{
"db_name": "SQLite",
"query": "\nSELECT NAME, KEY FROM USERS WHERE NAME = ?1 AND KEY = ?2\n ",
"describe": {
"columns": [
{
"name": "NAME",
"ordinal": 0,
"type_info": "Text"
},
{
"name": "KEY",
"ordinal": 1,
"type_info": "Text"
}
],
"parameters": {
"Right": 2
},
"nullable": [
false,
false
]
},
"hash": "6b379715355f3443d66111020be5c3c7ec2a57c9789d04485b998fd934d6b0a8"
}

View File

@ -0,0 +1,12 @@
{
"db_name": "SQLite",
"query": "UPDATE CREDENTIALS SET credential = $1 WHERE user_id = $2 AND credential = $3;",
"describe": {
"columns": [],
"parameters": {
"Right": 3
},
"nullable": []
},
"hash": "810f3f513dd59e14557979e50b72b0b40fa6a7ea682a74a806ebf2be30c6e4cc"
}

View File

@ -0,0 +1,20 @@
{
"db_name": "SQLite",
"query": "SELECT COUNT(KEY) AS count FROM users WHERE KEY = $1;",
"describe": {
"columns": [
{
"name": "count",
"ordinal": 0,
"type_info": "Int"
}
],
"parameters": {
"Right": 1
},
"nullable": [
false
]
},
"hash": "db45619d7a6d50b845ed8f31f58b525ae6b4de88eba24b0d4126ba54d07b0731"
}

View File

@ -0,0 +1,20 @@
{
"db_name": "SQLite",
"query": "SELECT NAME FROM users WHERE KEY = $1;",
"describe": {
"columns": [
{
"name": "NAME",
"ordinal": 0,
"type_info": "Text"
}
],
"parameters": {
"Right": 1
},
"nullable": [
false
]
},
"hash": "e8b97d3910dbfb9ea1dd5d6d8a105886df0573e55c9ac62ac1cc8ecd15ae9801"
}

View File

@ -0,0 +1,20 @@
{
"db_name": "SQLite",
"query": "SELECT KEY FROM users WHERE NAME = $1;",
"describe": {
"columns": [
{
"name": "KEY",
"ordinal": 0,
"type_info": "Text"
}
],
"parameters": {
"Right": 1
},
"nullable": [
false
]
},
"hash": "f1948f31683e327633ad9ae6b56764a0965331c825faf0a86ca18331e4d8ebdf"
}

402
Cargo.lock generated
View File

@ -19,9 +19,9 @@ checksum = "f26201604c87b1e01bd3d98f8d5d9a8fcbb815e8cedb41ffccbeb4bf593a35fe"
[[package]] [[package]]
name = "ahash" name = "ahash"
version = "0.8.6" version = "0.8.11"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "91429305e9f0a25f6205c5b8e0d2db09e0708a7a6df0f42212bb56c32c8ac97a" checksum = "e89da841a80418a9b391ebaea17f5c112ffaaa96f621d2c285b5174da76b9011"
dependencies = [ dependencies = [
"cfg-if", "cfg-if",
"getrandom", "getrandom",
@ -97,6 +97,45 @@ dependencies = [
"toml", "toml",
] ]
[[package]]
name = "asn1-rs"
version = "0.3.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "30ff05a702273012438132f449575dbc804e27b2f3cbe3069aa237d26c98fa33"
dependencies = [
"asn1-rs-derive",
"asn1-rs-impl",
"displaydoc",
"nom 7.1.3",
"num-traits",
"rusticata-macros",
"thiserror",
"time",
]
[[package]]
name = "asn1-rs-derive"
version = "0.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "db8b7511298d5b7784b40b092d9e9dcd3a627a5707e4b5e507931ab0d44eeebf"
dependencies = [
"proc-macro2",
"quote",
"syn 1.0.109",
"synstructure",
]
[[package]]
name = "asn1-rs-impl"
version = "0.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2777730b2039ac0f95f093556e61b6d26cebed5393ca6f152717777cec3a42ed"
dependencies = [
"proc-macro2",
"quote",
"syn 1.0.109",
]
[[package]] [[package]]
name = "async-trait" name = "async-trait"
version = "0.1.74" version = "0.1.74"
@ -105,7 +144,7 @@ checksum = "a66537f1bb974b254c98ed142ff995236e81b9d0fe4db0575f46612cb15eb0f9"
dependencies = [ dependencies = [
"proc-macro2", "proc-macro2",
"quote", "quote",
"syn 2.0.38", "syn 2.0.52",
] ]
[[package]] [[package]]
@ -126,6 +165,7 @@ dependencies = [
"minijinja", "minijinja",
"once_cell", "once_cell",
"serde", "serde",
"serde_json",
"sqlx", "sqlx",
"tokio", "tokio",
"totp-rs", "totp-rs",
@ -135,6 +175,7 @@ dependencies = [
"tracing", "tracing",
"tracing-subscriber", "tracing-subscriber",
"uuid", "uuid",
"webauthn-rs",
] ]
[[package]] [[package]]
@ -151,6 +192,7 @@ checksum = "1236b4b292f6c4d6dc34604bb5120d85c3fe1d1aa596bd5cc52ca054d13e7b9e"
dependencies = [ dependencies = [
"async-trait", "async-trait",
"axum-core", "axum-core",
"axum-macros",
"bytes", "bytes",
"futures-util", "futures-util",
"http", "http",
@ -198,6 +240,18 @@ dependencies = [
"tracing", "tracing",
] ]
[[package]]
name = "axum-macros"
version = "0.4.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "00c055ee2d014ae5981ce1016374e8213682aa14d9bf40e48ab48b5f3ef20eaa"
dependencies = [
"heck",
"proc-macro2",
"quote",
"syn 2.0.52",
]
[[package]] [[package]]
name = "backtrace" name = "backtrace"
version = "0.3.69" version = "0.3.69"
@ -219,6 +273,12 @@ version = "0.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "23ce669cd6c8588f79e15cf450314f9638f967fc5770ff1c7c1deb0925ea7cfa" checksum = "23ce669cd6c8588f79e15cf450314f9638f967fc5770ff1c7c1deb0925ea7cfa"
[[package]]
name = "base64"
version = "0.13.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9e1b586273c5702936fe7b7d6896644d8be71e6314cfe09d3167c95f712589e8"
[[package]] [[package]]
name = "base64" name = "base64"
version = "0.21.5" version = "0.21.5"
@ -237,6 +297,17 @@ version = "1.6.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8c3c1a368f70d6cf7302d78f8f7093da241fb8e8807c05cc9e51a125895a6d5b" checksum = "8c3c1a368f70d6cf7302d78f8f7093da241fb8e8807c05cc9e51a125895a6d5b"
[[package]]
name = "base64urlsafedata"
version = "0.1.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "18b3d30abb74120a9d5267463b9e0045fdccc4dd152e7249d966612dc1721384"
dependencies = [
"base64 0.21.5",
"serde",
"serde_json",
]
[[package]] [[package]]
name = "bitflags" name = "bitflags"
version = "1.3.2" version = "1.3.2"
@ -300,6 +371,23 @@ version = "1.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd"
[[package]]
name = "compact_jwt"
version = "0.2.10"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7aa76ef19968577838a34d02848136bb9b6bdbfd7675fb968fe9c931bc434b33"
dependencies = [
"base64 0.13.1",
"base64urlsafedata",
"hex",
"openssl",
"serde",
"serde_json",
"tracing",
"url",
"uuid",
]
[[package]] [[package]]
name = "const-oid" name = "const-oid"
version = "0.9.5" version = "0.9.5"
@ -376,6 +464,12 @@ dependencies = [
"typenum", "typenum",
] ]
[[package]]
name = "data-encoding"
version = "2.5.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7e962a19be5cfc3f3bf6dd8f61eb50107f356ad6270fbb3ed41476571db78be5"
[[package]] [[package]]
name = "der" name = "der"
version = "0.7.8" version = "0.7.8"
@ -387,6 +481,20 @@ dependencies = [
"zeroize", "zeroize",
] ]
[[package]]
name = "der-parser"
version = "7.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "fe398ac75057914d7d07307bf67dc7f3f574a26783b4fc7805a20ffa9f506e82"
dependencies = [
"asn1-rs",
"displaydoc",
"nom 7.1.3",
"num-bigint",
"num-traits",
"rusticata-macros",
]
[[package]] [[package]]
name = "deranged" name = "deranged"
version = "0.3.9" version = "0.3.9"
@ -409,6 +517,17 @@ dependencies = [
"subtle", "subtle",
] ]
[[package]]
name = "displaydoc"
version = "0.2.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "487585f4d0c6655fe74905e2504d8ad6908e4db67f744eb140876906c2f3175d"
dependencies = [
"proc-macro2",
"quote",
"syn 2.0.52",
]
[[package]] [[package]]
name = "dotenvy" name = "dotenvy"
version = "0.15.7" version = "0.15.7"
@ -486,6 +605,21 @@ version = "1.0.7"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1" checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1"
[[package]]
name = "foreign-types"
version = "0.3.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f6f339eb8adc052cd2ca78910fda869aefa38d22d5cb648e6485e4d3fc06f3b1"
dependencies = [
"foreign-types-shared",
]
[[package]]
name = "foreign-types-shared"
version = "0.1.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "00b0228411908ca8685dba7fc2cdd70ec9990a6e753e89b6ac91a84c40fbaf4b"
[[package]] [[package]]
name = "form_urlencoded" name = "form_urlencoded"
version = "1.2.0" version = "1.2.0"
@ -567,7 +701,7 @@ checksum = "87750cf4b7a4c0625b1529e4c543c2182106e4dedc60a2a6455e00d212c489ac"
dependencies = [ dependencies = [
"proc-macro2", "proc-macro2",
"quote", "quote",
"syn 2.0.38", "syn 2.0.52",
] ]
[[package]] [[package]]
@ -645,6 +779,12 @@ dependencies = [
"tracing", "tracing",
] ]
[[package]]
name = "half"
version = "1.8.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1b43ede17f21864e81be2fa654110bf1e793774238d86ef8555c37e6519c0403"
[[package]] [[package]]
name = "hashbrown" name = "hashbrown"
version = "0.14.2" version = "0.14.2"
@ -871,9 +1011,9 @@ checksum = "4ec2a862134d2a7d32d7983ddcdd1c4923530833c9f2ea1a44fc5fa473989058"
[[package]] [[package]]
name = "libsqlite3-sys" name = "libsqlite3-sys"
version = "0.26.0" version = "0.27.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "afc22eff61b133b115c6e8c74e818c628d6d5e7a502afea6f64dee076dd94326" checksum = "cf4e226dcd58b4be396f7bd3c20da8fdee2911400705297ba7d2d7cc2c30f716"
dependencies = [ dependencies = [
"cc", "cc",
"pkg-config", "pkg-config",
@ -1008,6 +1148,17 @@ dependencies = [
"winapi", "winapi",
] ]
[[package]]
name = "num-bigint"
version = "0.4.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "608e7659b5c3d7cba262d894801b9ec9d00de989e8a82bd4bef91d08da45cdc0"
dependencies = [
"autocfg",
"num-integer",
"num-traits",
]
[[package]] [[package]]
name = "num-bigint-dig" name = "num-bigint-dig"
version = "0.8.4" version = "0.8.4"
@ -1075,12 +1226,59 @@ dependencies = [
"memchr", "memchr",
] ]
[[package]]
name = "oid-registry"
version = "0.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "38e20717fa0541f39bd146692035c37bedfa532b3e5071b35761082407546b2a"
dependencies = [
"asn1-rs",
]
[[package]] [[package]]
name = "once_cell" name = "once_cell"
version = "1.18.0" version = "1.18.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "dd8b5dd2ae5ed71462c540258bedcb51965123ad7e7ccf4b9a8cafaa4a63576d" checksum = "dd8b5dd2ae5ed71462c540258bedcb51965123ad7e7ccf4b9a8cafaa4a63576d"
[[package]]
name = "openssl"
version = "0.10.64"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "95a0481286a310808298130d22dd1fef0fa571e05a8f44ec801801e84b216b1f"
dependencies = [
"bitflags 2.4.1",
"cfg-if",
"foreign-types",
"libc",
"once_cell",
"openssl-macros",
"openssl-sys",
]
[[package]]
name = "openssl-macros"
version = "0.1.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a948666b637a0f465e8564c73e89d4dde00d72d4d473cc972f390fc3dcee7d9c"
dependencies = [
"proc-macro2",
"quote",
"syn 2.0.52",
]
[[package]]
name = "openssl-sys"
version = "0.9.101"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "dda2b0f344e78efc2facf7d195d098df0dd72151b26ab98da807afc26c198dff"
dependencies = [
"cc",
"libc",
"pkg-config",
"vcpkg",
]
[[package]] [[package]]
name = "overload" name = "overload"
version = "0.1.1" version = "0.1.1"
@ -1148,7 +1346,7 @@ checksum = "4359fd9c9171ec6e8c62926d6faaf553a8dc3f64e1507e76da7911b4f6a04405"
dependencies = [ dependencies = [
"proc-macro2", "proc-macro2",
"quote", "quote",
"syn 2.0.38", "syn 2.0.52",
] ]
[[package]] [[package]]
@ -1204,18 +1402,18 @@ checksum = "5b40af805b3121feab8a3c29f04d8ad262fa8e0561883e7653e024ae4479e6de"
[[package]] [[package]]
name = "proc-macro2" name = "proc-macro2"
version = "1.0.69" version = "1.0.79"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "134c189feb4956b20f6f547d2cf727d4c0fe06722b20a0eec87ed445a97f92da" checksum = "e835ff2298f5721608eb1a980ecaee1aef2c132bf95ecc026a11b7bf3c01c02e"
dependencies = [ dependencies = [
"unicode-ident", "unicode-ident",
] ]
[[package]] [[package]]
name = "quote" name = "quote"
version = "1.0.33" version = "1.0.35"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5267fca4496028628a95160fc423a33e8b2e6af8a5302579e322e4b520293cae" checksum = "291ec9ab5efd934aaf503a6466c5d5251535d108ee747472c3977cc5acc868ef"
dependencies = [ dependencies = [
"proc-macro2", "proc-macro2",
] ]
@ -1335,6 +1533,15 @@ version = "0.1.23"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d626bb9dae77e28219937af045c257c28bfd3f69333c512553507f5f9798cb76" checksum = "d626bb9dae77e28219937af045c257c28bfd3f69333c512553507f5f9798cb76"
[[package]]
name = "rusticata-macros"
version = "4.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "faf0c4a6ece9950b9abdb62b1cfcf2a68b3b67a10ba445b3bb85be2a293d0632"
dependencies = [
"nom 7.1.3",
]
[[package]] [[package]]
name = "rustix" name = "rustix"
version = "0.38.21" version = "0.38.21"
@ -1368,29 +1575,39 @@ checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49"
[[package]] [[package]]
name = "serde" name = "serde"
version = "1.0.190" version = "1.0.197"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "91d3c334ca1ee894a2c6f6ad698fe8c435b76d504b13d436f0685d648d6d96f7" checksum = "3fb1c873e1b9b056a4dc4c0c198b24c3ffa059243875552b2bd0933b1aee4ce2"
dependencies = [ dependencies = [
"serde_derive", "serde_derive",
] ]
[[package]] [[package]]
name = "serde_derive" name = "serde_cbor_2"
version = "1.0.190" version = "0.12.0-dev"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "67c5609f394e5c2bd7fc51efda478004ea80ef42fee983d5c67a65e34f32c0e3" checksum = "b46d75f449e01f1eddbe9b00f432d616fbbd899b809c837d0fbc380496a0dd55"
dependencies = [
"half",
"serde",
]
[[package]]
name = "serde_derive"
version = "1.0.197"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7eb0b34b42edc17f6b7cac84a52a1c5f0e1bb2227e997ca9011ea3dd34e8610b"
dependencies = [ dependencies = [
"proc-macro2", "proc-macro2",
"quote", "quote",
"syn 2.0.38", "syn 2.0.52",
] ]
[[package]] [[package]]
name = "serde_json" name = "serde_json"
version = "1.0.107" version = "1.0.114"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6b420ce6e3d8bd882e9b243c6eed35dbc9a6110c9769e74b584e0d68d1f20c65" checksum = "c5f09b1bd632ef549eaa9f60a1f8de742bdbc698e6cee2095fc84dde5f549ae0"
dependencies = [ dependencies = [
"itoa", "itoa",
"ryu", "ryu",
@ -1532,9 +1749,9 @@ dependencies = [
[[package]] [[package]]
name = "sqlx" name = "sqlx"
version = "0.7.2" version = "0.7.4"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0e50c216e3624ec8e7ecd14c6a6a6370aad6ee5d8cfc3ab30b5162eeeef2ed33" checksum = "c9a2ccff1a000a5a59cd33da541d9f2fdcd9e6e8229cc200565942bff36d0aaa"
dependencies = [ dependencies = [
"sqlx-core", "sqlx-core",
"sqlx-macros", "sqlx-macros",
@ -1545,9 +1762,9 @@ dependencies = [
[[package]] [[package]]
name = "sqlx-core" name = "sqlx-core"
version = "0.7.2" version = "0.7.4"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8d6753e460c998bbd4cd8c6f0ed9a64346fcca0723d6e75e52fdc351c5d2169d" checksum = "24ba59a9342a3d9bab6c56c118be528b27c9b60e490080e9711a04dccac83ef6"
dependencies = [ dependencies = [
"ahash", "ahash",
"atoi", "atoi",
@ -1555,7 +1772,6 @@ dependencies = [
"bytes", "bytes",
"crc", "crc",
"crossbeam-queue", "crossbeam-queue",
"dotenvy",
"either", "either",
"event-listener", "event-listener",
"futures-channel", "futures-channel",
@ -1585,9 +1801,9 @@ dependencies = [
[[package]] [[package]]
name = "sqlx-macros" name = "sqlx-macros"
version = "0.7.2" version = "0.7.4"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9a793bb3ba331ec8359c1853bd39eed32cdd7baaf22c35ccf5c92a7e8d1189ec" checksum = "4ea40e2345eb2faa9e1e5e326db8c34711317d2b5e08d0d5741619048a803127"
dependencies = [ dependencies = [
"proc-macro2", "proc-macro2",
"quote", "quote",
@ -1598,9 +1814,9 @@ dependencies = [
[[package]] [[package]]
name = "sqlx-macros-core" name = "sqlx-macros-core"
version = "0.7.2" version = "0.7.4"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0a4ee1e104e00dedb6aa5ffdd1343107b0a4702e862a84320ee7cc74782d96fc" checksum = "5833ef53aaa16d860e92123292f1f6a3d53c34ba8b1969f152ef1a7bb803f3c8"
dependencies = [ dependencies = [
"dotenvy", "dotenvy",
"either", "either",
@ -1623,9 +1839,9 @@ dependencies = [
[[package]] [[package]]
name = "sqlx-mysql" name = "sqlx-mysql"
version = "0.7.2" version = "0.7.4"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "864b869fdf56263f4c95c45483191ea0af340f9f3e3e7b4d57a61c7c87a970db" checksum = "1ed31390216d20e538e447a7a9b959e06ed9fc51c37b514b46eb758016ecd418"
dependencies = [ dependencies = [
"atoi", "atoi",
"base64 0.21.5", "base64 0.21.5",
@ -1665,9 +1881,9 @@ dependencies = [
[[package]] [[package]]
name = "sqlx-postgres" name = "sqlx-postgres"
version = "0.7.2" version = "0.7.4"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "eb7ae0e6a97fb3ba33b23ac2671a5ce6e3cabe003f451abd5a56e7951d975624" checksum = "7c824eb80b894f926f89a0b9da0c7f435d27cdd35b8c655b114e58223918577e"
dependencies = [ dependencies = [
"atoi", "atoi",
"base64 0.21.5", "base64 0.21.5",
@ -1692,7 +1908,6 @@ dependencies = [
"rand", "rand",
"serde", "serde",
"serde_json", "serde_json",
"sha1",
"sha2", "sha2",
"smallvec", "smallvec",
"sqlx-core", "sqlx-core",
@ -1704,9 +1919,9 @@ dependencies = [
[[package]] [[package]]
name = "sqlx-sqlite" name = "sqlx-sqlite"
version = "0.7.2" version = "0.7.4"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d59dc83cf45d89c555a577694534fcd1b55c545a816c816ce51f20bbe56a4f3f" checksum = "b244ef0a8414da0bed4bb1910426e890b19e5e9bccc27ada6b797d05c55ae0aa"
dependencies = [ dependencies = [
"atoi", "atoi",
"flume", "flume",
@ -1722,6 +1937,7 @@ dependencies = [
"sqlx-core", "sqlx-core",
"tracing", "tracing",
"url", "url",
"urlencoding",
] ]
[[package]] [[package]]
@ -1760,9 +1976,9 @@ dependencies = [
[[package]] [[package]]
name = "syn" name = "syn"
version = "2.0.38" version = "2.0.52"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e96b79aaa137db8f61e26363a0c9b47d8b4ec75da28b7d1d614c2303e232408b" checksum = "b699d15b36d1f02c3e7c69f8ffef53de37aefae075d8488d4ba1a7788d574a07"
dependencies = [ dependencies = [
"proc-macro2", "proc-macro2",
"quote", "quote",
@ -1775,6 +1991,18 @@ version = "0.1.2"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2047c6ded9c721764247e62cd3b03c09ffc529b2ba5b10ec482ae507a4a70160" checksum = "2047c6ded9c721764247e62cd3b03c09ffc529b2ba5b10ec482ae507a4a70160"
[[package]]
name = "synstructure"
version = "0.12.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f36bdaa60a83aca3921b5259d5400cbf5e90fc51931376a9bd4a0eb79aa7210f"
dependencies = [
"proc-macro2",
"quote",
"syn 1.0.109",
"unicode-xid",
]
[[package]] [[package]]
name = "tap" name = "tap"
version = "1.0.1" version = "1.0.1"
@ -1811,7 +2039,7 @@ checksum = "266b2e40bc00e5a6c09c3584011e08b06f123c00362c92b975ba9843aaaa14b8"
dependencies = [ dependencies = [
"proc-macro2", "proc-macro2",
"quote", "quote",
"syn 2.0.38", "syn 2.0.52",
] ]
[[package]] [[package]]
@ -1895,7 +2123,7 @@ checksum = "630bdcf245f78637c13ec01ffae6187cca34625e8c63150d424b59e55af2675e"
dependencies = [ dependencies = [
"proc-macro2", "proc-macro2",
"quote", "quote",
"syn 2.0.38", "syn 2.0.52",
] ]
[[package]] [[package]]
@ -2078,7 +2306,7 @@ checksum = "34704c8d6ebcbc939824180af020566b01a7c01f80641264eba0999f6c2b6be7"
dependencies = [ dependencies = [
"proc-macro2", "proc-macro2",
"quote", "quote",
"syn 2.0.38", "syn 2.0.52",
] ]
[[package]] [[package]]
@ -2153,6 +2381,12 @@ version = "1.10.1"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1dd624098567895118886609431a7c3b8f516e41d30e0643f03d94592a147e36" checksum = "1dd624098567895118886609431a7c3b8f516e41d30e0643f03d94592a147e36"
[[package]]
name = "unicode-xid"
version = "0.2.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f962df74c8c05a667b5ee8bcf162993134c104e96440b663c8daa176dc772d8c"
[[package]] [[package]]
name = "unicode_categories" name = "unicode_categories"
version = "0.1.1" version = "0.1.1"
@ -2168,8 +2402,15 @@ dependencies = [
"form_urlencoded", "form_urlencoded",
"idna", "idna",
"percent-encoding", "percent-encoding",
"serde",
] ]
[[package]]
name = "urlencoding"
version = "2.1.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "daf8dba3b7eb870caf1ddeed7bc9d2a049f3cfdfae7cb521b087cc33ae4c49da"
[[package]] [[package]]
name = "uuid" name = "uuid"
version = "1.5.0" version = "1.5.0"
@ -2178,6 +2419,7 @@ checksum = "88ad59a7560b41a70d191093a945f0b87bc1deeda46fb237479708a1d6b6cdfc"
dependencies = [ dependencies = [
"getrandom", "getrandom",
"rand", "rand",
"serde",
] ]
[[package]] [[package]]
@ -2204,6 +2446,56 @@ version = "0.11.0+wasi-snapshot-preview1"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423"
[[package]]
name = "webauthn-rs"
version = "0.4.8"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2db00711c712414e93b019c4596315085792215bc2ac2d5872f9e8913b0a6316"
dependencies = [
"base64urlsafedata",
"serde",
"tracing",
"url",
"uuid",
"webauthn-rs-core",
]
[[package]]
name = "webauthn-rs-core"
version = "0.4.9"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "294c78c83f12153a51e1cf1e6970b5da1397645dada39033a9c3173a8fc4fc2b"
dependencies = [
"base64 0.13.1",
"base64urlsafedata",
"compact_jwt",
"der-parser",
"nom 7.1.3",
"openssl",
"rand",
"serde",
"serde_cbor_2",
"serde_json",
"thiserror",
"tracing",
"url",
"uuid",
"webauthn-rs-proto",
"x509-parser",
]
[[package]]
name = "webauthn-rs-proto"
version = "0.4.9"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d24e638361a63ba5c0a0be6a60229490fcdf33740ed63df5bb6bdb627b52a138"
dependencies = [
"base64urlsafedata",
"serde",
"serde_json",
"url",
]
[[package]] [[package]]
name = "whoami" name = "whoami"
version = "1.4.1" version = "1.4.1"
@ -2305,23 +2597,41 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "85e60b0d1b5f99db2556934e21937020776a5d31520bf169e851ac44e6420214" checksum = "85e60b0d1b5f99db2556934e21937020776a5d31520bf169e851ac44e6420214"
[[package]] [[package]]
name = "zerocopy" name = "x509-parser"
version = "0.7.18" version = "0.13.2"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ede7d7c7970ca2215b8c1ccf4d4f354c4733201dfaaba72d44ae5b37472e4901" checksum = "9fb9bace5b5589ffead1afb76e43e34cff39cd0f3ce7e170ae0c29e53b88eb1c"
dependencies = [
"asn1-rs",
"base64 0.13.1",
"data-encoding",
"der-parser",
"lazy_static",
"nom 7.1.3",
"oid-registry",
"rusticata-macros",
"thiserror",
"time",
]
[[package]]
name = "zerocopy"
version = "0.7.32"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "74d4d3961e53fa4c9a25a8637fc2bfaf2595b3d3ae34875568a5cf64787716be"
dependencies = [ dependencies = [
"zerocopy-derive", "zerocopy-derive",
] ]
[[package]] [[package]]
name = "zerocopy-derive" name = "zerocopy-derive"
version = "0.7.18" version = "0.7.32"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4b27b1bb92570f989aac0ab7e9cbfbacdd65973f7ee920d9f0e71ebac878fd0b" checksum = "9ce1b18ccd8e73a9321186f97e46f9f04b778851177567b1975109d26a08d2a6"
dependencies = [ dependencies = [
"proc-macro2", "proc-macro2",
"quote", "quote",
"syn 2.0.38", "syn 2.0.52",
] ]
[[package]] [[package]]

View File

@ -6,9 +6,9 @@ edition = "2021"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[dependencies] [dependencies]
axum = {version = "0.7.4", features = [ "default", "form" ]} axum = {version = "0.7.4", features = [ "default", "form", "tracing", "macros" ]}
tokio = { version = "1.0", features = ["full"] } tokio = { version = "1.0", features = ["full"] }
sqlx = { version = "0.7", features = ["runtime-tokio", "sqlite"] } sqlx = { version = "0.7.4", features = ["runtime-tokio", "sqlite"] }
tower-cookies = "0.10.0" tower-cookies = "0.10.0"
serde = "1.0.190" serde = "1.0.190"
uuid = { version = "1.5.0", features = ["v4", "fast-rng"] } uuid = { version = "1.5.0", features = ["v4", "fast-rng"] }
@ -20,3 +20,5 @@ askama = "0.10"
minijinja = "1.0.9" minijinja = "1.0.9"
once_cell = "1.18.0" once_cell = "1.18.0"
tower-sessions = "0.11.0" tower-sessions = "0.11.0"
webauthn-rs = { version = "0.4.8", features = [ "danger-allow-state-serialisation" ]}
serde_json = "1.0.114"

View File

@ -0,0 +1,22 @@
use once_cell::sync::Lazy;
use std::env;
pub const COOKIE_NAME: Lazy<String> =
Lazy::new(|| env::var("COOKIE_NAME").unwrap_or("aaron_auth".to_string()));
pub const SESSION_ACTIVE_TIME: Lazy<u64> = Lazy::new(|| {
env::var("SESSION_ACTIVE_TIME")
.ok()
.and_then(|value| value.parse().ok())
.unwrap_or(600)
});
pub const COOKIE_DOMAIN: Lazy<String> =
Lazy::new(|| env::var("DOMAIN").ok().unwrap_or(".aaronhu.cn".to_owned()));
pub const LOGIN_PAGE_HTML: &str = include_str!("../loginpage.html");
pub const PORT: Lazy<String> = Lazy::new(|| env::var("PORT").unwrap_or("3000".to_string()));
pub const DATABASE_URL: Lazy<String> =
Lazy::new(|| env::var("DATABASE_URL").unwrap_or("".to_owned()));
pub const RP_ID: Lazy<String> =
Lazy::new(|| env::var("RP_ID").unwrap_or("localhost".to_owned()));
pub const RP_ORIGIN: Lazy<String> =
Lazy::new(|| env::var("RP_ORIGIN").unwrap_or("http://localhost:8080".to_owned()));
pub const RP_NAME: Lazy<String> =
Lazy::new(|| env::var("RP_NAME").unwrap_or("Localhost".to_owned()));

View File

@ -1,14 +1,52 @@
use serde::Deserialize; use serde::Deserialize;
use std::time::Instant; use sqlx::SqlitePool;
use std::collections::HashMap; use std::collections::HashMap;
use std::sync::Arc;
use std::time::Instant;
use tokio::sync::Mutex; use tokio::sync::Mutex;
use uuid::Uuid; use uuid::Uuid;
use webauthn_rs::prelude::Url;
use webauthn_rs::{Webauthn, WebauthnBuilder};
use crate::config::{DATABASE_URL, RP_ID, RP_NAME, RP_ORIGIN};
pub struct ServerState { pub struct ServerState {
pub db: sqlx::Pool<sqlx::Sqlite>, pub db: sqlx::Pool<sqlx::Sqlite>,
pub session: Mutex<HashMap<Uuid, Instant>>, pub session: Mutex<HashMap<Uuid, Instant>>,
pub webauthn: Arc<Webauthn>,
// started: Instant, // started: Instant,
} }
impl ServerState {
pub async fn new() -> Self {
// Effective domain name.
let rp_id = RP_ID;
// Url containing the effective domain name
// MUST include the port number!
let rp_origin = Url::parse(&RP_ORIGIN).expect("Invalid URL");
let builder = WebauthnBuilder::new(&rp_id, &rp_origin).expect("Invalid configuration");
// Now, with the builder you can define other options.
// Set a "nice" relying party name. Has no security properties and
// may be changed in the future.
let _RP_NAME = RP_NAME.to_string();
let builder = builder.rp_name(&_RP_NAME);
// Consume the builder and create our webauthn instance.
let webauthn = Arc::new(builder.build().expect("Invalid configuration"));
let session = Mutex::new(HashMap::new());
let db = SqlitePool::connect(&DATABASE_URL)
.await
.expect("DB OPEN FAILURE"); // our router
ServerState {
db,
session,
webauthn,
}
}
}
#[derive(Deserialize, sqlx::FromRow, Debug)] #[derive(Deserialize, sqlx::FromRow, Debug)]
pub struct UserLoginForm { pub struct UserLoginForm {
#[sqlx(rename = "NAME")] #[sqlx(rename = "NAME")]
@ -23,4 +61,4 @@ pub struct User {
pub username: String, pub username: String,
#[sqlx(rename = "KEY")] #[sqlx(rename = "KEY")]
pub uid: String, pub uid: String,
} }

View File

@ -1,36 +1,22 @@
use axum::extract::Query;
use axum::http::{HeaderMap, HeaderValue};
use axum::response::{Html, Redirect};
use axum::{extract::State, http::StatusCode, response::IntoResponse, routing::get, Form, Router}; use axum::{routing::{get, post, Route}, Router};
use entities::*; use entities::*;
use minijinja::{context, Environment};
use once_cell::sync::Lazy; use services::{auth::{self, finish_authentication, start_authentication}, gc_task, login, login_page};
use services::{auth, gc_task, login, login_page, login_with_passkey};
use sqlx::sqlite::SqlitePool;
use std::net::SocketAddr; use std::net::SocketAddr;
use std::sync::Arc; use std::sync::Arc;
use std::time::{Duration, Instant}; use tower_cookies::{CookieManagerLayer};
use std::{borrow::BorrowMut, env};
use std::{collections::HashMap, str::FromStr};
use tower_cookies::{Cookie, CookieManagerLayer, Cookies};
use tower_http::trace::{self, TraceLayer}; use tower_http::trace::{self, TraceLayer};
use tower_sessions::{Expiry, MemoryStore, Session, SessionManagerLayer}; use tower_sessions::{Expiry, MemoryStore, SessionManagerLayer};
use tracing::Level; use tracing::Level;
use uuid::Uuid;
pub mod config;
pub mod controllers; pub mod controllers;
pub mod entities; pub mod entities;
pub mod services; pub mod services;
static COOKIE_NAME: Lazy<String> = use config::{PORT};
Lazy::new(|| env::var("COOKIE_NAME").unwrap_or("aaron_auth".to_string()));
const SESSION_ACTIVE_TIME: Lazy<u64> = Lazy::new(|| {
env::var("SESSION_ACTIVE_TIME")
.ok()
.and_then(|value| value.parse().ok())
.unwrap_or(600)
});
const COOKIE_DOMAIN: Lazy<String> =
Lazy::new(|| env::var("DOMAIN").ok().unwrap_or(".aaronhu.cn".to_owned()));
const LOGIN_PAGE_HTML: &str = include_str!("../loginpage.html");
#[tokio::main] #[tokio::main]
async fn main() { async fn main() {
// 初始化日志记录器 // 初始化日志记录器
@ -39,17 +25,15 @@ async fn main() {
let session_layer = SessionManagerLayer::new(session_store) let session_layer = SessionManagerLayer::new(session_store)
.with_secure(false) .with_secure(false)
.with_expiry(Expiry::OnSessionEnd); .with_expiry(Expiry::OnSessionEnd);
let pool = SqlitePool::connect(&env::var("DATABASE_URL").expect("DB URL NOT SPECIFIED")) let state = Arc::new(ServerState::new().await);
.await
.expect("DB OPEN FAILURE"); // our router
let state = Arc::new(ServerState {
db: pool,
session: HashMap::new().into(),
// started: std::time::Instant::now(),
});
let app = Router::new() let app = Router::new()
.route("/auth", get(auth)) // http://127.0.0.1:3000 .route("/auth", get(crate::services::auth_otp)) // http://127.0.0.1:3000
.route("/login", get(login_page).post(login)) .route("/login", get(login_page).post(login))
.route("/register_start/:username", post(auth::start_register))
.route("/register_finish/:username", post(auth::finish_register))
.route("/webauthn_login_start/:username", post(start_authentication))
.route("/webauthn_login_finish/:username", post(finish_authentication))
.layer( .layer(
TraceLayer::new_for_http() TraceLayer::new_for_http()
.make_span_with(trace::DefaultMakeSpan::new().level(Level::INFO)) .make_span_with(trace::DefaultMakeSpan::new().level(Level::INFO))
@ -58,11 +42,11 @@ async fn main() {
.with_state(state.clone()) .with_state(state.clone())
.layer(CookieManagerLayer::new()) .layer(CookieManagerLayer::new())
.layer(session_layer); .layer(session_layer);
let port = env::var("PORT").unwrap_or("3000".to_string()); let aaronhu = Router::new().nest("/aaron/", app);
let port = port.parse::<u16>().unwrap_or(3000); let port = PORT.parse::<u16>().unwrap_or(3000);
let addr = SocketAddr::from(([127, 0, 0, 1], port)); let addr = SocketAddr::from(([127, 0, 0, 1], port));
// run it with hyper on localhost:3000 // run it with hyper on localhost:3000
tokio::spawn(gc_task(state.clone())); tokio::spawn(gc_task(state.clone()));
let listener = tokio::net::TcpListener::bind(addr).await.unwrap(); let listener = tokio::net::TcpListener::bind(addr).await.unwrap();
axum::serve(listener, app).await.unwrap(); axum::serve(listener, aaronhu).await.unwrap();
} }

413
src/services/auth.rs Normal file
View File

@ -0,0 +1,413 @@
#![allow(non_snake_case)]
use std::collections::HashMap;
use std::sync::Arc;
use std::time::Duration;
use axum::debug_handler;
use axum::extract::State;
use axum::{
extract::{Json, Path},
http::StatusCode,
response::IntoResponse,
};
use std::time::Instant;
use tower_cookies::Cookies;
use tower_sessions::Session;
use tracing::*;
/*
* Webauthn RS auth handlers.
* These files use webauthn to process the data received from each route, and are closely tied to axum
*/
// 1. Import the prelude - this contains everything needed for the server to function.
use webauthn_rs::prelude::*;
use crate::config::{COOKIE_NAME, SESSION_ACTIVE_TIME};
use crate::ServerState;
// 2. The first step a client (user) will carry out is requesting a credential to be
// registered. We need to provide a challenge for this. The work flow will be:
//
// ┌───────────────┐ ┌───────────────┐ ┌───────────────┐
// │ Authenticator │ │ Browser │ │ Site │
// └───────────────┘ └───────────────┘ └───────────────┘
// │ │ │
// │ │ 1. Start Reg │
// │ │─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─▶│
// │ │ │
// │ │ 2. Challenge │
// │ │◀ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ┤
// │ │ │
// │ 3. Select Token │ │
// ─ ─ ─│◀ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─│ │
// 4. Verify │ │ │ │
// │ 4. Yield PubKey │ │
// └ ─ ─▶│─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─▶ │
// │ │ │
// │ │ 5. Send Reg Opts │
// │ │─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─▶│─ ─ ─
// │ │ │ │ 5. Verify
// │ │ │ PubKey
// │ │ │◀─ ─ ┘
// │ │ │─ ─ ─
// │ │ │ │ 6. Persist
// │ │ │ Credential
// │ │ │◀─ ─ ┘
// │ │ │
// │ │ │
//
// In this step, we are responding to the start reg(istration) request, and providing
// the challenge to the browser.
// TODO - Improve error handling and messages
fn auth_user(cookie_content:String,session_table:&mut HashMap<Uuid, Instant>) -> bool {
let Ok(uuid) = Uuid::parse_str(&cookie_content) else {
return false;
};
let Some(expire) = session_table.get(&uuid) else {
return false;
};
if *expire <= Instant::now() {
return false;
}
session_table.insert(uuid, Instant::now()+Duration::from_secs(*SESSION_ACTIVE_TIME));
tracing::info!("valid cookie {}",uuid);
return true;
}
#[debug_handler]
pub async fn start_register(
State(state): State<Arc<ServerState>>,
cookies: Cookies,
session: Session,
Path(username): Path<String>,
) -> Result<Json<CreationChallengeResponse>, String> {
tracing::info!("Start register");
// todo!("Auth User");
let Some(cookie_content) = cookies.get(&COOKIE_NAME) else {
return Err(WebauthnError::AuthenticationFailure.to_string());
};
let mut session_table = state.session.lock().await;
if !auth_user(cookie_content.value().to_string(), &mut session_table){
return Err(WebauthnError::AuthenticationFailure.to_string());
}
// Remove any previous registrations that may have occurred from the session.
let _ = session
.remove::<(String, Uuid, PasskeyRegistration)>("reg_state")
.await
.map_err(|_| WebauthnError::AuthenticationFailure.to_string())?;
let pool = &state.db;
let username = username.clone();
let (user_id, exclude_credentials): (Uuid, Option<Vec<CredentialID>>) =
match sqlx::query!("SELECT key FROM users WHERE name = $1;", username)
.fetch_optional(pool)
.await
.map_err(|_| WebauthnError::AuthenticationFailure.to_string())?
{
Some(record) => {
let uid = record.KEY.clone();
let records = sqlx::query!(
"SELECT credential FROM CREDENTIALS WHERE user_id = $1;",
uid
)
.fetch_all(pool)
.await
.map_err(|_| WebauthnError::AuthenticationFailure.to_string())?;
(
Uuid::parse_str(&record.KEY)
.map_err(|_| WebauthnError::AuthenticationFailure.to_string())?,
Some(
records
.iter()
.map(|record| serde_json::from_str::<Passkey>(&record.CREDENTIAL))
.collect::<Result<Vec<Passkey>, _>>()
.map_err(|_| WebauthnError::CredentialPersistenceError.to_string())?
.iter()
.map(|passkey| passkey.cred_id().clone())
.collect(),
),
)
}
None => (Uuid::new_v4(), None),
};
let res = match state.webauthn.start_passkey_registration(
user_id,
&username,
&username,
exclude_credentials,
) {
Ok((ccr, reg_state)) => {
// Note that due to the session store in use being a server side memory store, this is
// safe to store the reg_state into the session since it is not client controlled and
// not open to replay attacks. If this was a cookie store, this would be UNSAFE.
session
.insert("reg_state", (username, user_id, reg_state))
.await
.map_err(|_| WebauthnError::ChallengePersistenceError.to_string())?;
Json(ccr)
}
Err(e) => {
debug!("challenge_register -> {:?}", e);
return Err(WebauthnError::ChallengePersistenceError.to_string());
}
};
Ok(res)
}
// 3. The browser has completed it's steps and the user has created a public key
// on their device. Now we have the registration options sent to us, and we need
// to verify these and persist them.
pub async fn finish_register(
State(state): State<Arc<ServerState>>,
session: Session,
Json(reg): Json<RegisterPublicKeyCredential>,
) -> Result<impl IntoResponse, String> {
info!("Confirming registration....");
let pool = &state.db;
let Ok(Some((user_name, user_id, reg_state))) = session
.get::<(String, Uuid, PasskeyRegistration)>("reg_state")
.await
else {
return Err(WebauthnError::AuthenticationFailure.to_string()); //Corrupt Session
};
let _ = session.remove::<(Uuid, PasskeyAuthentication)>("reg_state").await;
let res = match state.webauthn.finish_passkey_registration(&reg, &reg_state) {
Ok(key) => {
info!("Passkey is okay");
let uid = &user_id.to_string();
let username = &user_name.to_string();
// Check if the user_id already exists
let record = sqlx::query!(
"SELECT COUNT(KEY) AS count FROM users WHERE KEY = $1;",
uid
)
.fetch_one(pool)
.await
.map_err(|_| WebauthnError::AuthenticationFailure.to_string())?;
// If the user doesn't exist, insert them into the users table
if record.count == 0
&& sqlx::query!(
"INSERT INTO users(KEY, NAME) VALUES($1, $2);",
uid,
username
)
.execute(pool)
.await
.map_err(|_| WebauthnError::UserNotPresent.to_string())?
.rows_affected()
!= 1
{
return Err(WebauthnError::AuthenticationFailure.to_string());
}
// Serialise the key
let serialised_key = serde_json::ser::to_string(&key)
.map_err(|_| WebauthnError::CredentialPersistenceError.to_string())?;
// Insert the key into the auth table
if sqlx::query!(
"INSERT INTO CREDENTIALS(user_id, credential) VALUES($1, $2);",
uid,
serialised_key
)
.execute(pool)
.await
.map_err(|_| WebauthnError::UserNotPresent.to_string())?
.rows_affected()
!= 1
{
return Err(WebauthnError::AuthenticationFailure.to_string());
}
StatusCode::OK
}
Err(e) => {
debug!("challenge_register -> {:?}", e);
StatusCode::BAD_REQUEST
}
};
Ok(res)
}
// 4. Now that our public key has been registered, we can authenticate a user and verify
// that they are the holder of that security token. The work flow is similar to registration.
//
// ┌───────────────┐ ┌───────────────┐ ┌───────────────┐
// │ Authenticator │ │ Browser │ │ Site │
// └───────────────┘ └───────────────┘ └───────────────┘
// │ │ │
// │ │ 1. Start Auth │
// │ │─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─▶│
// │ │ │
// │ │ 2. Challenge │
// │ │◀ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ┤
// │ │ │
// │ 3. Select Token │ │
// ─ ─ ─│◀ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─│ │
// 4. Verify │ │ │ │
// │ 4. Yield Sig │ │
// └ ─ ─▶│─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─▶ │
// │ │ 5. Send Auth │
// │ │ Opts │
// │ │─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─▶│─ ─ ─
// │ │ │ │ 5. Verify
// │ │ │ Sig
// │ │ │◀─ ─ ┘
// │ │ │
// │ │ │
//
// The user indicates the wish to start authentication and we need to provide a challenge.
pub async fn start_authentication(
State(state): State<Arc<ServerState>>,
session: Session,
Path(user_name): Path<String>,
) -> Result<impl IntoResponse, String> {
info!("Start Authentication");
let pool = &state.db;
// Remove any previous authentication that may have occurred from the session.
let _ = session.remove::<(Uuid, PasskeyAuthentication)>("auth_state");
let user_id = sqlx::query!("SELECT KEY FROM users WHERE NAME = $1;", user_name)
.fetch_one(pool)
.await
.map_err(|_| WebauthnError::UserNotPresent.to_string())?
.KEY;
let records = sqlx::query!(
"SELECT credential FROM CREDENTIALS WHERE user_id = $1;",
user_id
)
.fetch_all(pool)
.await
.map_err(|_| WebauthnError::AuthenticationFailure.to_string())?;
if records.is_empty() {
return Err(WebauthnError::CredentialNotFound.to_string());
}
let allow_credentials: Vec<Passkey> = records
.iter()
.map(|record| serde_json::de::from_str::<Passkey>(&record.CREDENTIAL))
.collect::<Result<Vec<Passkey>, _>>()
.map_err(|_| WebauthnError::CredentialPersistenceError.to_string())?;
let res = match state
.webauthn
.start_passkey_authentication(&allow_credentials)
{
Ok((rcr, auth_state)) => {
// Note that due to the session store in use being a server side memory store, this is
// safe to store the auth_state into the session since it is not client controlled and
// not open to replay attacks. If this was a cookie store, this would be UNSAFE.
session
.insert("auth_state", (&user_id, auth_state))
.await
.map_err(|_| WebauthnError::AuthenticationFailure.to_string())?;
Json(rcr)
}
Err(e) => {
debug!("challenge_authenticate -> {:?}", e);
return Err(WebauthnError::MismatchedChallenge.to_string());
}
};
Ok(res)
}
// 5. The browser and user have completed their part of the processing. Only in the
// case that the webauthn authenticate call returns Ok, is authentication considered
// a success. If the browser does not complete this call, or *any* error occurs,
// this is an authentication failure.
pub async fn finish_authentication(
State(state): State<Arc<ServerState>>,
session: Session,
Json(auth): Json<PublicKeyCredential>,
) -> Result<impl IntoResponse, String> {
let pool = &state.db;
let (user_id, auth_state): (Uuid, PasskeyAuthentication) = session
.get("auth_state")
.await
.unwrap()
.ok_or(WebauthnError::AuthenticationFailure.to_string())?;
let _ = session.remove::<(Uuid, PasskeyAuthentication)>("auth_state");
let res = match state
.webauthn
.finish_passkey_authentication(&auth, &auth_state)
{
Ok(auth_result) => {
let uid = user_id.clone().to_string();
let records = sqlx::query!(
"SELECT CREDENTIAL FROM CREDENTIALS WHERE USER_ID = $1;",
uid
)
.fetch_all(pool)
.await
.map_err(|_| WebauthnError::AuthenticatorDataMissingExtension.to_string())?;
if records.is_empty() {
return Err(WebauthnError::UserNotPresent.to_string());
}
for record in records {
let mut credential = serde_json::from_str::<Passkey>(&record.CREDENTIAL)
.map_err(|_| WebauthnError::CredentialExistCheckError.to_string())?;
if credential.cred_id() == auth_result.cred_id() {
credential.update_credential(&auth_result);
let credential = serde_json::to_string(&credential)
.map_err(|_| WebauthnError::CredentialPersistenceError.to_string())?;
if sqlx::query!(
"UPDATE CREDENTIALS SET credential = $1 WHERE user_id = $2 AND credential = $3;",
credential,
uid,
record.CREDENTIAL
)
.execute(pool)
.await.is_ok_and(|e|e.rows_affected() !=1 )
{
return Err(WebauthnError::AuthenticationFailure.to_string());
}
break;
}
}
let user_name = sqlx::query!(
"SELECT NAME FROM users WHERE KEY = $1;",
uid
)
.fetch_one(pool)
.await
.map_err(|_| WebauthnError::AuthenticationFailure.to_string())?;
// Add our own values to the session
session
.insert("user_id", user_id)
.await
.map_err(|_| WebauthnError::InvalidUserUniqueId.to_string())?;
session
.insert("user_name", user_name.NAME)
.await
.map_err(|_| WebauthnError::InvalidUsername.to_string())?;
StatusCode::OK
}
Err(e) => {
debug!("challenge_register -> {:?}", e);
StatusCode::BAD_REQUEST
}
};
info!("Authentication Successful!");
Ok(res)
}

View File

@ -1,22 +1,23 @@
use axum::extract::Query; use axum::extract::Query;
use axum::http::{HeaderMap, HeaderValue}; use axum::http::{HeaderMap, HeaderValue};
use axum::response::{Html, Redirect}; use axum::response::{Html, Redirect};
use axum::{extract::State, http::StatusCode, response::IntoResponse, routing::get, Form, Router}; use axum::{extract::State, http::StatusCode, response::IntoResponse, Form};
use minijinja::{context, Environment}; use minijinja::{context, Environment};
use serde::Deserialize;
use sqlx::sqlite::SqlitePool;
use std::net::SocketAddr;
use std::sync::Arc; use std::sync::Arc;
use std::time::{Duration, Instant}; use std::time::{Duration, Instant};
use std::{borrow::BorrowMut, env}; use std::borrow::BorrowMut;
use std::{collections::HashMap, str::FromStr}; use std::{collections::HashMap, str::FromStr};
use tokio::sync::Mutex;
use tower_cookies::{Cookie, CookieManagerLayer, Cookies}; use tower_cookies::{Cookie, Cookies};
use tower_http::trace::{self, TraceLayer};
use tracing::Level;
use uuid::Uuid; use uuid::Uuid;
use crate::{ServerState, UserLoginForm, COOKIE_DOMAIN, COOKIE_NAME, LOGIN_PAGE_HTML, SESSION_ACTIVE_TIME}; use crate::{ServerState, UserLoginForm};
use super::config::{COOKIE_DOMAIN, COOKIE_NAME, LOGIN_PAGE_HTML, SESSION_ACTIVE_TIME};
pub async fn gc_task(state: Arc<ServerState>) { pub async fn gc_task(state: Arc<ServerState>) {
@ -32,7 +33,7 @@ pub async fn gc_task(state: Arc<ServerState>) {
} }
// 处理/auth // 处理/auth
pub async fn auth( pub async fn auth_otp(
State(state): State<Arc<ServerState>>, State(state): State<Arc<ServerState>>,
// Form(frm): Form<UserLoginForm>, // Form(frm): Form<UserLoginForm>,
cookies: Cookies, cookies: Cookies,
@ -69,9 +70,9 @@ pub async fn login(
}; };
tracing::info!("{:?}", &frm); tracing::info!("{:?}", &frm);
let target = sqlx::query_as::<_, UserLoginForm>( let target = sqlx::query_as::<_, UserLoginForm>(
r#" r"
SELECT NAME, KEY FROM USERS WHERE NAME = ? SELECT NAME, KEY FROM USERS WHERE NAME = ?
"#, ",
) )
.bind(frm.username) .bind(frm.username)
.fetch_optional(&mut *conn) .fetch_optional(&mut *conn)
@ -86,7 +87,7 @@ pub async fn login(
s, s,
Instant::now() + Duration::from_secs(*SESSION_ACTIVE_TIME), Instant::now() + Duration::from_secs(*SESSION_ACTIVE_TIME),
); );
let mut new_cookie = Cookie::new(&*COOKIE_NAME, s.to_string()); let mut new_cookie = Cookie::new(COOKIE_NAME.to_string(), s.to_string());
new_cookie.set_domain(COOKIE_DOMAIN.to_string()); new_cookie.set_domain(COOKIE_DOMAIN.to_string());
cookies.add(new_cookie); cookies.add(new_cookie);
if let Some(original_uri) = params.get("original_url") { if let Some(original_uri) = params.get("original_url") {
@ -166,9 +167,9 @@ pub async fn login_with_passkey(
}; };
tracing::info!("{:?}", &frm); tracing::info!("{:?}", &frm);
let target = sqlx::query_as::<_, UserLoginForm>( let target = sqlx::query_as::<_, UserLoginForm>(
r#" r"
SELECT NAME, KEY FROM USERS WHERE NAME = ? SELECT NAME, KEY FROM USERS WHERE NAME = ?
"#, ",
) )
.bind(frm.username) .bind(frm.username)
.fetch_optional(&mut *conn) .fetch_optional(&mut *conn)
@ -183,7 +184,7 @@ pub async fn login_with_passkey(
s, s,
Instant::now() + Duration::from_secs(*SESSION_ACTIVE_TIME), Instant::now() + Duration::from_secs(*SESSION_ACTIVE_TIME),
); );
let mut new_cookie = Cookie::new(&*COOKIE_NAME, s.to_string()); let mut new_cookie = Cookie::new(COOKIE_NAME.to_string(), s.to_string());
new_cookie.set_domain(COOKIE_DOMAIN.to_string()); new_cookie.set_domain(COOKIE_DOMAIN.to_string());
cookies.add(new_cookie); cookies.add(new_cookie);
if let Some(original_uri) = params.get("original_url") { if let Some(original_uri) = params.get("original_url") {
@ -199,3 +200,4 @@ pub async fn login_with_passkey(
} }
pub mod auth;