very rough SSO code

This commit is contained in:
Jackzie 2025-04-20 15:03:24 -05:00
parent ed5cbfa848
commit 60813de8cb
9 changed files with 546 additions and 15 deletions

383
Cargo.lock generated
View file

@ -53,6 +53,17 @@ version = "1.0.98"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e16d2d3311acee920a9eb8d33b8cbc1787ce4a264e85f964c2404b969bdcd487" checksum = "e16d2d3311acee920a9eb8d33b8cbc1787ce4a264e85f964c2404b969bdcd487"
[[package]]
name = "async-lock"
version = "3.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ff6e472cdea888a4bd64f342f09b3f50e1886d32afe8df3d663c01140b811b18"
dependencies = [
"event-listener",
"event-listener-strategy",
"pin-project-lite",
]
[[package]] [[package]]
name = "async-stream" name = "async-stream"
version = "0.3.6" version = "0.3.6"
@ -110,6 +121,12 @@ dependencies = [
"bytemuck", "bytemuck",
] ]
[[package]]
name = "atomic-waker"
version = "1.1.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1505bd5d3d116872e7271a6d4e16d81d0c8570876c8de68093a09ac269d8aac0"
[[package]] [[package]]
name = "autocfg" name = "autocfg"
version = "1.4.0" version = "1.4.0"
@ -304,6 +321,16 @@ dependencies = [
"version_check", "version_check",
] ]
[[package]]
name = "core-foundation"
version = "0.9.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "91e195e091a93c46f7102ec7818a2aa394e1e1771c3ab4825963fa03e45afb8f"
dependencies = [
"core-foundation-sys",
"libc",
]
[[package]] [[package]]
name = "core-foundation-sys" name = "core-foundation-sys"
version = "0.8.7" version = "0.8.7"
@ -343,6 +370,15 @@ dependencies = [
"crossbeam-utils", "crossbeam-utils",
] ]
[[package]]
name = "crossbeam-epoch"
version = "0.9.18"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5b82ac4a3c2ca9c3460964f020e1402edd5753411d7737aa39c3714ad1b5420e"
dependencies = [
"crossbeam-utils",
]
[[package]] [[package]]
name = "crossbeam-queue" name = "crossbeam-queue"
version = "0.3.12" version = "0.3.12"
@ -646,6 +682,16 @@ dependencies = [
"pin-project-lite", "pin-project-lite",
] ]
[[package]]
name = "event-listener-strategy"
version = "0.5.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8be9f3dfaaffdae2972880079a491a1a8bb7cbed0b8dd7a347f668b4150a3b93"
dependencies = [
"event-listener",
"pin-project-lite",
]
[[package]] [[package]]
name = "fastrand" name = "fastrand"
version = "2.3.0" version = "2.3.0"
@ -717,6 +763,21 @@ version = "0.1.5"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d9c4f5dac5e15c24eb999c26181a6ca40b39fe946cbe4c263c7209467bc83af2" checksum = "d9c4f5dac5e15c24eb999c26181a6ca40b39fe946cbe4c263c7209467bc83af2"
[[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.1" version = "1.2.1"
@ -793,6 +854,17 @@ version = "0.3.31"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9e5c1b78ca4aae1ac06c48a526a655760685149f0d465d21f37abfe57ce075c6" checksum = "9e5c1b78ca4aae1ac06c48a526a655760685149f0d465d21f37abfe57ce075c6"
[[package]]
name = "futures-macro"
version = "0.3.31"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "162ee34ebcb7c64a8abebc059ce0fee27c2262618d7b60ed8faf72fef13c3650"
dependencies = [
"proc-macro2",
"quote",
"syn",
]
[[package]] [[package]]
name = "futures-sink" name = "futures-sink"
version = "0.3.31" version = "0.3.31"
@ -814,6 +886,7 @@ dependencies = [
"futures-channel", "futures-channel",
"futures-core", "futures-core",
"futures-io", "futures-io",
"futures-macro",
"futures-sink", "futures-sink",
"futures-task", "futures-task",
"memchr", "memchr",
@ -832,7 +905,20 @@ dependencies = [
"libc", "libc",
"log", "log",
"rustversion", "rustversion",
"windows", "windows 0.48.0",
]
[[package]]
name = "generator"
version = "0.8.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "cc6bd114ceda131d3b1d665eba35788690ad37f5916457286b32ab6fd3c438dd"
dependencies = [
"cfg-if",
"libc",
"log",
"rustversion",
"windows 0.58.0",
] ]
[[package]] [[package]]
@ -915,6 +1001,25 @@ dependencies = [
"tracing", "tracing",
] ]
[[package]]
name = "h2"
version = "0.4.9"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "75249d144030531f8dee69fe9cea04d3edf809a017ae445e2abdff6629e86633"
dependencies = [
"atomic-waker",
"bytes",
"fnv",
"futures-core",
"futures-sink",
"http 1.3.1",
"indexmap 2.9.0",
"slab",
"tokio",
"tokio-util",
"tracing",
]
[[package]] [[package]]
name = "handlebars" name = "handlebars"
version = "5.1.2" version = "5.1.2"
@ -1093,7 +1198,7 @@ dependencies = [
"futures-channel", "futures-channel",
"futures-core", "futures-core",
"futures-util", "futures-util",
"h2", "h2 0.3.26",
"http 0.2.12", "http 0.2.12",
"http-body 0.4.6", "http-body 0.4.6",
"httparse", "httparse",
@ -1116,6 +1221,7 @@ dependencies = [
"bytes", "bytes",
"futures-channel", "futures-channel",
"futures-util", "futures-util",
"h2 0.4.9",
"http 1.3.1", "http 1.3.1",
"http-body 1.0.1", "http-body 1.0.1",
"httparse", "httparse",
@ -1144,6 +1250,22 @@ dependencies = [
"webpki-roots", "webpki-roots",
] ]
[[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.6.0",
"hyper-util",
"native-tls",
"tokio",
"tokio-native-tls",
"tower-service",
]
[[package]] [[package]]
name = "hyper-util" name = "hyper-util"
version = "0.1.11" version = "0.1.11"
@ -1176,7 +1298,7 @@ dependencies = [
"js-sys", "js-sys",
"log", "log",
"wasm-bindgen", "wasm-bindgen",
"windows-core", "windows-core 0.61.0",
] ]
[[package]] [[package]]
@ -1541,7 +1663,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ff50ecb28bb86013e935fb6683ab1f6d3a20016f123c76fd4c27470076ac30f5" checksum = "ff50ecb28bb86013e935fb6683ab1f6d3a20016f123c76fd4c27470076ac30f5"
dependencies = [ dependencies = [
"cfg-if", "cfg-if",
"generator", "generator 0.7.5",
"scoped-tls", "scoped-tls",
"serde", "serde",
"serde_json", "serde_json",
@ -1549,6 +1671,19 @@ dependencies = [
"tracing-subscriber", "tracing-subscriber",
] ]
[[package]]
name = "loom"
version = "0.7.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "419e0dc8046cb947daa77eb95ae174acfbddb7673b4151f56d1eed8e93fbfaca"
dependencies = [
"cfg-if",
"generator 0.8.4",
"scoped-tls",
"tracing",
"tracing-subscriber",
]
[[package]] [[package]]
name = "matchers" name = "matchers"
version = "0.1.0" version = "0.1.0"
@ -1612,6 +1747,28 @@ dependencies = [
"windows-sys 0.52.0", "windows-sys 0.52.0",
] ]
[[package]]
name = "moka"
version = "0.12.10"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a9321642ca94a4282428e6ea4af8cc2ca4eac48ac7a6a4ea8f33f76d0ce70926"
dependencies = [
"async-lock",
"crossbeam-channel",
"crossbeam-epoch",
"crossbeam-utils",
"event-listener",
"futures-util",
"loom 0.7.2",
"parking_lot",
"portable-atomic",
"rustc_version",
"smallvec",
"tagptr",
"thiserror 1.0.69",
"uuid",
]
[[package]] [[package]]
name = "multer" name = "multer"
version = "3.1.0" version = "3.1.0"
@ -1631,6 +1788,23 @@ dependencies = [
"version_check", "version_check",
] ]
[[package]]
name = "native-tls"
version = "0.2.14"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "87de3442987e9dbec73158d5c715e7ad9072fda936bb03d19d7fa10e00520f0e"
dependencies = [
"libc",
"log",
"openssl",
"openssl-probe",
"openssl-sys",
"schannel",
"security-framework",
"security-framework-sys",
"tempfile",
]
[[package]] [[package]]
name = "normpath" name = "normpath"
version = "1.3.0" version = "1.3.0"
@ -1798,6 +1972,50 @@ dependencies = [
"url", "url",
] ]
[[package]]
name = "openssl"
version = "0.10.71"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5e14130c6a98cd258fdcb0fb6d744152343ff729cbfcb28c656a9d12b999fbcd"
dependencies = [
"bitflags 2.9.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.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d05e27ee213611ffe7d6348b942e8f942b37114c00cc03cec254295a4a17852e"
[[package]]
name = "openssl-sys"
version = "0.9.106"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8bb61ea9811cc39e3c2069f40b8b8e2e70d8569b361f879786cc7ed48b777cdd"
dependencies = [
"cc",
"libc",
"pkg-config",
"vcpkg",
]
[[package]] [[package]]
name = "ordered-float" name = "ordered-float"
version = "2.10.1" version = "2.10.1"
@ -1988,6 +2206,12 @@ version = "0.3.32"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7edddbd0b52d732b21ad9a5fab5c704c14cd949e5e9a1ec5929a24fded1b904c" checksum = "7edddbd0b52d732b21ad9a5fab5c704c14cd949e5e9a1ec5929a24fded1b904c"
[[package]]
name = "portable-atomic"
version = "1.11.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "350e9b48cbc6b0e028b0473b114454c6316e57336ee184ceab6e53f72c178b3e"
[[package]] [[package]]
name = "powerfmt" name = "powerfmt"
version = "0.2.0" version = "0.2.0"
@ -2244,18 +2468,22 @@ checksum = "d19c46a6fdd48bc4dab94b6103fccc55d34c67cc0ad04653aad4ea2a07cd7bbb"
dependencies = [ dependencies = [
"base64 0.22.1", "base64 0.22.1",
"bytes", "bytes",
"encoding_rs",
"futures-core", "futures-core",
"futures-util", "futures-util",
"h2 0.4.9",
"http 1.3.1", "http 1.3.1",
"http-body 1.0.1", "http-body 1.0.1",
"http-body-util", "http-body-util",
"hyper 1.6.0", "hyper 1.6.0",
"hyper-rustls", "hyper-rustls",
"hyper-tls",
"hyper-util", "hyper-util",
"ipnet", "ipnet",
"js-sys", "js-sys",
"log", "log",
"mime", "mime",
"native-tls",
"once_cell", "once_cell",
"percent-encoding", "percent-encoding",
"pin-project-lite", "pin-project-lite",
@ -2267,7 +2495,9 @@ dependencies = [
"serde_json", "serde_json",
"serde_urlencoded", "serde_urlencoded",
"sync_wrapper", "sync_wrapper",
"system-configuration",
"tokio", "tokio",
"tokio-native-tls",
"tokio-rustls", "tokio-rustls",
"tower", "tower",
"tower-service", "tower-service",
@ -2531,6 +2761,15 @@ dependencies = [
"winapi-util", "winapi-util",
] ]
[[package]]
name = "schannel"
version = "0.1.27"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1f29ebaa345f945cec9fbbc532eb307f0fdad8161f281b6369539c8d84876b3d"
dependencies = [
"windows-sys 0.59.0",
]
[[package]] [[package]]
name = "scoped-tls" name = "scoped-tls"
version = "1.0.1" version = "1.0.1"
@ -2557,6 +2796,29 @@ dependencies = [
"zeroize", "zeroize",
] ]
[[package]]
name = "security-framework"
version = "2.11.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "897b2245f0b511c87893af39b033e5ca9cce68824c4d7e7630b5a1d339658d02"
dependencies = [
"bitflags 2.9.0",
"core-foundation",
"core-foundation-sys",
"libc",
"security-framework-sys",
]
[[package]]
name = "security-framework-sys"
version = "2.14.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "49db231d56a190491cb4aeda9527f1ad45345af50b0851622a7adb8c03b01c32"
dependencies = [
"core-foundation-sys",
"libc",
]
[[package]] [[package]]
name = "semver" name = "semver"
version = "1.0.26" version = "1.0.26"
@ -3005,7 +3267,7 @@ version = "0.6.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2b8c4a4445d81357df8b1a650d0d0d6fbbbfe99d064aa5e02f3e4022061476d8" checksum = "2b8c4a4445d81357df8b1a650d0d0d6fbbbfe99d064aa5e02f3e4022061476d8"
dependencies = [ dependencies = [
"loom", "loom 0.5.6",
] ]
[[package]] [[package]]
@ -3025,8 +3287,10 @@ dependencies = [
"humanize-bytes", "humanize-bytes",
"int-enum", "int-enum",
"log", "log",
"moka",
"openidconnect", "openidconnect",
"rand 0.9.0", "rand 0.9.0",
"reqwest",
"rocket", "rocket",
"rocket-session-store", "rocket-session-store",
"rocket_dyn_templates", "rocket_dyn_templates",
@ -3092,6 +3356,33 @@ dependencies = [
"syn", "syn",
] ]
[[package]]
name = "system-configuration"
version = "0.6.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3c879d448e9d986b661742763247d3693ed13609438cf3d006f51f5368a5ba6b"
dependencies = [
"bitflags 2.9.0",
"core-foundation",
"system-configuration-sys",
]
[[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 = "tagptr"
version = "0.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7b2093cf4c8eb1e67749a6762251bc9cd836b6fc171623bd0a9d324d37af2417"
[[package]] [[package]]
name = "tempfile" name = "tempfile"
version = "3.19.1" version = "3.19.1"
@ -3240,6 +3531,16 @@ dependencies = [
"syn", "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]] [[package]]
name = "tokio-rustls" name = "tokio-rustls"
version = "0.26.2" version = "0.26.2"
@ -3715,19 +4016,53 @@ dependencies = [
"windows-targets 0.48.5", "windows-targets 0.48.5",
] ]
[[package]]
name = "windows"
version = "0.58.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "dd04d41d93c4992d421894c18c8b43496aa748dd4c081bac0dc93eb0489272b6"
dependencies = [
"windows-core 0.58.0",
"windows-targets 0.52.6",
]
[[package]]
name = "windows-core"
version = "0.58.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6ba6d44ec8c2591c134257ce647b7ea6b20335bf6379a27dac5f1641fcf59f99"
dependencies = [
"windows-implement 0.58.0",
"windows-interface 0.58.0",
"windows-result 0.2.0",
"windows-strings 0.1.0",
"windows-targets 0.52.6",
]
[[package]] [[package]]
name = "windows-core" name = "windows-core"
version = "0.61.0" version = "0.61.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4763c1de310c86d75a878046489e2e5ba02c649d185f21c67d4cf8a56d098980" checksum = "4763c1de310c86d75a878046489e2e5ba02c649d185f21c67d4cf8a56d098980"
dependencies = [ dependencies = [
"windows-implement", "windows-implement 0.60.0",
"windows-interface", "windows-interface 0.59.1",
"windows-link", "windows-link",
"windows-result", "windows-result 0.3.2",
"windows-strings 0.4.0", "windows-strings 0.4.0",
] ]
[[package]]
name = "windows-implement"
version = "0.58.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2bbd5b46c938e506ecbce286b6628a02171d56153ba733b6c741fc627ec9579b"
dependencies = [
"proc-macro2",
"quote",
"syn",
]
[[package]] [[package]]
name = "windows-implement" name = "windows-implement"
version = "0.60.0" version = "0.60.0"
@ -3739,6 +4074,17 @@ dependencies = [
"syn", "syn",
] ]
[[package]]
name = "windows-interface"
version = "0.58.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "053c4c462dc91d3b1504c6fe5a726dd15e216ba718e84a0e46a88fbe5ded3515"
dependencies = [
"proc-macro2",
"quote",
"syn",
]
[[package]] [[package]]
name = "windows-interface" name = "windows-interface"
version = "0.59.1" version = "0.59.1"
@ -3762,11 +4108,20 @@ 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 = "4286ad90ddb45071efd1a66dfa43eb02dd0dfbae1545ad6cc3c51cf34d7e8ba3" checksum = "4286ad90ddb45071efd1a66dfa43eb02dd0dfbae1545ad6cc3c51cf34d7e8ba3"
dependencies = [ dependencies = [
"windows-result", "windows-result 0.3.2",
"windows-strings 0.3.1", "windows-strings 0.3.1",
"windows-targets 0.53.0", "windows-targets 0.53.0",
] ]
[[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]] [[package]]
name = "windows-result" name = "windows-result"
version = "0.3.2" version = "0.3.2"
@ -3776,6 +4131,16 @@ dependencies = [
"windows-link", "windows-link",
] ]
[[package]]
name = "windows-strings"
version = "0.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4cd9b125c486025df0eabcb585e62173c6c9eddcec5d117d3b6e8c30e2ee4d10"
dependencies = [
"windows-result 0.2.0",
"windows-targets 0.52.6",
]
[[package]] [[package]]
name = "windows-strings" name = "windows-strings"
version = "0.3.1" version = "0.3.1"

View file

@ -23,4 +23,6 @@ rocket-session-store = "0.2.1"
uuid = { version = "1.16.0", features = ["v4"] } uuid = { version = "1.16.0", features = ["v4"] }
rand = { version = "0.9.0", features = ["thread_rng"] } rand = { version = "0.9.0", features = ["thread_rng"] }
bcrypt = "0.17.0" bcrypt = "0.17.0"
openidconnect = "4.0.0" openidconnect = "4.0.0"
reqwest = "0.12.15"
moka = { version = "0.12.10", features = ["future"] }

View file

@ -1,6 +1,11 @@
[general] [general]
listen_ip = "0.0.0.0" listen_ip = "0.0.0.0"
listen_port = 80 listen_port = 80
# if under reverse proxy
#public_url = "https://storage.example.com"
#public_port = 443
public_url = "http://localhost:8080"
public_port = 80
[backends.local] [backends.local]
path = "/var/tmp/test" path = "/var/tmp/test"

View file

@ -1,6 +1,6 @@
use std::cell::OnceCell; use std::cell::OnceCell;
use std::env; use std::env;
use std::sync::OnceLock; use std::sync::{LazyLock, OnceLock};
use std::time::Duration; use std::time::Duration;
use rocket::data::ByteUnit; use rocket::data::ByteUnit;
use rocket::serde::Serialize; use rocket::serde::Serialize;
@ -29,7 +29,8 @@ pub const FILE_CONSTANTS: FileConstants = FileConstants {
/// Disables CSRF & password verification for login /// Disables CSRF & password verification for login
/// Used for development due to no session persistence /// Used for development due to no session persistence
pub static DISABLE_LOGIN_CHECK: OnceLock<bool> = OnceLock::new(); pub static DISABLE_LOGIN_CHECK: LazyLock<bool> = LazyLock::new(|| {
env::var("DANGER_DISABLE_LOGIN_CHECKS").is_ok()
});
pub fn init_statics() { pub fn init_statics() {
DISABLE_LOGIN_CHECK.set(env::var("DANGER_DISABLE_LOGIN_CHECKS").is_ok()).unwrap();
} }

View file

@ -144,6 +144,7 @@ async fn rocket() -> _ {
.mount("/", routes![ .mount("/", routes![
ui::auth::logout, ui::auth::logout,
ui::auth::login::page, ui::auth::login::handler, ui::auth::register::page, ui::auth::register::handler, ui::auth::login::page, ui::auth::login::handler, ui::auth::register::page, ui::auth::register::handler,
ui::auth::sso::page, ui::auth::sso::callback,
ui::auth::forgot_password::page, ui::auth::forgot_password::handler, ui::auth::forgot_password::page, ui::auth::forgot_password::handler,
]) ])
.mount("/", routes![ .mount("/", routes![

View file

@ -124,7 +124,7 @@ pub async fn validate_user(pool: &DB, email_or_usrname: &str, password: &str) ->
return Err(UserAuthError::UserNotFound); return Err(UserAuthError::UserNotFound);
}; };
if let Some(db_password) = user.password { if let Some(db_password) = user.password {
if !DISABLE_LOGIN_CHECK.get().unwrap() || bcrypt::verify(password, &db_password).map_err(|e| UserAuthError::EncryptionError(e))? { if !*DISABLE_LOGIN_CHECK || bcrypt::verify(password, &db_password).map_err(|e| UserAuthError::EncryptionError(e))? {
return Ok(UserModel { return Ok(UserModel {
id: user.id, id: user.id,
email: user.email, email: user.email,

View file

@ -20,6 +20,8 @@ pub mod forgot_password;
pub mod login; pub mod login;
pub mod register; pub mod register;
pub mod sso;
#[get("/logout")] #[get("/logout")]
pub async fn logout(session: Session<'_, SessionData>, user: AuthUser) -> Redirect { pub async fn logout(session: Session<'_, SessionData>, user: AuthUser) -> Redirect {
session.remove().await.unwrap(); session.remove().await.unwrap();

View file

@ -61,7 +61,7 @@ pub async fn handler(
return_to: Option<String>, return_to: Option<String>,
) -> Result<HackyRedirectBecauseRocketBug, Template> { ) -> Result<HackyRedirectBecauseRocketBug, Template> {
trace!("handler"); trace!("handler");
if !DISABLE_LOGIN_CHECK.get().unwrap() { if !*DISABLE_LOGIN_CHECK {
validate_csrf_form(&mut form.context, &session).await; validate_csrf_form(&mut form.context, &session).await;
} }
let user = validate_user_form(&mut form.context, &pool).await; let user = validate_user_form(&mut form.context, &pool).await;

155
src/routes/ui/auth/sso.rs Normal file
View file

@ -0,0 +1,155 @@
use std::env::var;
use std::net::IpAddr;
use std::sync::{LazyLock, OnceLock};
use std::time::Duration;
use anyhow::anyhow;
use moka::future::Cache;
use rocket::{get, post, uri};
use rocket::response::Redirect;
use rocket_session_store::Session;
use crate::guards::AuthUser;
use crate::SessionData;
use openidconnect::{reqwest, AccessTokenHash, AuthenticationFlow, AuthorizationCode, Client, ClientId, ClientSecret, CsrfToken, EmptyAdditionalClaims, HttpClientError, IssuerUrl, Nonce, OAuth2TokenResponse, PkceCodeChallenge, PkceCodeVerifier, ProviderMetadata, RedirectUrl, Scope, StandardErrorResponse, TokenResponse};
use openidconnect::core::{CoreAuthDisplay, CoreAuthPrompt, CoreAuthenticationFlow, CoreClient, CoreGenderClaim, CoreJsonWebKey, CoreJweContentEncryptionAlgorithm, CoreProviderMetadata, CoreTokenResponse, CoreUserInfoClaims};
// TODO: not have this lazy somehow, move to OnceLock and have fn to refresh it? (own module?)
// and/or also move to State<>
static HTTP_CLIENT: LazyLock<reqwest::Client> = LazyLock::new(|| {
reqwest::ClientBuilder::new()
// Following redirects opens the client up to SSRF vulnerabilities.
.redirect(reqwest::redirect::Policy::none())
.build()
.expect("Client should build")
});
#[derive(Clone)]
struct SSOSessionData {
pkce_challenge: String,
nonce: Nonce,
csrf_token: CsrfToken
// ip: IpAddr,
}
static SSO_SESSION_CACHE: LazyLock<Cache<IpAddr, SSOSessionData>> = LazyLock::new(|| Cache::builder()
.time_to_live(Duration::from_secs(120))
.max_capacity(100)
.build());
#[get("/auth/sso")]
pub async fn page(session: Session<'_, SessionData>, ip: IpAddr) -> Redirect {
let s = session.get().await.unwrap().unwrap();
let http_client = HTTP_CLIENT.clone();
// FIXME: temp, remove
let provider_metadata = CoreProviderMetadata::discover_async(
/* TODO: pull from config */
IssuerUrl::new(var("SSO_ISSUER_URL").expect("dev: missing sso url")).expect("bad issuer url"),
&http_client,
).await.expect("discovery failed");
let client =
CoreClient::from_provider_metadata(
provider_metadata,
// TODO: pull from config
ClientId::new(var("SSO_CLIENT_ID").expect("dev: sso client id missing")),
Some(ClientSecret::new(var("SSO_CLIENT_SECRET").expect("dev sso client secret missing")
.to_string())),
).set_redirect_uri(RedirectUrl::new("http://localhost:8080/auth/sso/cb".to_string()).unwrap());
// Generate a PKCE challenge.
// TODO: store in hashmap for request ip? leaky bucket?
let (pkce_challenge, pkce_verifier) = PkceCodeChallenge::new_random_sha256();
// Generate the full authorization URL.
let (auth_url, csrf_token, nonce) = client
.authorize_url(
CoreAuthenticationFlow::AuthorizationCode,
CsrfToken::new_random,
Nonce::new_random,
)
// Set the desired scopes.
// TODO: change scopes
.add_scope(Scope::new("read".to_string()))
.add_scope(Scope::new("write".to_string()))
// Set the PKCE code challenge.
.set_pkce_challenge(pkce_challenge)
.url();
SSO_SESSION_CACHE.insert(ip, SSOSessionData {
nonce: nonce,
pkce_challenge: pkce_verifier.into_secret(),
csrf_token
}).await;
Redirect::to(auth_url.to_string())
// This is the URL you should redirect the user to, in order to trigger the authorization
// process.
}
#[post("/auth/sso/cb?<state>&<code>")]
pub async fn callback(session: Session<'_, SessionData>, ip: IpAddr, code: String, state: String) -> Result<String, String> {
let session_data = SSO_SESSION_CACHE.remove(&ip).await.ok_or_else(|| "no sso session started".to_string())?;
// Now you can exchange it for an access token and ID token.
if &state != session_data.csrf_token.secret() {
return Err(format!("csrf validation failed {}", state));
}
// FIXME: temp, remove
let http_client = HTTP_CLIENT.clone();
let provider_metadata = CoreProviderMetadata::discover_async(
/* TODO: pull from config */
IssuerUrl::new(var("SSO_ISSUER_URL").expect("dev: missing sso url")).expect("bad issuer url"),
&http_client,
).await.expect("discovery failed");
let client =
CoreClient::from_provider_metadata(
provider_metadata,
// TODO: pull from config
ClientId::new(var("SSO_CLIENT_ID").expect("dev: sso client id missing")),
Some(ClientSecret::new(var("SSO_CLIENT_SECRET").expect("dev sso client secret missing")
.to_string())),
).set_redirect_uri(RedirectUrl::new("http://localhost:8080/auth/sso/cb".to_string()).unwrap());
let token_response =
client
.exchange_code(AuthorizationCode::new(code)).expect("bad auth code")
// Set the PKCE code verifier.
.set_pkce_verifier(PkceCodeVerifier::new(session_data.pkce_challenge)) // TODO: somehow have this??
.request_async(&http_client).await.expect("token exchange error");
// Extract the ID token claims after verifying its authenticity and nonce.
let id_token = token_response
.id_token()
.ok_or_else(|| "Server did not return an ID token".to_string())?;
let id_token_verifier = client.id_token_verifier();
let claims = id_token.claims(&id_token_verifier, &session_data.nonce).expect("bad claims"); // TODO: and this?
// Verify the access token hash to ensure that the access token hasn't been substituted for
// another user's.
if let Some(expected_access_token_hash) = claims.access_token_hash() {
let actual_access_token_hash = AccessTokenHash::from_token(
token_response.access_token(),
id_token.signing_alg().expect("signing failed (alg)"),
id_token.signing_key(&id_token_verifier).expect("signing failed (key)"),
).expect("access token resolve error");
if actual_access_token_hash != *expected_access_token_hash {
return Err("Invalid access token".to_string());
}
}
// The authenticated user's identity is now available. See the IdTokenClaims struct for a
// complete listing of the available claims.
println!(
"User {} with e-mail address {} has authenticated successfully",
claims.subject().as_str(),
claims.email().map(|email| email.as_str()).unwrap_or("<not provided>"),
);
// If available, we can use the user info endpoint to request additional information.
// The user_info request uses the AccessToken returned in the token response. To parse custom
// claims, use UserInfoClaims directly (with the desired type parameters) rather than using the
// CoreUserInfoClaims type alias.
let userinfo: CoreUserInfoClaims = client
.user_info(token_response.access_token().to_owned(), None).expect("user info missing")
.request_async(&http_client)
.await
.map_err(|err| format!("Failed requesting user info: {}", err))?;
Ok(format!("user={:?}\nemail={:?}\nname={:?}", userinfo.subject(), userinfo.email(), userinfo.name()))
}