From 2fb1c747995be0dd669189dbd677c465dcc2302e Mon Sep 17 00:00:00 2001 From: Chris Jean-Marie Date: Sat, 21 Sep 2024 16:13:47 +0000 Subject: [PATCH] Merge with rust-axum-with-google-auth project --- backend/Cargo.lock | 1131 +++++++++++++++++++++++++++++++- backend/Cargo.toml | 21 +- backend/src/error_handling.rs | 89 +++ backend/src/google_oauth.rs | 20 +- backend/src/initialmain.rs | 350 ---------- backend/src/main.rs | 41 +- backend/src/middlewares.rs | 81 +++ backend/src/oauth.rs | 232 ++++++- backend/templates/about.html | 5 + backend/templates/login.html | 4 +- backend/templates/profile.html | 6 + 11 files changed, 1576 insertions(+), 404 deletions(-) create mode 100644 backend/src/error_handling.rs delete mode 100644 backend/src/initialmain.rs create mode 100644 backend/src/middlewares.rs create mode 100644 backend/templates/about.html create mode 100644 backend/templates/profile.html diff --git a/backend/Cargo.lock b/backend/Cargo.lock index 0fd2810..4bfa366 100644 --- a/backend/Cargo.lock +++ b/backend/Cargo.lock @@ -52,6 +52,18 @@ dependencies = [ "subtle", ] +[[package]] +name = "ahash" +version = "0.8.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e89da841a80418a9b391ebaea17f5c112ffaaa96f621d2c285b5174da76b9011" +dependencies = [ + "cfg-if", + "once_cell", + "version_check", + "zerocopy", +] + [[package]] name = "aho-corasick" version = "1.1.3" @@ -76,6 +88,12 @@ dependencies = [ "alloc-no-stdlib", ] +[[package]] +name = "allocator-api2" +version = "0.2.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5c6cb57a04249c6480766f7f7cef5467412af1490f8d1e243141daddada3264f" + [[package]] name = "android-tzdata" version = "0.1.1" @@ -162,6 +180,15 @@ dependencies = [ "syn", ] +[[package]] +name = "atoi" +version = "2.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f28d99ec8bfea296261ca1af174f24225171fea9664ba9003cbebee704810528" +dependencies = [ + "num-traits", +] + [[package]] name = "atomic-waker" version = "1.1.2" @@ -176,9 +203,9 @@ checksum = "0c4b4d0bd25bd0b74681c0ad21497610ce1b7c91b1022cd21c80c6fbdd9476b0" [[package]] name = "axum" -version = "0.7.5" +version = "0.7.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3a6c9af12842a67734c9a2e355436e5d03b22383ed60cf13cd0c18fbfe3dcbcf" +checksum = "8f43644eed690f5374f1af436ecd6aea01cd201f6fbdf0178adaf6907afb2cec" dependencies = [ "async-trait", "axum-core", @@ -202,7 +229,7 @@ dependencies = [ "serde_urlencoded", "sync_wrapper 1.0.1", "tokio", - "tower", + "tower 0.5.1", "tower-layer", "tower-service", "tracing", @@ -210,9 +237,9 @@ dependencies = [ [[package]] name = "axum-core" -version = "0.4.3" +version = "0.4.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a15c63fd72d41492dc4f497196f5da1fb04fb7529e631d73630d1b491e47a2e3" +checksum = "5e6b8ba012a258d63c9adfa28b9ddcf66149da6f986c5b5452e629d5ee64bf00" dependencies = [ "async-trait", "bytes", @@ -223,7 +250,31 @@ dependencies = [ "mime", "pin-project-lite", "rustversion", - "sync_wrapper 0.1.2", + "sync_wrapper 1.0.1", + "tower-layer", + "tower-service", + "tracing", +] + +[[package]] +name = "axum-extra" +version = "0.9.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "73c3220b188aea709cf1b6c5f9b01c3bd936bb08bd2b5184a12b35ac8131b1f9" +dependencies = [ + "axum", + "axum-core", + "bytes", + "cookie", + "futures-util", + "headers", + "http 1.1.0", + "http-body 1.0.1", + "http-body-util", + "mime", + "pin-project-lite", + "serde", + "tower 0.5.1", "tower-layer", "tower-service", "tracing", @@ -244,7 +295,7 @@ dependencies = [ "hyper-util", "pin-project-lite", "tokio", - "tower", + "tower 0.4.13", "tower-service", ] @@ -312,6 +363,12 @@ version = "0.22.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "72b3254f16251a8381aa12e40e3c4d2f0199f8c6508fbecb9d91f575e0fbb8c6" +[[package]] +name = "base64ct" +version = "1.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8c3c1a368f70d6cf7302d78f8f7093da241fb8e8807c05cc9e51a125895a6d5b" + [[package]] name = "basic-toml" version = "0.1.9" @@ -332,6 +389,9 @@ name = "bitflags" version = "2.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b048fb63fd8b5923fc5aa7b340d8e156aec7ec02f0c78fa8a6ddc2613f6f71de" +dependencies = [ + "serde", +] [[package]] name = "block-buffer" @@ -423,6 +483,27 @@ dependencies = [ "inout", ] +[[package]] +name = "concurrent-queue" +version = "2.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4ca0197aee26d1ae37445ee532fefce43251d24cc7c166799f4d46817f1d3973" +dependencies = [ + "crossbeam-utils", +] + +[[package]] +name = "const-oid" +version = "0.9.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c2459377285ad874054d797f3ccebf984978aa39129f6eafde5cdc8315b612f8" + +[[package]] +name = "constant_time_eq" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7c74b8349d32d297c9134b8c88677813a227df8f779daa29bfc29c183fe3dca6" + [[package]] name = "cookie" version = "0.18.1" @@ -463,6 +544,21 @@ dependencies = [ "libc", ] +[[package]] +name = "crc" +version = "3.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "69e6e4d7b33a94f0991c26729976b10ebde1d34c3ee82408fb536164fa10d636" +dependencies = [ + "crc-catalog", +] + +[[package]] +name = "crc-catalog" +version = "2.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "19d374276b40fb8bbdee95aef7c7fa6b5316ec764510eb64b8dd0e2ed0d7e7f5" + [[package]] name = "crc32fast" version = "1.4.2" @@ -472,6 +568,15 @@ dependencies = [ "cfg-if", ] +[[package]] +name = "crossbeam-queue" +version = "0.3.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "df0346b5d5e76ac2fe4e327c5fd1118d6be7c51dfb18f9b7922923f287471e35" +dependencies = [ + "crossbeam-utils", +] + [[package]] name = "crossbeam-utils" version = "0.8.20" @@ -512,6 +617,17 @@ dependencies = [ "parking_lot_core", ] +[[package]] +name = "der" +version = "0.7.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f55bf8e7b65898637379c1b74eb1551107c8294ed26d855ceb9fd1a09cfc9bc0" +dependencies = [ + "const-oid", + "pem-rfc7468", + "zeroize", +] + [[package]] name = "deranged" version = "0.3.11" @@ -528,10 +644,26 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9ed9a281f7bc9b7576e61468ba615a66a5c8cfdff42420a70aa82701a3b1e292" dependencies = [ "block-buffer", + "const-oid", "crypto-common", "subtle", ] +[[package]] +name = "dotenvy" +version = "0.15.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1aaf95b3e5c8f23aa320147307562d361db0ae0d51242340f558153b4eb2439b" + +[[package]] +name = "either" +version = "1.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "60b1af1c220855b6ceac025d3f6ecdd2b7c4894bfe9cd9bda4fbb4bc7c0d4cf0" +dependencies = [ + "serde", +] + [[package]] name = "encoding_rs" version = "0.8.34" @@ -547,6 +679,44 @@ version = "1.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5443807d6dff69373d433ab9ef5378ad8df50ca6298caf15de6e52e24aaf54d5" +[[package]] +name = "errno" +version = "0.3.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "534c5cf6194dfab3db3242765c03bbe257cf92f22b38f6bc0c58d59108a820ba" +dependencies = [ + "libc", + "windows-sys 0.52.0", +] + +[[package]] +name = "etcetera" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "136d1b5283a1ab77bd9257427ffd09d8667ced0570b6f938942bc7568ed5b943" +dependencies = [ + "cfg-if", + "home", + "windows-sys 0.48.0", +] + +[[package]] +name = "event-listener" +version = "5.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6032be9bd27023a771701cc49f9f053c751055f71efb2e0ae5c15809093675ba" +dependencies = [ + "concurrent-queue", + "parking", + "pin-project-lite", +] + +[[package]] +name = "fastrand" +version = "2.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e8c02a5121d4ea3eb16a80748c74f5549a5665e4c21333c6098f283870fbdea6" + [[package]] name = "flate2" version = "1.0.33" @@ -557,12 +727,38 @@ dependencies = [ "miniz_oxide", ] +[[package]] +name = "flume" +version = "0.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "55ac459de2512911e4b674ce33cf20befaba382d05b62b008afc1c8b57cbf181" +dependencies = [ + "futures-core", + "futures-sink", + "spin", +] + [[package]] name = "fnv" version = "1.0.7" source = "registry+https://github.com/rust-lang/crates.io-index" 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]] name = "form_urlencoded" version = "1.2.1" @@ -624,6 +820,17 @@ dependencies = [ "futures-util", ] +[[package]] +name = "futures-intrusive" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1d930c203dd0b6ff06e0201a4a2fe9149b43c684fd4420555b26d21b1a02956f" +dependencies = [ + "futures-core", + "lock_api", + "parking_lot", +] + [[package]] name = "futures-io" version = "0.3.30" @@ -753,6 +960,49 @@ name = "hashbrown" version = "0.14.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e5274423e17b7c9fc20b6e7e208532f9b19825d82dfd615708b70edd83df41f1" +dependencies = [ + "ahash", + "allocator-api2", +] + +[[package]] +name = "hashlink" +version = "0.9.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6ba4ff7128dee98c7dc9794b6a411377e1404dba1c97deb8d1a55297bd25d8af" +dependencies = [ + "hashbrown", +] + +[[package]] +name = "headers" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "322106e6bd0cba2d5ead589ddb8150a13d7c4217cf80d7c4f682ca994ccc6aa9" +dependencies = [ + "base64 0.21.7", + "bytes", + "headers-core", + "http 1.1.0", + "httpdate", + "mime", + "sha1", +] + +[[package]] +name = "headers-core" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "54b4a22553d4242c49fddb9ba998a99962b5cc6f22cb5a3482bec22522403ce4" +dependencies = [ + "http 1.1.0", +] + +[[package]] +name = "heck" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2304e00983f87ffb38b55b444b5e3b60a884b5d30c0fca7d82fe33449bbe55ea" [[package]] name = "hermit-abi" @@ -760,6 +1010,21 @@ version = "0.3.9" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d231dfb89cfffdbc30e7fc41579ed6066ad03abda9e567ccafae602b97ec5024" +[[package]] +name = "hex" +version = "0.4.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7f24254aa9a54b5c858eaee2f5bccdb46aaf0e486a595ed5fd8f86ba55232a70" + +[[package]] +name = "hkdf" +version = "0.12.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7b5f8eb2ad728638ea2c7d47a21db23b7b58a72ed6a38256b8a1849f15fbbdf7" +dependencies = [ + "hmac", +] + [[package]] name = "hmac" version = "0.12.1" @@ -769,6 +1034,15 @@ dependencies = [ "digest", ] +[[package]] +name = "home" +version = "0.5.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e3d1354bf6b7235cb4a0576c2619fd4ed18183f689b12b006a0ee7329eeff9a5" +dependencies = [ + "windows-sys 0.52.0", +] + [[package]] name = "http" version = "0.2.12" @@ -894,6 +1168,7 @@ dependencies = [ "pin-project-lite", "smallvec", "tokio", + "want", ] [[package]] @@ -905,9 +1180,42 @@ dependencies = [ "futures-util", "http 0.2.12", "hyper 0.14.30", - "rustls", + "rustls 0.21.12", "tokio", - "tokio-rustls", + "tokio-rustls 0.24.1", +] + +[[package]] +name = "hyper-rustls" +version = "0.27.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "08afdbb5c31130e3034af566421053ab03787c640246a446327f550d11bcb333" +dependencies = [ + "futures-util", + "http 1.1.0", + "hyper 1.4.1", + "hyper-util", + "rustls 0.23.13", + "rustls-pki-types", + "tokio", + "tokio-rustls 0.26.0", + "tower-service", +] + +[[package]] +name = "hyper-tls" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "70206fc6890eaca9fde8a0bf71caa2ddfc9fe045ac9e5c70df101a7dbde866e0" +dependencies = [ + "bytes", + "http-body-util", + "hyper 1.4.1", + "hyper-util", + "native-tls", + "tokio", + "tokio-native-tls", + "tower-service", ] [[package]] @@ -917,12 +1225,17 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "da62f120a8a37763efb0cf8fdf264b884c7b8b9ac8660b900c8661030c00e6ba" dependencies = [ "bytes", + "futures-channel", "futures-util", "http 1.1.0", "http-body 1.0.1", "hyper 1.4.1", "pin-project-lite", + "socket2", "tokio", + "tower 0.4.13", + "tower-service", + "tracing", ] [[package]] @@ -985,9 +1298,9 @@ checksum = "187674a687eed5fe42285b40c6291f9a01517d415fad1c3cbc6a9f778af7fcd4" [[package]] name = "iri-string" -version = "0.7.4" +version = "0.7.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3e0f755bd3806e06ad4f366f92639415d99a339a2c7ecf8c26ccea2097c11cb6" +checksum = "9c25163201be6ded9e686703e85532f8f852ea1f92ba625cb3c51f7fe6d07a4a" dependencies = [ "memchr", "serde", @@ -1005,15 +1318,25 @@ version = "0.1.1" dependencies = [ "askama", "axum", + "axum-extra", "axum-server", "axum_session", + "chrono", + "constant_time_eq", + "dotenvy", + "headers", "http 1.1.0", + "minijinja", "oauth2", + "reqwest 0.12.7", "serde", + "serde_json", + "sqlx", "tokio", "tower-http", "tracing", "tracing-subscriber", + "uuid", ] [[package]] @@ -1039,6 +1362,9 @@ name = "lazy_static" version = "1.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bbd2bcb4c963f2ddae06a2efc7e9f3591312473c50c6685e1f298068316e66fe" +dependencies = [ + "spin", +] [[package]] name = "libc" @@ -1052,6 +1378,23 @@ version = "0.2.8" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4ec2a862134d2a7d32d7983ddcdd1c4923530833c9f2ea1a44fc5fa473989058" +[[package]] +name = "libsqlite3-sys" +version = "0.30.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2e99fb7a497b1e3339bc746195567ed8d3e24945ecd636e3619d20b9de9e9149" +dependencies = [ + "cc", + "pkg-config", + "vcpkg", +] + +[[package]] +name = "linux-raw-sys" +version = "0.4.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "78b3ae25bc7c8c38cec158d1f2757ee79e9b3740fbc7ccf0e59e4b08d793fa89" + [[package]] name = "lock_api" version = "0.4.12" @@ -1083,12 +1426,28 @@ version = "0.7.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0e7465ac9959cc2b1404e8e2367b43684a6d13790fe23056cc8c6c5a6b7bcb94" +[[package]] +name = "md-5" +version = "0.10.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d89e7ee0cfbedfc4da3340218492196241d89eefb6dab27de5df917a6d2e78cf" +dependencies = [ + "cfg-if", + "digest", +] + [[package]] name = "memchr" version = "2.7.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "78ca9ab1a0babb1e7d5695e3530886289c18cf2f87ec19a575a0abdce112e3a3" +[[package]] +name = "memo-map" +version = "0.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "38d1115007560874e373613744c6fba374c17688327a71c1476d1a5954cc857b" + [[package]] name = "mime" version = "0.3.17" @@ -1105,6 +1464,17 @@ dependencies = [ "unicase", ] +[[package]] +name = "minijinja" +version = "2.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1028b628753a7e1a88fc59c9ba4b02ecc3bc0bd3c7af23df667bc28df9b3310e" +dependencies = [ + "memo-map", + "self_cell", + "serde", +] + [[package]] name = "minimal-lexical" version = "0.2.1" @@ -1132,6 +1502,23 @@ dependencies = [ "windows-sys 0.52.0", ] +[[package]] +name = "native-tls" +version = "0.2.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a8614eb2c83d59d1c8cc974dd3f920198647674a0a035e1af1fa58707e317466" +dependencies = [ + "libc", + "log", + "openssl", + "openssl-probe", + "openssl-sys", + "schannel", + "security-framework", + "security-framework-sys", + "tempfile", +] + [[package]] name = "nom" version = "7.1.3" @@ -1158,12 +1545,49 @@ dependencies = [ "winapi", ] +[[package]] +name = "num-bigint-dig" +version = "0.8.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dc84195820f291c7697304f3cbdadd1cb7199c0efc917ff5eafd71225c136151" +dependencies = [ + "byteorder", + "lazy_static", + "libm", + "num-integer", + "num-iter", + "num-traits", + "rand", + "smallvec", + "zeroize", +] + [[package]] name = "num-conv" version = "0.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "51d515d32fb182ee37cda2ccdcb92950d6a3c2893aa280e540671c2cd0f3b1d9" +[[package]] +name = "num-integer" +version = "0.1.46" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7969661fd2958a5cb096e56c8e1ad0444ac2bbcd0061bd28660485a44879858f" +dependencies = [ + "num-traits", +] + +[[package]] +name = "num-iter" +version = "0.1.45" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1429034a0490724d0075ebb2bc9e875d6503c3cf69e235a8941aa757d83ef5bf" +dependencies = [ + "autocfg", + "num-integer", + "num-traits", +] + [[package]] name = "num-traits" version = "0.2.19" @@ -1171,6 +1595,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "071dfc062690e90b734c0b2273ce72ad0ffa95f0c74596bc250dcfd960262841" dependencies = [ "autocfg", + "libm", ] [[package]] @@ -1184,7 +1609,7 @@ dependencies = [ "getrandom", "http 0.2.12", "rand", - "reqwest", + "reqwest 0.11.27", "serde", "serde_json", "serde_path_to_error", @@ -1214,12 +1639,62 @@ version = "0.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c08d65885ee38876c4f86fa503fb49d7b507c2b62552df7c70b2fce627e06381" +[[package]] +name = "openssl" +version = "0.10.66" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9529f4786b70a3e8c61e11179af17ab6188ad8d0ded78c5529441ed39d4bd9c1" +dependencies = [ + "bitflags 2.6.0", + "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", +] + +[[package]] +name = "openssl-probe" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ff011a302c396a5197692431fc1948019154afc178baf7d8e37367442a4601cf" + +[[package]] +name = "openssl-sys" +version = "0.9.103" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7f9e8deee91df40a943c71b917e5874b951d32a802526c85721ce3b776c929d6" +dependencies = [ + "cc", + "libc", + "pkg-config", + "vcpkg", +] + [[package]] name = "overload" version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b15813163c1d831bf4a13c3610c05c0d03b39feb07f7e09fa234dac9b15aaf39" +[[package]] +name = "parking" +version = "2.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f38d5652c16fde515bb1ecef450ab0f6a219d619a7274976324d5e377f7dceba" + [[package]] name = "parking_lot" version = "0.12.3" @@ -1243,6 +1718,21 @@ dependencies = [ "windows-targets 0.52.6", ] +[[package]] +name = "paste" +version = "1.0.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "57c0d7b74b563b49d38dae00a0c37d4d6de9b432382b2892f0574ddcae73fd0a" + +[[package]] +name = "pem-rfc7468" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "88b39c9bfcfc231068454382784bb460aae594343fb030d46e9f50a645418412" +dependencies = [ + "base64ct", +] + [[package]] name = "percent-encoding" version = "2.3.1" @@ -1281,6 +1771,27 @@ version = "0.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184" +[[package]] +name = "pkcs1" +version = "0.7.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c8ffb9f10fa047879315e6625af03c164b16962a5368d724ed16323b68ace47f" +dependencies = [ + "der", + "pkcs8", + "spki", +] + +[[package]] +name = "pkcs8" +version = "0.10.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f950b2377845cebe5cf8b5165cb3cc1a5e0fa5cfa3e1f7f55707d8fd82e0a7b7" +dependencies = [ + "der", + "spki", +] + [[package]] name = "pkg-config" version = "0.3.30" @@ -1430,7 +1941,7 @@ dependencies = [ "http 0.2.12", "http-body 0.4.6", "hyper 0.14.30", - "hyper-rustls", + "hyper-rustls 0.24.2", "ipnet", "js-sys", "log", @@ -1438,15 +1949,15 @@ dependencies = [ "once_cell", "percent-encoding", "pin-project-lite", - "rustls", - "rustls-pemfile", + "rustls 0.21.12", + "rustls-pemfile 1.0.4", "serde", "serde_json", "serde_urlencoded", "sync_wrapper 0.1.2", - "system-configuration", + "system-configuration 0.5.1", "tokio", - "tokio-rustls", + "tokio-rustls 0.24.1", "tower-service", "url", "wasm-bindgen", @@ -1456,6 +1967,49 @@ dependencies = [ "winreg", ] +[[package]] +name = "reqwest" +version = "0.12.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f8f4955649ef5c38cc7f9e8aa41761d48fb9677197daea9984dc54f56aad5e63" +dependencies = [ + "base64 0.22.1", + "bytes", + "encoding_rs", + "futures-core", + "futures-util", + "h2 0.4.6", + "http 1.1.0", + "http-body 1.0.1", + "http-body-util", + "hyper 1.4.1", + "hyper-rustls 0.27.3", + "hyper-tls", + "hyper-util", + "ipnet", + "js-sys", + "log", + "mime", + "native-tls", + "once_cell", + "percent-encoding", + "pin-project-lite", + "rustls-pemfile 2.1.3", + "serde", + "serde_json", + "serde_urlencoded", + "sync_wrapper 1.0.1", + "system-configuration 0.6.1", + "tokio", + "tokio-native-tls", + "tower-service", + "url", + "wasm-bindgen", + "wasm-bindgen-futures", + "web-sys", + "windows-registry", +] + [[package]] name = "ring" version = "0.17.8" @@ -1471,12 +2025,45 @@ dependencies = [ "windows-sys 0.52.0", ] +[[package]] +name = "rsa" +version = "0.9.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5d0e5124fcb30e76a7e79bfee683a2746db83784b86289f6251b54b7950a0dfc" +dependencies = [ + "const-oid", + "digest", + "num-bigint-dig", + "num-integer", + "num-traits", + "pkcs1", + "pkcs8", + "rand_core", + "signature", + "spki", + "subtle", + "zeroize", +] + [[package]] name = "rustc-demangle" version = "0.1.24" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "719b953e2095829ee67db738b3bfa9fa368c94900df327b3f07fe6e794d2fe1f" +[[package]] +name = "rustix" +version = "0.38.37" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8acb788b847c24f28525660c4d7758620a7210875711f79e7f663cc152726811" +dependencies = [ + "bitflags 2.6.0", + "errno", + "libc", + "linux-raw-sys", + "windows-sys 0.52.0", +] + [[package]] name = "rustls" version = "0.21.12" @@ -1485,10 +2072,23 @@ checksum = "3f56a14d1f48b391359b22f731fd4bd7e43c97f3c50eee276f3aa09c94784d3e" dependencies = [ "log", "ring", - "rustls-webpki", + "rustls-webpki 0.101.7", "sct", ] +[[package]] +name = "rustls" +version = "0.23.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f2dabaac7466917e566adb06783a81ca48944c6898a1b08b9374106dd671f4c8" +dependencies = [ + "once_cell", + "rustls-pki-types", + "rustls-webpki 0.102.8", + "subtle", + "zeroize", +] + [[package]] name = "rustls-pemfile" version = "1.0.4" @@ -1498,6 +2098,22 @@ dependencies = [ "base64 0.21.7", ] +[[package]] +name = "rustls-pemfile" +version = "2.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "196fe16b00e106300d3e45ecfcb764fa292a535d7326a29a5875c579c7417425" +dependencies = [ + "base64 0.22.1", + "rustls-pki-types", +] + +[[package]] +name = "rustls-pki-types" +version = "1.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fc0a2ce646f8655401bb81e7927b812614bd5d91dbc968696be50603510fcaf0" + [[package]] name = "rustls-webpki" version = "0.101.7" @@ -1508,6 +2124,17 @@ dependencies = [ "untrusted", ] +[[package]] +name = "rustls-webpki" +version = "0.102.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "64ca1bc8749bd4cf37b5ce386cc146580777b4e8572c7b97baf22c83f444bee9" +dependencies = [ + "ring", + "rustls-pki-types", + "untrusted", +] + [[package]] name = "rustversion" version = "1.0.17" @@ -1520,6 +2147,15 @@ version = "1.0.18" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f3cb5ba0dc43242ce17de99c180e96db90b235b8a9fdc9543c96d2209116bd9f" +[[package]] +name = "schannel" +version = "0.1.24" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e9aaafd5a2b6e3d657ff009d82fbd630b6bd54dd4eb06f21693925cdf80f9b8b" +dependencies = [ + "windows-sys 0.59.0", +] + [[package]] name = "scopeguard" version = "1.2.0" @@ -1536,6 +2172,35 @@ dependencies = [ "untrusted", ] +[[package]] +name = "security-framework" +version = "2.11.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "897b2245f0b511c87893af39b033e5ca9cce68824c4d7e7630b5a1d339658d02" +dependencies = [ + "bitflags 2.6.0", + "core-foundation", + "core-foundation-sys", + "libc", + "security-framework-sys", +] + +[[package]] +name = "security-framework-sys" +version = "2.12.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ea4a292869320c0272d7bc55a5a6aafaff59b4f63404a003887b679a2e05b4b6" +dependencies = [ + "core-foundation-sys", + "libc", +] + +[[package]] +name = "self_cell" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d369a96f978623eb3dc28807c4852d6cc617fed53da5d3c400feff1ef34a714a" + [[package]] name = "serde" version = "1.0.210" @@ -1590,6 +2255,17 @@ dependencies = [ "serde", ] +[[package]] +name = "sha1" +version = "0.10.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e3bf829a2d51ab4a5ddf1352d8470c140cadc8301b2ae1789db023f01cedd6ba" +dependencies = [ + "cfg-if", + "cpufeatures", + "digest", +] + [[package]] name = "sha2" version = "0.10.8" @@ -1625,6 +2301,16 @@ dependencies = [ "libc", ] +[[package]] +name = "signature" +version = "2.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "77549399552de45a898a580c1b41d445bf730df867cc44e6c0233bbc4b8329de" +dependencies = [ + "digest", + "rand_core", +] + [[package]] name = "slab" version = "0.4.9" @@ -1639,6 +2325,9 @@ name = "smallvec" version = "1.13.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3c5e1a9a646d36c3599cd173a41282daf47c44583ad367b8e6837255952e5c67" +dependencies = [ + "serde", +] [[package]] name = "socket2" @@ -1655,6 +2344,234 @@ name = "spin" version = "0.9.8" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6980e8d7511241f8acf4aebddbb1ff938df5eebe98691418c4468d0b72a96a67" +dependencies = [ + "lock_api", +] + +[[package]] +name = "spki" +version = "0.7.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d91ed6c858b01f942cd56b37a94b3e0a1798290327d1236e4d9cf4eaca44d29d" +dependencies = [ + "base64ct", + "der", +] + +[[package]] +name = "sqlformat" +version = "0.2.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7bba3a93db0cc4f7bdece8bb09e77e2e785c20bfebf79eb8340ed80708048790" +dependencies = [ + "nom", + "unicode_categories", +] + +[[package]] +name = "sqlx" +version = "0.8.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "93334716a037193fac19df402f8571269c84a00852f6a7066b5d2616dcd64d3e" +dependencies = [ + "sqlx-core", + "sqlx-macros", + "sqlx-mysql", + "sqlx-postgres", + "sqlx-sqlite", +] + +[[package]] +name = "sqlx-core" +version = "0.8.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d4d8060b456358185f7d50c55d9b5066ad956956fddec42ee2e8567134a8936e" +dependencies = [ + "atoi", + "byteorder", + "bytes", + "crc", + "crossbeam-queue", + "either", + "event-listener", + "futures-channel", + "futures-core", + "futures-intrusive", + "futures-io", + "futures-util", + "hashbrown", + "hashlink", + "hex", + "indexmap", + "log", + "memchr", + "once_cell", + "paste", + "percent-encoding", + "serde", + "serde_json", + "sha2", + "smallvec", + "sqlformat", + "thiserror", + "tokio", + "tokio-stream", + "tracing", + "url", +] + +[[package]] +name = "sqlx-macros" +version = "0.8.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cac0692bcc9de3b073e8d747391827297e075c7710ff6276d9f7a1f3d58c6657" +dependencies = [ + "proc-macro2", + "quote", + "sqlx-core", + "sqlx-macros-core", + "syn", +] + +[[package]] +name = "sqlx-macros-core" +version = "0.8.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1804e8a7c7865599c9c79be146dc8a9fd8cc86935fa641d3ea58e5f0688abaa5" +dependencies = [ + "dotenvy", + "either", + "heck", + "hex", + "once_cell", + "proc-macro2", + "quote", + "serde", + "serde_json", + "sha2", + "sqlx-core", + "sqlx-mysql", + "sqlx-postgres", + "sqlx-sqlite", + "syn", + "tempfile", + "tokio", + "url", +] + +[[package]] +name = "sqlx-mysql" +version = "0.8.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "64bb4714269afa44aef2755150a0fc19d756fb580a67db8885608cf02f47d06a" +dependencies = [ + "atoi", + "base64 0.22.1", + "bitflags 2.6.0", + "byteorder", + "bytes", + "crc", + "digest", + "dotenvy", + "either", + "futures-channel", + "futures-core", + "futures-io", + "futures-util", + "generic-array", + "hex", + "hkdf", + "hmac", + "itoa", + "log", + "md-5", + "memchr", + "once_cell", + "percent-encoding", + "rand", + "rsa", + "serde", + "sha1", + "sha2", + "smallvec", + "sqlx-core", + "stringprep", + "thiserror", + "tracing", + "whoami", +] + +[[package]] +name = "sqlx-postgres" +version = "0.8.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6fa91a732d854c5d7726349bb4bb879bb9478993ceb764247660aee25f67c2f8" +dependencies = [ + "atoi", + "base64 0.22.1", + "bitflags 2.6.0", + "byteorder", + "crc", + "dotenvy", + "etcetera", + "futures-channel", + "futures-core", + "futures-io", + "futures-util", + "hex", + "hkdf", + "hmac", + "home", + "itoa", + "log", + "md-5", + "memchr", + "once_cell", + "rand", + "serde", + "serde_json", + "sha2", + "smallvec", + "sqlx-core", + "stringprep", + "thiserror", + "tracing", + "whoami", +] + +[[package]] +name = "sqlx-sqlite" +version = "0.8.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d5b2cf34a45953bfd3daaf3db0f7a7878ab9b7a6b91b422d24a7a9e4c857b680" +dependencies = [ + "atoi", + "flume", + "futures-channel", + "futures-core", + "futures-executor", + "futures-intrusive", + "futures-util", + "libsqlite3-sys", + "log", + "percent-encoding", + "serde", + "serde_urlencoded", + "sqlx-core", + "tracing", + "url", +] + +[[package]] +name = "stringprep" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7b4df3d392d81bd458a8a621b8bffbd2302a12ffe288a9d931670948749463b1" +dependencies = [ + "unicode-bidi", + "unicode-normalization", + "unicode-properties", +] [[package]] name = "subtle" @@ -1684,6 +2601,9 @@ name = "sync_wrapper" version = "1.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a7065abeca94b6a8a577f9bd45aa0867a2238b74e8eb67cf10d492bc39351394" +dependencies = [ + "futures-core", +] [[package]] name = "system-configuration" @@ -1693,7 +2613,18 @@ checksum = "ba3a3adc5c275d719af8cb4272ea1c4a6d668a777f37e115f6d11ddbc1c8e0e7" dependencies = [ "bitflags 1.3.2", "core-foundation", - "system-configuration-sys", + "system-configuration-sys 0.5.0", +] + +[[package]] +name = "system-configuration" +version = "0.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3c879d448e9d986b661742763247d3693ed13609438cf3d006f51f5368a5ba6b" +dependencies = [ + "bitflags 2.6.0", + "core-foundation", + "system-configuration-sys 0.6.0", ] [[package]] @@ -1706,6 +2637,29 @@ dependencies = [ "libc", ] +[[package]] +name = "system-configuration-sys" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8e1d1b10ced5ca923a1fcb8d03e96b8d3268065d724548c0211415ff6ac6bac4" +dependencies = [ + "core-foundation-sys", + "libc", +] + +[[package]] +name = "tempfile" +version = "3.12.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "04cbcdd0c794ebb0d4cf35e88edd2f7d2c4c3e9a5a6dab322839b321c6a87a64" +dependencies = [ + "cfg-if", + "fastrand", + "once_cell", + "rustix", + "windows-sys 0.59.0", +] + [[package]] name = "thiserror" version = "1.0.63" @@ -1811,13 +2765,45 @@ dependencies = [ "syn", ] +[[package]] +name = "tokio-native-tls" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bbae76ab933c85776efabc971569dd6119c580d8f5d448769dec1764bf796ef2" +dependencies = [ + "native-tls", + "tokio", +] + [[package]] name = "tokio-rustls" version = "0.24.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c28327cf380ac148141087fbfb9de9d7bd4e84ab5d2c28fbc911d753de8a7081" dependencies = [ - "rustls", + "rustls 0.21.12", + "tokio", +] + +[[package]] +name = "tokio-rustls" +version = "0.26.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0c7bc40d0e5a97695bb96e27995cd3a08538541b0a846f65bba7a359f36700d4" +dependencies = [ + "rustls 0.23.13", + "rustls-pki-types", + "tokio", +] + +[[package]] +name = "tokio-stream" +version = "0.1.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4f4e6ce100d0eb49a2734f8c0812bcd324cf357d21810932c5df6b96ef2b86f1" +dependencies = [ + "futures-core", + "pin-project-lite", "tokio", ] @@ -1851,13 +2837,29 @@ dependencies = [ ] [[package]] -name = "tower-http" -version = "0.5.2" +name = "tower" +version = "0.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1e9cd434a998747dd2c4276bc96ee2e0c7a2eadf3cae88e52be55a05fa9053f5" +checksum = "2873938d487c3cfb9aed7546dc9f2711d867c9f90c46b889989a2cb84eba6b4f" +dependencies = [ + "futures-core", + "futures-util", + "pin-project-lite", + "sync_wrapper 0.1.2", + "tokio", + "tower-layer", + "tower-service", + "tracing", +] + +[[package]] +name = "tower-http" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "41515cc9e193536d93fd0dbbea0c73819c08eca76e0b30909a325c3ec90985bb" dependencies = [ "async-compression", - "base64 0.21.7", + "base64 0.22.1", "bitflags 2.6.0", "bytes", "futures-core", @@ -1874,7 +2876,7 @@ dependencies = [ "pin-project-lite", "tokio", "tokio-util", - "tower", + "tower 0.5.1", "tower-layer", "tower-service", "tracing", @@ -1997,6 +2999,18 @@ dependencies = [ "tinyvec", ] +[[package]] +name = "unicode-properties" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "52ea75f83c0137a9b98608359a5f1af8144876eb67bcb1ce837368e906a9f524" + +[[package]] +name = "unicode_categories" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "39ec24b3121d976906ece63c9daad25b85969647682eee313cb5779fdd69e14e" + [[package]] name = "universal-hash" version = "0.5.1" @@ -2041,6 +3055,12 @@ version = "0.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "830b7e5d4d90034032940e4ace0d9a9a057e7a45cd94e6c007832e39edb82f6d" +[[package]] +name = "vcpkg" +version = "0.2.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "accd4ea62f7bb7a82fe23066fb0957d48ef677f6eeb8215f372f52e48bb32426" + [[package]] name = "version_check" version = "0.9.5" @@ -2062,6 +3082,12 @@ version = "0.11.0+wasi-snapshot-preview1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" +[[package]] +name = "wasite" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b8dad83b4f25e74f184f64c43b150b91efe7647395b42289f38e50566d82855b" + [[package]] name = "wasm-bindgen" version = "0.2.93" @@ -2145,6 +3171,16 @@ version = "0.25.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5f20c57d8d7db6d3b86154206ae5d8fba62dd39573114de97c2cb0578251f8e1" +[[package]] +name = "whoami" +version = "1.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "372d5b87f58ec45c384ba03563b03544dc5fadc3983e434b286913f5b4a9bb6d" +dependencies = [ + "redox_syscall", + "wasite", +] + [[package]] name = "winapi" version = "0.3.9" @@ -2176,6 +3212,36 @@ dependencies = [ "windows-targets 0.52.6", ] +[[package]] +name = "windows-registry" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e400001bb720a623c1c69032f8e3e4cf09984deec740f007dd2b03ec864804b0" +dependencies = [ + "windows-result", + "windows-strings", + "windows-targets 0.52.6", +] + +[[package]] +name = "windows-result" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1d1043d8214f791817bab27572aaa8af63732e11bf84aa21a45a78d6c317ae0e" +dependencies = [ + "windows-targets 0.52.6", +] + +[[package]] +name = "windows-strings" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4cd9b125c486025df0eabcb585e62173c6c9eddcec5d117d3b6e8c30e2ee4d10" +dependencies = [ + "windows-result", + "windows-targets 0.52.6", +] + [[package]] name = "windows-sys" version = "0.48.0" @@ -2194,6 +3260,15 @@ dependencies = [ "windows-targets 0.52.6", ] +[[package]] +name = "windows-sys" +version = "0.59.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1e38bc4d79ed67fd075bcc251a1c39b32a1776bbe92e5bef1f0bf1f8c531853b" +dependencies = [ + "windows-targets 0.52.6", +] + [[package]] name = "windows-targets" version = "0.48.5" @@ -2346,6 +3421,12 @@ dependencies = [ "syn", ] +[[package]] +name = "zeroize" +version = "1.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ced3678a2879b30306d323f4542626697a464a97c0a07c9aebf7ebca65cd4dde" + [[package]] name = "zstd" version = "0.13.2" diff --git a/backend/Cargo.toml b/backend/Cargo.toml index dc3c5da..1b0a63e 100644 --- a/backend/Cargo.toml +++ b/backend/Cargo.toml @@ -7,21 +7,24 @@ edition = "2021" # Update all dependencies with `cargo upgrade -i allow && cargo update` [dependencies] -axum = { version = "0.7.5" } +axum = { version = "0.7.6" } axum_session = { version = "0.14.2" } axum-server = { version = "0.7.1" } +axum-extra = { version = "0.9.4", features = ["cookie-private", "typed-header"] } +headers = "0.4" serde = { version = "1.0", features = ["derive"] } -#serde_json = "1.0.68" +serde_json = "1.0" tokio = { version = "1.40", features = ["full"] } tracing = "0.1" tracing-subscriber = { version="0.3", features = ["env-filter"] } -#uuid = { version = "1.1.2", features = ["v4", "serde"] } -#async-session = "3.0.0" askama = "0.12" +minijinja = { version = "2", features = ["loader"] } oauth2 = "4.4" -#reqwest = { version = "0.11", default-features = false, features = ["rustls-tls", "json"] } -#headers = "0.3" http = "1.1" -tower-http = { version = "0.5.2", features = ["full"] } -#sqlx = { version = "0.6.2", features = ["runtime-tokio-rustls", "postgres", "macros", "migrate", "chrono", "json"]} -#anyhow = "1.0" \ No newline at end of file +tower-http = { version = "0.6.0", features = ["full"] } +chrono = { version = "0.4.38", features = ["serde"] } +sqlx = { version = "0.8", features = ["sqlite", "runtime-tokio"] } +uuid = { version = "1.10", features = ["v4"] } +dotenvy = "0.15" +constant_time_eq = "0.3" +reqwest = "0.12" diff --git a/backend/src/error_handling.rs b/backend/src/error_handling.rs new file mode 100644 index 0000000..9731ae3 --- /dev/null +++ b/backend/src/error_handling.rs @@ -0,0 +1,89 @@ +use axum::{ + http::StatusCode, + response::{Html, IntoResponse}, +}; + +pub struct AppError { + code: StatusCode, + message: String, + user_message: String, +} + +impl AppError { + pub fn new(message: impl Into) -> Self { + Self { + message: message.into(), + user_message: "".to_owned(), + code: StatusCode::INTERNAL_SERVER_ERROR, + } + } + pub fn with_user_message(self, user_message: impl Into) -> Self { + Self { + user_message: user_message.into(), + ..self + } + } + // pub fn with_code(self, code: StatusCode) -> Self { + // Self { + // code, + // ..self + // } + // } +} + +impl IntoResponse for AppError { + fn into_response(self) -> axum::response::Response { + println!("AppError: {}", self.message); + ( + self.code, + Html(format!( + r#" + + + + + Oops! + + +

Oops!

+

Sorry, but something went wrong.

+

{}

+ + + "#, + self.user_message + )), + ) + .into_response() + } +} + +impl From for AppError { + fn from(err: minijinja::Error) -> Self { + AppError::new(format!("Template error: {:#}", err)) + } +} + +impl From for AppError { + fn from(err: dotenvy::Error) -> Self { + AppError::new(format!("Dotenv error: {:#}", err)) + } +} + +impl From for AppError { + fn from(err: sqlx::Error) -> Self { + AppError::new(format!("Database query error: {:#}", err)) + } +} + +impl From for AppError { + fn from(err: String) -> Self { + AppError::new(err) + } +} + +impl From<&str> for AppError { + fn from(err: &str) -> Self { + AppError::new(err) + } +} diff --git a/backend/src/google_oauth.rs b/backend/src/google_oauth.rs index 13f6181..99a43fe 100644 --- a/backend/src/google_oauth.rs +++ b/backend/src/google_oauth.rs @@ -4,8 +4,7 @@ use axum::{ response::{IntoResponse, Redirect}, }; use oauth2::{ - basic::BasicClient, AuthUrl, ClientId, - ClientSecret, CsrfToken, PkceCodeChallenge, RedirectUrl, Scope, TokenUrl, + basic::BasicClient, reqwest::async_http_client, AuthUrl, AuthorizationCode, ClientId, ClientSecret, CsrfToken, PkceCodeChallenge, RedirectUrl, Scope, TokenResponse, TokenUrl }; use serde::Deserialize; use std::env; @@ -46,13 +45,13 @@ pub async fn google_authorized(_session: Session, println!("{:?}", query); // Get an auth token - //let token = match google_oauth_client - //.exchange_code(AuthorizationCode::new(query.code.clone())) - //.request_async(async_http_client) - //.await { -// Ok(token) => token, -// Err(_) => panic!("Didn't get a token"), -// }; + let token = match google_oauth_client() + .exchange_code(AuthorizationCode::new(query.code.clone())) + .request_async(async_http_client) + .await { + Ok(token) => token, + Err(_) => panic!("Didn't get a token"), + }; /* // Fetch user data from google let client = reqwest::Client::new(); @@ -77,7 +76,8 @@ pub async fn google_authorized(_session: Session, page.push_str(&query.state); page.push_str(&"\nCode: ".to_string()); page.push_str(&query.code); - page.push_str(&"\nScope: ".to_string()); + page.push_str(&"\nAccess Token: ".to_string()); + page.push_str(&token.access_token().secret()); page } diff --git a/backend/src/initialmain.rs b/backend/src/initialmain.rs deleted file mode 100644 index 394c6c4..0000000 --- a/backend/src/initialmain.rs +++ /dev/null @@ -1,350 +0,0 @@ -use askama::Template; -use async_session::{MemoryStore, Session, SessionStore as _}; -use axum::{ - async_trait, - extract::{ - rejection::TypedHeaderRejectionReason, Extension, FromRequest, RequestParts, - TypedHeader, - }, - headers::Cookie, - http::{ - self, - header::{HeaderValue}, - StatusCode - }, - response::{Html, IntoResponse, Redirect, Response}, - routing::{get, get_service}, - Router, -}; -use http::{header}; -use oauth2::{ - basic::BasicClient, -}; -use serde::{Deserialize, Serialize}; -use std::{net::SocketAddr, collections::HashMap}; -use tower_http::services::ServeDir; -use uuid::Uuid; - -use sqlx::{PgPool}; -use anyhow::*; -use sqlx::postgres::PgPoolOptions; - -mod db; - -use db::*; - -mod google_oauth; -mod facebook_oauth; -mod discord_oauth; - -use google_oauth::*; -use facebook_oauth::*; -use discord_oauth::*; - -const COOKIE_NAME: &str = "SESSION"; - -// The user data we'll get back from Discord. -// https://discord.com/developers/docs/resources/user#user-object-user-structure -#[derive(Debug, Serialize, Deserialize)] -struct User { - id: String, - avatar: Option, - username: String, - discriminator: String, -} - -#[tokio::main] -async fn main() { - // Set the default environment variables - if std::env::var_os("RUST_LOG").is_none() { - std::env::set_var("RUST_LOG", "example_sessions=debug") - } - if std::env::var_os("GOOGLE_CLIENT_ID").is_none() { - std::env::set_var("GOOGLE_CLIENT_ID", "735264084619-clsmvgdqdmum4rvrcj0kuk28k9agir1c.apps.googleusercontent.com") - } - if std::env::var_os("GOOGLE_CLIENT_SECRET").is_none() { - std::env::set_var("GOOGLE_CLIENT_SECRET", "L6uI7FQGoMJd-ay1HO_iGJ6M") - } - if std::env::var_os("DISCORD_CLIENT_ID").is_none() { - std::env::set_var("DISCORD_CLIENT_ID", "956189108559036427") - } - if std::env::var_os("DISCORD_CLIENT_SECRET").is_none() { - std::env::set_var("DISCORD_CLIENT_SECRET", "dx2DZxjDhVMCCnGX4xpz5MxSTgZ4lHBI") - } - if std::env::var_os("FACEBOOK_CLIENT_ID").is_none() { - std::env::set_var("FACEBOOK_CLIENT_ID", "1529124327484248") - } - if std::env::var_os("FACEBOOK_CLIENT_SECRET").is_none() { - std::env::set_var("FACEBOOK_CLIENT_SECRET", "189509b5eb907b3ce34b7e8459030f21") - } - - // initialize tracing - tracing_subscriber::fmt::init(); - - // Initialize database - let db = DBApplication::new("postgres://postgres:postgres@localhost/sqlx-demo".into()).await?; - println!("Connection acquired!"); - - - // `MemoryStore` just used as an example. Don't use this in production. - let store = MemoryStore::new(); - - // Create HashMap to store oauth configurations - let mut oauth_clients = HashMap::<&str, BasicClient>::new(); - - // Get the client structures - let facebook_oauth_client = facebook_oauth_client(); - let discord_oauth_client = discord_oauth_client(); - let google_oauth_client = google_oauth_client(); - - // Get oauth clients for the hashmap - oauth_clients.insert("Facebook", facebook_oauth_client); - oauth_clients.insert("Discord", discord_oauth_client); - oauth_clients.insert("Google", google_oauth_client); - - // build our application with a route - let app = Router::new() - // `GET /` goes to `root` - .nest( - "/assets", - get_service(ServeDir::new("templates/assets")).handle_error( - |error: std::io::Error| async move { - ( - StatusCode::INTERNAL_SERVER_ERROR, - format!("Unhandled internal error: {}", error), - ) - }, - ), - ) - .route("/", get(index)) - .route("/login", get(login)) - .route("/logout", get(logout)) - .route("/dashboard", get(dashboard)) - .route("/google_auth", get(google_auth)) - .route("/auth/google", get(google_authorized)) - .route("/facebook_auth", get(facebook_auth)) - .route("/auth/facebook", get(facebook_authorized)) - .route("/discord_auth", get(discord_auth)) - .route("/auth/discord", get(discord_authorized)) - .layer(Extension(store)) - .layer(Extension(oauth_clients)); - - // run our app with hyper - // `axum::Server` is a re-export of `hyper::Server` - let addr = SocketAddr::from(([0, 0, 0, 0], 40192)); - tracing::debug!("listening on {}", addr); - axum::Server::bind(&addr) - .serve(app.into_make_service()) - .await - .unwrap(); -} - -// Session is optional -async fn index(user: Option) -> impl IntoResponse { - let (userid, name) = match user { - Some(u) => (true, u.username), - None => (false, "You're not logged in.".to_string()), - }; - let template = IndexTemplate { userid, name }; - HtmlTemplate(template) -} - -async fn login() -> impl IntoResponse { - let name = "".to_string(); - let userid = false; - let template = LoginTemplate { userid, name }; - HtmlTemplate(template) -} - -// Valid user session required. If there is none, redirect to the auth page -async fn dashboard(user: User) -> impl IntoResponse { - let name = user.username; - let userid = true; - let template = DashboardTemplate { userid, name }; - HtmlTemplate(template) -} - -#[derive(Template)] -#[template(path = "login.html")] -struct LoginTemplate { - userid: bool, - name: String, -} - -#[derive(Template)] -#[template(path = "dashboard.html")] -struct DashboardTemplate { - userid: bool, - name: String, -} - -struct FreshUserId { - pub user_id: UserId, - pub cookie: HeaderValue, -} - -enum UserIdFromSession { - FoundUserId(UserId), - CreatedFreshUserId(FreshUserId), -} - -#[async_trait] -impl FromRequest for UserIdFromSession -where - B: Send, -{ - type Rejection = (StatusCode, &'static str); - - async fn from_request(req: &mut RequestParts) -> Result { - let Extension(store) = Extension::::from_request(req) - .await - .expect("`MemoryStore` extension missing"); - - let cookie = Option::>::from_request(req) - .await - .unwrap(); - - let session_cookie = cookie.as_ref().and_then(|cookie| cookie.get(COOKIE_NAME)); - - // return the new created session cookie for client - if session_cookie.is_none() { - let user_id = UserId::new(); - let mut session = Session::new(); - session.insert("user_id", user_id).unwrap(); - let cookie = store.store_session(session).await.unwrap().unwrap(); - return Ok(Self::CreatedFreshUserId(FreshUserId { - user_id, - cookie: HeaderValue::from_str(format!("{}={}", COOKIE_NAME, cookie).as_str()) - .unwrap(), - })); - } - - tracing::debug!( - "UserIdFromSession: got session cookie from user agent, {}={}", - COOKIE_NAME, - session_cookie.unwrap() - ); - // continue to decode the session cookie - let user_id = if let Some(session) = store - .load_session(session_cookie.unwrap().to_owned()) - .await - .unwrap() - { - if let Some(user_id) = session.get::("user_id") { - tracing::debug!( - "UserIdFromSession: session decoded success, user_id={:?}", - user_id - ); - user_id - } else { - return Err(( - StatusCode::INTERNAL_SERVER_ERROR, - "No `user_id` found in session", - )); - } - } else { - tracing::debug!( - "UserIdFromSession: err session not exists in store, {}={}", - COOKIE_NAME, - session_cookie.unwrap() - ); - return Err((StatusCode::BAD_REQUEST, "No session found for cookie")); - }; - - Ok(Self::FoundUserId(user_id)) - } -} - -#[derive(Serialize, Deserialize, Debug, Clone, Copy)] -struct UserId(Uuid); - -impl UserId { - fn new() -> Self { - Self(Uuid::new_v4()) - } -} - -#[derive(Template)] -#[template(path = "index.html")] -struct IndexTemplate { - userid: bool, - name: String, -} - -struct HtmlTemplate(T); - -impl IntoResponse for HtmlTemplate -where - T: Template, -{ - fn into_response(self) -> Response { - match self.0.render() { - Ok(html) => Html(html).into_response(), - Err(err) => ( - StatusCode::INTERNAL_SERVER_ERROR, - format!("Failed to render template. Error: {}", err), - ) - .into_response(), - } - } -} - -async fn logout( - Extension(store): Extension, - TypedHeader(cookies): TypedHeader, -) -> impl IntoResponse { - let cookie = cookies.get(COOKIE_NAME).unwrap(); - let session = match store.load_session(cookie.to_string()).await.unwrap() { - Some(s) => s, - // No session active, just redirect - None => return Redirect::to(&"/"), - }; - - store.destroy_session(session).await.unwrap(); - - Redirect::to(&"/") -} - -struct AuthRedirect; - -impl IntoResponse for AuthRedirect { - fn into_response(self) -> Response { - Redirect::temporary(&"/login").into_response() - } -} - -#[async_trait] -impl FromRequest for User -where - B: Send, -{ - // If anything goes wrong or no session is found, redirect to the auth page - type Rejection = AuthRedirect; - - async fn from_request(req: &mut RequestParts) -> Result { - let Extension(store) = Extension::::from_request(req) - .await - .expect("`MemoryStore` extension is missing"); - - let cookies = TypedHeader::::from_request(req) - .await - .map_err(|e| match *e.name() { - header::COOKIE => match e.reason() { - TypedHeaderRejectionReason::Missing => AuthRedirect, - _ => panic!("unexpected error getting Cookie header(s): {}", e), - }, - _ => panic!("unexpected error getting cookies: {}", e), - })?; - let session_cookie = cookies.get(COOKIE_NAME).ok_or(AuthRedirect)?; - - let session = store - .load_session(session_cookie.to_string()) - .await - .unwrap() - .ok_or(AuthRedirect)?; - - let user = session.get::("user").ok_or(AuthRedirect)?; - - Ok(user) - } -} diff --git a/backend/src/main.rs b/backend/src/main.rs index 15c489c..17fbfde 100644 --- a/backend/src/main.rs +++ b/backend/src/main.rs @@ -5,12 +5,21 @@ use axum::{ Router, routing::{get, get_service}, response::{Html, IntoResponse, Response}, }; +use axum_extra::extract::cookie::PrivateCookieJar; use http::StatusCode; use serde::{Serialize, Deserialize}; +use sqlx::{sqlite::SqlitePoolOptions, SqlitePool}; use tower_http::services::ServeDir; +mod error_handling; mod google_oauth; +mod middlewares; +mod oauth; + +use error_handling::AppError; use google_oauth::*; +use middlewares::{check_auth, inject_user_data}; +use oauth::{login, logout, oauth_return}; struct HtmlTemplate(T); @@ -51,6 +60,18 @@ struct DashboardTemplate { name: String, } +#[derive(Clone)] +pub struct AppState { + pub db_pool: SqlitePool, +} + +#[derive(Clone, Debug)] +pub struct UserData { + #[allow(dead_code)] + pub user_id: i64, + pub user_email: String, +} + #[derive(Debug, Serialize, Deserialize)] struct User { id: String, @@ -67,7 +88,17 @@ async fn main() { let session_config = SessionConfig::default() .with_table_name("sessions"); - let session_store = SessionStore::::new(None, session_config).await.unwrap(); + //let session_store = SessionStore::::new(None, session_config).await.unwrap(); + + let db_pool = SqlitePoolOptions::new() + .max_connections(5) + .connect("sqlite://db/db.sqlite3") + .await + .map_err(|e| format!("DB connection failed: {}", e))?; + + let app_state = AppState { + db_pool: db_pool + }; // Create HashMap to store oauth configurations // let mut oauth_clients = HashMap::<&str, BasicClient>::new(); @@ -91,7 +122,9 @@ async fn main() { .route("/login", get(login)) .route("/google_auth", get(google_auth)) .route("/auth/google", get(google_authorized)) - .layer(SessionLayer::new(session_store)) + .route("/logout", get(logout)) + //.layer(SessionLayer::new(session_store)) + .with_state(app_state) // .layer(Extension(oauth_clients)); ; @@ -112,7 +145,7 @@ async fn index(session: Session) -> impl IntoResponse { HtmlTemplate(template) } -async fn login(session: Session) -> impl IntoResponse { +/* async fn login(session: Session) -> impl IntoResponse { let logged_in = session.get("logged_in").unwrap_or(false); let name = session.get("name").unwrap_or("".to_string()); @@ -123,7 +156,7 @@ async fn login(session: Session) -> impl IntoResponse { let template = LoginTemplate { logged_in, name }; HtmlTemplate(template) -} +} */ async fn dashboard(session: Session) -> impl IntoResponse { let logged_in = session.get("logged_in").unwrap_or(false); diff --git a/backend/src/middlewares.rs b/backend/src/middlewares.rs new file mode 100644 index 0000000..ef2c6f9 --- /dev/null +++ b/backend/src/middlewares.rs @@ -0,0 +1,81 @@ +use super::{AppError, UserData}; +use axum::{ + body::Body, + extract::State, + http::Request, + middleware::Next, + response::{IntoResponse, Redirect}, +}; +use axum_extra::TypedHeader; +use chrono::Utc; +use headers::Cookie; +use sqlx::SqlitePool; + +pub async fn inject_user_data( + State(db_pool): State, + cookie: Option>, + mut request: Request, + next: Next, +) -> Result { + if let Some(cookie) = cookie { + if let Some(session_token) = cookie.get("session_token") { + let session_token: Vec<&str> = session_token.split('_').collect(); + let query: Result<(i64, i64, String), _> = sqlx::query_as( + r#"SELECT user_id,expires_at,session_token_p2 FROM user_sessions WHERE session_token_p1=?"#, + ) + .bind(session_token[0]) + .fetch_one(&db_pool) + .await; + + if let Ok(query) = query { + if let Ok(session_token_p2_db) = query.2.as_bytes().try_into() { + if let Ok(session_token_p2_cookie) = session_token + .get(1) + .copied() + .unwrap_or_default() + .as_bytes() + .try_into() + { + if constant_time_eq::constant_time_eq_n::<36>( + session_token_p2_cookie, + session_token_p2_db, + ) { + let user_id = query.0; + let expires_at = query.1; + if expires_at > Utc::now().timestamp() { + let query: Result<(String,), _> = + sqlx::query_as(r#"SELECT email FROM users WHERE id=?"#) + .bind(user_id) + .fetch_one(&db_pool) + .await; + if let Ok(query) = query { + let user_email = query.0; + request.extensions_mut().insert(Some(UserData { + user_id, + user_email, + })); + } + } + } + } + } + } + } + } + + Ok(next.run(request).await) +} + +pub async fn check_auth(request: Request, next: Next) -> Result { + if request + .extensions() + .get::>() + .ok_or("check_auth: extensions have no UserData")? + .is_some() + { + Ok(next.run(request).await) + } else { + let login_url = "/login?return_url=".to_owned() + &*request.uri().to_string(); + Ok(Redirect::to(login_url.as_str()).into_response()) + } +} diff --git a/backend/src/oauth.rs b/backend/src/oauth.rs index 0bd5470..155e20e 100644 --- a/backend/src/oauth.rs +++ b/backend/src/oauth.rs @@ -1,5 +1,229 @@ -pub struct OauthSession { - client: BasicClient, - pkce_code_verifier: PkceCodeVerifier, - csrf_state: CsrfToken +// Code adapted from https://github.com/ramosbugs/oauth2-rs/blob/main/examples/google.rs +// +// Must set the enviroment variables: +// GOOGLE_CLIENT_ID=xxx +// GOOGLE_CLIENT_SECRET=yyy + +use axum::{ + extract::{Extension, Host, Query, State}, + response::{IntoResponse, Redirect}, +}; +use axum_extra::TypedHeader; +use dotenvy::var; +use headers::Cookie; +use oauth2::{ + basic::BasicClient, reqwest::http_client, AuthUrl, AuthorizationCode, ClientId, ClientSecret, + CsrfToken, PkceCodeChallenge, PkceCodeVerifier, RedirectUrl, RevocationUrl, Scope, + TokenResponse, TokenUrl, +}; + +use chrono::Utc; +use sqlx::SqlitePool; +use std::collections::HashMap; +use uuid::Uuid; + +use super::{AppError, UserData}; + +fn get_client(hostname: String) -> Result { + let google_client_id = ClientId::new(var("GOOGLE_CLIENT_ID")?); + let google_client_secret = ClientSecret::new(var("GOOGLE_CLIENT_SECRET")?); + let auth_url = AuthUrl::new("https://accounts.google.com/o/oauth2/v2/auth".to_string()) + .map_err(|_| "OAuth: invalid authorization endpoint URL")?; + let token_url = TokenUrl::new("https://www.googleapis.com/oauth2/v3/token".to_string()) + .map_err(|_| "OAuth: invalid token endpoint URL")?; + + let protocol = if hostname.starts_with("localhost") || hostname.starts_with("127.0.0.1") { + "http" + } else { + "https" + }; + + let redirect_url = format!("{}://{}/oauth_return", protocol, hostname); + + // Set up the config for the Google OAuth2 process. + let client = BasicClient::new( + google_client_id, + Some(google_client_secret), + auth_url, + Some(token_url), + ) + .set_redirect_uri(RedirectUrl::new(redirect_url).map_err(|_| "OAuth: invalid redirect URL")?) + .set_revocation_uri( + RevocationUrl::new("https://oauth2.googleapis.com/revoke".to_string()) + .map_err(|_| "OAuth: invalid revocation endpoint URL")?, + ); + Ok(client) +} + +pub async fn login( + Extension(user_data): Extension>, + Query(mut params): Query>, + State(db_pool): State, + Host(hostname): Host, +) -> Result { + if user_data.is_some() { + // check if already authenticated + return Ok(Redirect::to("/")); + } + + let return_url = params + .remove("return_url") + .unwrap_or_else(|| "/".to_string()); + // TODO: check if return_url is valid + + let client = get_client(hostname)?; + + let (pkce_code_challenge, pkce_code_verifier) = PkceCodeChallenge::new_random_sha256(); + + let (authorize_url, csrf_state) = client + .authorize_url(CsrfToken::new_random) + .add_scope(Scope::new( + "https://www.googleapis.com/auth/userinfo.email".to_string(), + )) + .set_pkce_challenge(pkce_code_challenge) + .url(); + + sqlx::query( + "INSERT INTO oauth2_state_storage (csrf_state, pkce_code_verifier, return_url) VALUES (?, ?, ?);", + ) + .bind(csrf_state.secret()) + .bind(pkce_code_verifier.secret()) + .bind(return_url) + .execute(&db_pool) + .await?; + + Ok(Redirect::to(authorize_url.as_str())) +} + +pub async fn oauth_return( + Query(mut params): Query>, + State(db_pool): State, + Host(hostname): Host, +) -> Result { + let state = CsrfToken::new(params.remove("state").ok_or("OAuth: without state")?); + let code = AuthorizationCode::new(params.remove("code").ok_or("OAuth: without code")?); + + let query: (String, String) = sqlx::query_as( + r#"DELETE FROM oauth2_state_storage WHERE csrf_state = ? RETURNING pkce_code_verifier,return_url"#, + ) + .bind(state.secret()) + .fetch_one(&db_pool) + .await?; + + // Alternative: + // let query: (String, String) = sqlx::query_as( + // r#"SELECT pkce_code_verifier,return_url FROM oauth2_state_storage WHERE csrf_state = ?"#, + // ) + // .bind(state.secret()) + // .fetch_one(&db_pool) + // .await?; + // let _ = sqlx::query("DELETE FROM oauth2_state_storage WHERE csrf_state = ?") + // .bind(state.secret()) + // .execute(&db_pool) + // .await; + + let pkce_code_verifier = query.0; + let return_url = query.1; + let pkce_code_verifier = PkceCodeVerifier::new(pkce_code_verifier); + + // Exchange the code with a token. + let client = get_client(hostname)?; + let token_response = tokio::task::spawn_blocking(move || { + client + .exchange_code(code) + .set_pkce_verifier(pkce_code_verifier) + .request(http_client) + }) + .await + .map_err(|_| "OAuth: exchange_code failure")? + .map_err(|_| "OAuth: tokio spawn blocking failure")?; + let access_token = token_response.access_token().secret(); + + // Get user info from Google + let url = + "https://www.googleapis.com/oauth2/v2/userinfo?oauth_token=".to_owned() + access_token; + let body = reqwest::get(url) + .await + .map_err(|_| "OAuth: reqwest failed to query userinfo")? + .text() + .await + .map_err(|_| "OAuth: reqwest received invalid userinfo")?; + let mut body: serde_json::Value = + serde_json::from_str(body.as_str()).map_err(|_| "OAuth: Serde failed to parse userinfo")?; + let email = body["email"] + .take() + .as_str() + .ok_or("OAuth: Serde failed to parse email address")? + .to_owned(); + let verified_email = body["verified_email"] + .take() + .as_bool() + .ok_or("OAuth: Serde failed to parse verified_email")?; + if !verified_email { + return Err(AppError::new("OAuth: email address is not verified".to_owned()) + .with_user_message("Your email address is not verified. Please verify your email address with Google and try again.".to_owned())); + } + + // Check if user exists in database + // If not, create a new user + let query: Result<(i64,), _> = sqlx::query_as(r#"SELECT id FROM users WHERE email=?"#) + .bind(email.as_str()) + .fetch_one(&db_pool) + .await; + let user_id = if let Ok(query) = query { + query.0 + } else { + let query: (i64,) = sqlx::query_as("INSERT INTO users (email) VALUES (?) RETURNING id") + .bind(email) + .fetch_one(&db_pool) + .await?; + query.0 + }; + + // Create a session for the user + let session_token_p1 = Uuid::new_v4().to_string(); + let session_token_p2 = Uuid::new_v4().to_string(); + let session_token = [session_token_p1.as_str(), "_", session_token_p2.as_str()].concat(); + let headers = axum::response::AppendHeaders([( + axum::http::header::SET_COOKIE, + "session_token=".to_owned() + + &*session_token + + "; path=/; httponly; secure; samesite=strict", + )]); + let now = Utc::now().timestamp(); + + sqlx::query( + "INSERT INTO user_sessions + (session_token_p1, session_token_p2, user_id, created_at, expires_at) + VALUES (?, ?, ?, ?, ?);", + ) + .bind(session_token_p1) + .bind(session_token_p2) + .bind(user_id) + .bind(now) + .bind(now + 60 * 60 * 24) + .execute(&db_pool) + .await?; + + Ok((headers, Redirect::to(return_url.as_str()))) +} + +pub async fn logout( + cookie: Option>, + State(db_pool): State, +) -> Result { + if let Some(cookie) = cookie { + if let Some(session_token) = cookie.get("session_token") { + let session_token: Vec<&str> = session_token.split('_').collect(); + let _ = sqlx::query("DELETE FROM user_sessions WHERE session_token_1 = ?") + .bind(session_token[0]) + .execute(&db_pool) + .await; + } + } + let headers = axum::response::AppendHeaders([( + axum::http::header::SET_COOKIE, + "session_token=deleted; path=/; expires=Thu, 01 Jan 1970 00:00:00 GMT", + )]); + Ok((headers, Redirect::to("/"))) } diff --git a/backend/templates/about.html b/backend/templates/about.html new file mode 100644 index 0000000..969b23e --- /dev/null +++ b/backend/templates/about.html @@ -0,0 +1,5 @@ +{% extends "base.html" %} +{% block title %}About{% endblock %} +{% block content %} +This is a demo OAuth website. +{% endblock %} diff --git a/backend/templates/login.html b/backend/templates/login.html index dc0dc8e..3f3661e 100644 --- a/backend/templates/login.html +++ b/backend/templates/login.html @@ -38,14 +38,14 @@ src="/assets/icons/numix-circle/web-google.svg" width="100" alt="Login with Google"> - Login with Facebook Login with Discord - + --> diff --git a/backend/templates/profile.html b/backend/templates/profile.html new file mode 100644 index 0000000..d4a52ff --- /dev/null +++ b/backend/templates/profile.html @@ -0,0 +1,6 @@ +{% extends "base.html" %} +{% block title %}User Profile{% endblock %} +{% block content %} +This is your user profile page.
+Your email address: {{ user_email }}. +{% endblock %}