mirror of
https://github.com/lennart-k/rustical.git
synced 2025-12-14 11:42:25 +00:00
Add initial OIDC support #33
This commit is contained in:
369
Cargo.lock
generated
369
Cargo.lock
generated
@@ -503,12 +503,24 @@ dependencies = [
|
|||||||
"windows-targets 0.52.6",
|
"windows-targets 0.52.6",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "base16ct"
|
||||||
|
version = "0.2.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "4c7f02d4ea65f2c1853089ffd8d2787bdbc63de2f0d29dedbcf8ccdfa0ccd4cf"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "base64"
|
name = "base64"
|
||||||
version = "0.20.0"
|
version = "0.20.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "0ea22880d78093b0cbe17c89f64a7d457941e65759157ec6cb31a31d652b05e5"
|
checksum = "0ea22880d78093b0cbe17c89f64a7d457941e65759157ec6cb31a31d652b05e5"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "base64"
|
||||||
|
version = "0.21.7"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "9d297deb1925b89f2ccc13d7635fa0714f12c87adce1c75356b39ca9b7178567"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "base64"
|
name = "base64"
|
||||||
version = "0.22.1"
|
version = "0.22.1"
|
||||||
@@ -813,6 +825,18 @@ version = "0.8.21"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "d0a5c400df2834b80a4c3327b3aad3a4c4cd4de0629063962b03235697506a28"
|
checksum = "d0a5c400df2834b80a4c3327b3aad3a4c4cd4de0629063962b03235697506a28"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "crypto-bigint"
|
||||||
|
version = "0.5.5"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "0dc92fb57ca44df6db8059111ab3af99a63d5d0f8375d9972e319a379c6bab76"
|
||||||
|
dependencies = [
|
||||||
|
"generic-array",
|
||||||
|
"rand_core 0.6.4",
|
||||||
|
"subtle",
|
||||||
|
"zeroize",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "crypto-common"
|
name = "crypto-common"
|
||||||
version = "0.1.6"
|
version = "0.1.6"
|
||||||
@@ -833,6 +857,33 @@ dependencies = [
|
|||||||
"cipher",
|
"cipher",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "curve25519-dalek"
|
||||||
|
version = "4.1.3"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "97fb8b7c4503de7d6ae7b42ab72a5a59857b4c937ec27a3d4539dba95b5ab2be"
|
||||||
|
dependencies = [
|
||||||
|
"cfg-if",
|
||||||
|
"cpufeatures",
|
||||||
|
"curve25519-dalek-derive",
|
||||||
|
"digest",
|
||||||
|
"fiat-crypto",
|
||||||
|
"rustc_version",
|
||||||
|
"subtle",
|
||||||
|
"zeroize",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "curve25519-dalek-derive"
|
||||||
|
version = "0.1.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "f46882e17999c6cc590af592290432be3bce0428cb0d5f8b6715e4dc7b383eb3"
|
||||||
|
dependencies = [
|
||||||
|
"proc-macro2",
|
||||||
|
"quote",
|
||||||
|
"syn",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "darling"
|
name = "darling"
|
||||||
version = "0.20.11"
|
version = "0.20.11"
|
||||||
@@ -886,6 +937,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
|||||||
checksum = "9c9e6a11ca8224451684bc0d7d5a7adbf8f2fd6887261a1cfc3c0432f9d4068e"
|
checksum = "9c9e6a11ca8224451684bc0d7d5a7adbf8f2fd6887261a1cfc3c0432f9d4068e"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"powerfmt",
|
"powerfmt",
|
||||||
|
"serde",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@@ -959,6 +1011,50 @@ version = "0.15.7"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "1aaf95b3e5c8f23aa320147307562d361db0ae0d51242340f558153b4eb2439b"
|
checksum = "1aaf95b3e5c8f23aa320147307562d361db0ae0d51242340f558153b4eb2439b"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "dyn-clone"
|
||||||
|
version = "1.0.19"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "1c7a8fb8a9fbf66c1f703fe16184d10ca0ee9d23be5b4436400408ba54a95005"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "ecdsa"
|
||||||
|
version = "0.16.9"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "ee27f32b5c5292967d2d4a9d7f1e0b0aed2c15daded5a60300e4abb9d8020bca"
|
||||||
|
dependencies = [
|
||||||
|
"der",
|
||||||
|
"digest",
|
||||||
|
"elliptic-curve",
|
||||||
|
"rfc6979",
|
||||||
|
"signature",
|
||||||
|
"spki",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "ed25519"
|
||||||
|
version = "2.2.3"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "115531babc129696a58c64a4fef0a8bf9e9698629fb97e9e40767d235cfbcd53"
|
||||||
|
dependencies = [
|
||||||
|
"pkcs8",
|
||||||
|
"signature",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "ed25519-dalek"
|
||||||
|
version = "2.1.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "4a3daa8e81a3963a60642bcc1f90a670680bd4a77535faa384e9d1c79d620871"
|
||||||
|
dependencies = [
|
||||||
|
"curve25519-dalek",
|
||||||
|
"ed25519",
|
||||||
|
"serde",
|
||||||
|
"sha2",
|
||||||
|
"subtle",
|
||||||
|
"zeroize",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "either"
|
name = "either"
|
||||||
version = "1.15.0"
|
version = "1.15.0"
|
||||||
@@ -968,6 +1064,27 @@ dependencies = [
|
|||||||
"serde",
|
"serde",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "elliptic-curve"
|
||||||
|
version = "0.13.8"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "b5e6043086bf7973472e0c7dff2142ea0b680d30e18d9cc40f267efbf222bd47"
|
||||||
|
dependencies = [
|
||||||
|
"base16ct",
|
||||||
|
"crypto-bigint",
|
||||||
|
"digest",
|
||||||
|
"ff",
|
||||||
|
"generic-array",
|
||||||
|
"group",
|
||||||
|
"hkdf",
|
||||||
|
"pem-rfc7468",
|
||||||
|
"pkcs8",
|
||||||
|
"rand_core 0.6.4",
|
||||||
|
"sec1",
|
||||||
|
"subtle",
|
||||||
|
"zeroize",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "encoding_rs"
|
name = "encoding_rs"
|
||||||
version = "0.8.35"
|
version = "0.8.35"
|
||||||
@@ -1021,6 +1138,22 @@ version = "2.3.0"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "37909eebbb50d72f9059c3b6d82c0463f2ff062c9e95845c43a6c9c0355411be"
|
checksum = "37909eebbb50d72f9059c3b6d82c0463f2ff062c9e95845c43a6c9c0355411be"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "ff"
|
||||||
|
version = "0.13.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "c0b50bfb653653f9ca9095b427bed08ab8d75a137839d9ad64eb11810d5b6393"
|
||||||
|
dependencies = [
|
||||||
|
"rand_core 0.6.4",
|
||||||
|
"subtle",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "fiat-crypto"
|
||||||
|
version = "0.2.9"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "28dea519a9695b9977216879a3ebfddf92f1c08c05d984f8996aecd6ecdc811d"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "figment"
|
name = "figment"
|
||||||
version = "0.10.19"
|
version = "0.10.19"
|
||||||
@@ -1175,6 +1308,7 @@ checksum = "85649ca51fd72272d7821adaf274ad91c288277713d9c18820d8499a7ff69e9a"
|
|||||||
dependencies = [
|
dependencies = [
|
||||||
"typenum",
|
"typenum",
|
||||||
"version_check",
|
"version_check",
|
||||||
|
"zeroize",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@@ -1226,6 +1360,17 @@ version = "0.3.2"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "a8d1add55171497b4705a648c6b583acafb01d58050a51727785f0b2c8e0a2b2"
|
checksum = "a8d1add55171497b4705a648c6b583acafb01d58050a51727785f0b2c8e0a2b2"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "group"
|
||||||
|
version = "0.13.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "f0f9ef7462f7c099f518d754361858f86d8a07af53ba9af0fe635bbccb151a63"
|
||||||
|
dependencies = [
|
||||||
|
"ff",
|
||||||
|
"rand_core 0.6.4",
|
||||||
|
"subtle",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "h2"
|
name = "h2"
|
||||||
version = "0.3.26"
|
version = "0.3.26"
|
||||||
@@ -1238,7 +1383,7 @@ dependencies = [
|
|||||||
"futures-sink",
|
"futures-sink",
|
||||||
"futures-util",
|
"futures-util",
|
||||||
"http 0.2.12",
|
"http 0.2.12",
|
||||||
"indexmap",
|
"indexmap 2.9.0",
|
||||||
"slab",
|
"slab",
|
||||||
"tokio",
|
"tokio",
|
||||||
"tokio-util",
|
"tokio-util",
|
||||||
@@ -1257,13 +1402,19 @@ dependencies = [
|
|||||||
"futures-core",
|
"futures-core",
|
||||||
"futures-sink",
|
"futures-sink",
|
||||||
"http 1.3.1",
|
"http 1.3.1",
|
||||||
"indexmap",
|
"indexmap 2.9.0",
|
||||||
"slab",
|
"slab",
|
||||||
"tokio",
|
"tokio",
|
||||||
"tokio-util",
|
"tokio-util",
|
||||||
"tracing",
|
"tracing",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "hashbrown"
|
||||||
|
version = "0.12.3"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "8a9ee70c43aaf417c914396645a0fa852624801b24ebb7ae78fe8272889ac888"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "hashbrown"
|
name = "hashbrown"
|
||||||
version = "0.15.2"
|
version = "0.15.2"
|
||||||
@@ -1281,7 +1432,7 @@ version = "0.10.0"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "7382cf6263419f2d8df38c55d7da83da5c18aef87fc7a7fc1fb1e344edfe14c1"
|
checksum = "7382cf6263419f2d8df38c55d7da83da5c18aef87fc7a7fc1fb1e344edfe14c1"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"hashbrown",
|
"hashbrown 0.15.2",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@@ -1626,6 +1777,17 @@ version = "0.1.9"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "e8a5a9a0ff0086c7a148acb942baaabeadf9504d10400b5a05645853729b9cd2"
|
checksum = "e8a5a9a0ff0086c7a148acb942baaabeadf9504d10400b5a05645853729b9cd2"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "indexmap"
|
||||||
|
version = "1.9.3"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "bd070e393353796e801d209ad339e89596eb4c8d430d18ede6a1cced8fafbd99"
|
||||||
|
dependencies = [
|
||||||
|
"autocfg",
|
||||||
|
"hashbrown 0.12.3",
|
||||||
|
"serde",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "indexmap"
|
name = "indexmap"
|
||||||
version = "2.9.0"
|
version = "2.9.0"
|
||||||
@@ -1633,7 +1795,8 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
|||||||
checksum = "cea70ddb795996207ad57735b50c5982d8844f38ba9ee5f1aedcfb708a2aa11e"
|
checksum = "cea70ddb795996207ad57735b50c5982d8844f38ba9ee5f1aedcfb708a2aa11e"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"equivalent",
|
"equivalent",
|
||||||
"hashbrown",
|
"hashbrown 0.15.2",
|
||||||
|
"serde",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@@ -1663,6 +1826,15 @@ version = "1.70.1"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "7943c866cc5cd64cbc25b2e01621d07fa8eb2a1a23160ee81ce38704e97b8ecf"
|
checksum = "7943c866cc5cd64cbc25b2e01621d07fa8eb2a1a23160ee81ce38704e97b8ecf"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "itertools"
|
||||||
|
version = "0.10.5"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "b0fd2260e829bddf4cb6ea802289de2f86d6a7a690192fbe91b3f46e0f2c8473"
|
||||||
|
dependencies = [
|
||||||
|
"either",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "itertools"
|
name = "itertools"
|
||||||
version = "0.14.0"
|
version = "0.14.0"
|
||||||
@@ -1912,6 +2084,26 @@ dependencies = [
|
|||||||
"libm",
|
"libm",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "oauth2"
|
||||||
|
version = "5.0.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "51e219e79014df21a225b1860a479e2dcd7cbd9130f4defd4bd0e191ea31d67d"
|
||||||
|
dependencies = [
|
||||||
|
"base64 0.22.1",
|
||||||
|
"chrono",
|
||||||
|
"getrandom 0.2.15",
|
||||||
|
"http 1.3.1",
|
||||||
|
"rand 0.8.5",
|
||||||
|
"reqwest",
|
||||||
|
"serde",
|
||||||
|
"serde_json",
|
||||||
|
"serde_path_to_error",
|
||||||
|
"sha2",
|
||||||
|
"thiserror 1.0.69",
|
||||||
|
"url",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "object"
|
name = "object"
|
||||||
version = "0.36.7"
|
version = "0.36.7"
|
||||||
@@ -1933,6 +2125,37 @@ version = "0.3.1"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "c08d65885ee38876c4f86fa503fb49d7b507c2b62552df7c70b2fce627e06381"
|
checksum = "c08d65885ee38876c4f86fa503fb49d7b507c2b62552df7c70b2fce627e06381"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "openidconnect"
|
||||||
|
version = "4.0.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "6dd50d4a5e7730e754f94d977efe61f611aadd3131f6a2b464f6e3a4167e8ef7"
|
||||||
|
dependencies = [
|
||||||
|
"base64 0.21.7",
|
||||||
|
"chrono",
|
||||||
|
"dyn-clone",
|
||||||
|
"ed25519-dalek",
|
||||||
|
"hmac",
|
||||||
|
"http 1.3.1",
|
||||||
|
"itertools 0.10.5",
|
||||||
|
"log",
|
||||||
|
"oauth2",
|
||||||
|
"p256",
|
||||||
|
"p384",
|
||||||
|
"rand 0.8.5",
|
||||||
|
"rsa",
|
||||||
|
"serde",
|
||||||
|
"serde-value",
|
||||||
|
"serde_json",
|
||||||
|
"serde_path_to_error",
|
||||||
|
"serde_plain",
|
||||||
|
"serde_with",
|
||||||
|
"sha2",
|
||||||
|
"subtle",
|
||||||
|
"thiserror 1.0.69",
|
||||||
|
"url",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "opentelemetry"
|
name = "opentelemetry"
|
||||||
version = "0.29.1"
|
version = "0.29.1"
|
||||||
@@ -2017,12 +2240,45 @@ dependencies = [
|
|||||||
"tracing",
|
"tracing",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "ordered-float"
|
||||||
|
version = "2.10.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "68f19d67e5a2795c94e73e0bb1cc1a7edeb2e28efd39e2e1c9b7a40c1108b11c"
|
||||||
|
dependencies = [
|
||||||
|
"num-traits",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "overload"
|
name = "overload"
|
||||||
version = "0.1.1"
|
version = "0.1.1"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "b15813163c1d831bf4a13c3610c05c0d03b39feb07f7e09fa234dac9b15aaf39"
|
checksum = "b15813163c1d831bf4a13c3610c05c0d03b39feb07f7e09fa234dac9b15aaf39"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "p256"
|
||||||
|
version = "0.13.2"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "c9863ad85fa8f4460f9c48cb909d38a0d689dba1f6f6988a5e3e0d31071bcd4b"
|
||||||
|
dependencies = [
|
||||||
|
"ecdsa",
|
||||||
|
"elliptic-curve",
|
||||||
|
"primeorder",
|
||||||
|
"sha2",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "p384"
|
||||||
|
version = "0.13.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "fe42f1670a52a47d448f14b6a5c61dd78fce51856e68edaa38f7ae3a46b8d6b6"
|
||||||
|
dependencies = [
|
||||||
|
"ecdsa",
|
||||||
|
"elliptic-curve",
|
||||||
|
"primeorder",
|
||||||
|
"sha2",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "parking"
|
name = "parking"
|
||||||
version = "2.2.1"
|
version = "2.2.1"
|
||||||
@@ -2259,6 +2515,15 @@ dependencies = [
|
|||||||
"zerocopy",
|
"zerocopy",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "primeorder"
|
||||||
|
version = "0.13.6"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "353e1ca18966c16d9deb1c69278edbc5f194139612772bd9537af60ac231e1e6"
|
||||||
|
dependencies = [
|
||||||
|
"elliptic-curve",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "proc-macro-crate"
|
name = "proc-macro-crate"
|
||||||
version = "3.3.0"
|
version = "3.3.0"
|
||||||
@@ -2307,7 +2572,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
|||||||
checksum = "8a56d757972c98b346a9b766e3f02746cde6dd1cd1d1d563472929fdd74bec4d"
|
checksum = "8a56d757972c98b346a9b766e3f02746cde6dd1cd1d1d563472929fdd74bec4d"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"anyhow",
|
"anyhow",
|
||||||
"itertools",
|
"itertools 0.14.0",
|
||||||
"proc-macro2",
|
"proc-macro2",
|
||||||
"quote",
|
"quote",
|
||||||
"syn",
|
"syn",
|
||||||
@@ -2562,6 +2827,16 @@ dependencies = [
|
|||||||
"windows-registry",
|
"windows-registry",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "rfc6979"
|
||||||
|
version = "0.4.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "f8dd2a808d456c4a54e300a23e9f5a67e122c3024119acbfd73e3bf664491cb2"
|
||||||
|
dependencies = [
|
||||||
|
"hmac",
|
||||||
|
"subtle",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "ring"
|
name = "ring"
|
||||||
version = "0.17.14"
|
version = "0.17.14"
|
||||||
@@ -2809,7 +3084,7 @@ dependencies = [
|
|||||||
"async-trait",
|
"async-trait",
|
||||||
"derive_more 2.0.1",
|
"derive_more 2.0.1",
|
||||||
"futures-util",
|
"futures-util",
|
||||||
"itertools",
|
"itertools 0.14.0",
|
||||||
"log",
|
"log",
|
||||||
"quick-xml",
|
"quick-xml",
|
||||||
"reqwest",
|
"reqwest",
|
||||||
@@ -2833,6 +3108,8 @@ dependencies = [
|
|||||||
"futures-core",
|
"futures-core",
|
||||||
"hex",
|
"hex",
|
||||||
"mime_guess",
|
"mime_guess",
|
||||||
|
"openidconnect",
|
||||||
|
"reqwest",
|
||||||
"rust-embed",
|
"rust-embed",
|
||||||
"rustical_store",
|
"rustical_store",
|
||||||
"serde",
|
"serde",
|
||||||
@@ -2997,6 +3274,20 @@ version = "1.2.0"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49"
|
checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "sec1"
|
||||||
|
version = "0.7.3"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "d3e97a565f76233a6003f9f5c54be1d9c5bdfa3eccfb189469f11ec4901c47dc"
|
||||||
|
dependencies = [
|
||||||
|
"base16ct",
|
||||||
|
"der",
|
||||||
|
"generic-array",
|
||||||
|
"pkcs8",
|
||||||
|
"subtle",
|
||||||
|
"zeroize",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "semver"
|
name = "semver"
|
||||||
version = "1.0.26"
|
version = "1.0.26"
|
||||||
@@ -3012,6 +3303,16 @@ dependencies = [
|
|||||||
"serde_derive",
|
"serde_derive",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "serde-value"
|
||||||
|
version = "0.7.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "f3a1a3341211875ef120e117ea7fd5228530ae7e7036a779fdc9117be6b3282c"
|
||||||
|
dependencies = [
|
||||||
|
"ordered-float",
|
||||||
|
"serde",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "serde_derive"
|
name = "serde_derive"
|
||||||
version = "1.0.219"
|
version = "1.0.219"
|
||||||
@@ -3035,6 +3336,25 @@ dependencies = [
|
|||||||
"serde",
|
"serde",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "serde_path_to_error"
|
||||||
|
version = "0.1.17"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "59fab13f937fa393d08645bf3a84bdfe86e296747b506ada67bb15f10f218b2a"
|
||||||
|
dependencies = [
|
||||||
|
"itoa",
|
||||||
|
"serde",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "serde_plain"
|
||||||
|
version = "1.0.2"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "9ce1fc6db65a611022b23a0dec6975d63fb80a302cb3388835ff02c097258d50"
|
||||||
|
dependencies = [
|
||||||
|
"serde",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "serde_spanned"
|
name = "serde_spanned"
|
||||||
version = "0.6.8"
|
version = "0.6.8"
|
||||||
@@ -3056,6 +3376,36 @@ dependencies = [
|
|||||||
"serde",
|
"serde",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "serde_with"
|
||||||
|
version = "3.12.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "d6b6f7f2fcb69f747921f79f3926bd1e203fce4fef62c268dd3abfb6d86029aa"
|
||||||
|
dependencies = [
|
||||||
|
"base64 0.22.1",
|
||||||
|
"chrono",
|
||||||
|
"hex",
|
||||||
|
"indexmap 1.9.3",
|
||||||
|
"indexmap 2.9.0",
|
||||||
|
"serde",
|
||||||
|
"serde_derive",
|
||||||
|
"serde_json",
|
||||||
|
"serde_with_macros",
|
||||||
|
"time",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "serde_with_macros"
|
||||||
|
version = "3.12.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "8d00caa5193a3c8362ac2b73be6b9e768aa5a4b2f721d8f4b339600c3cb51f8e"
|
||||||
|
dependencies = [
|
||||||
|
"darling",
|
||||||
|
"proc-macro2",
|
||||||
|
"quote",
|
||||||
|
"syn",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "sha1"
|
name = "sha1"
|
||||||
version = "0.10.6"
|
version = "0.10.6"
|
||||||
@@ -3194,9 +3544,9 @@ dependencies = [
|
|||||||
"futures-intrusive",
|
"futures-intrusive",
|
||||||
"futures-io",
|
"futures-io",
|
||||||
"futures-util",
|
"futures-util",
|
||||||
"hashbrown",
|
"hashbrown 0.15.2",
|
||||||
"hashlink",
|
"hashlink",
|
||||||
"indexmap",
|
"indexmap 2.9.0",
|
||||||
"log",
|
"log",
|
||||||
"memchr",
|
"memchr",
|
||||||
"once_cell",
|
"once_cell",
|
||||||
@@ -3630,7 +3980,7 @@ version = "0.22.24"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "17b4795ff5edd201c7cd6dca065ae59972ce77d1b80fa0a84d94950ece7d1474"
|
checksum = "17b4795ff5edd201c7cd6dca065ae59972ce77d1b80fa0a84d94950ece7d1474"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"indexmap",
|
"indexmap 2.9.0",
|
||||||
"serde",
|
"serde",
|
||||||
"serde_spanned",
|
"serde_spanned",
|
||||||
"toml_datetime",
|
"toml_datetime",
|
||||||
@@ -3863,6 +4213,7 @@ dependencies = [
|
|||||||
"form_urlencoded",
|
"form_urlencoded",
|
||||||
"idna",
|
"idna",
|
||||||
"percent-encoding",
|
"percent-encoding",
|
||||||
|
"serde",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
|
|||||||
@@ -110,6 +110,7 @@ reqwest = { version = "0.12", features = [
|
|||||||
"charset",
|
"charset",
|
||||||
"http2",
|
"http2",
|
||||||
], default-features = false }
|
], default-features = false }
|
||||||
|
openidconnect = "4.0"
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
rustical_store = { workspace = true }
|
rustical_store = { workspace = true }
|
||||||
|
|||||||
20
README.md
20
README.md
@@ -15,6 +15,7 @@ a CalDAV/CardDAV server
|
|||||||
- adequately fast (I'd say blazingly fast™ :fire: if I did the benchmarks to back that claim up)
|
- adequately fast (I'd say blazingly fast™ :fire: if I did the benchmarks to back that claim up)
|
||||||
- deleted calendars are recoverable
|
- deleted calendars are recoverable
|
||||||
- Nextcloud login flow (In DAVx5 you can login through the Nextcloud flow and automatically generate an app token)
|
- Nextcloud login flow (In DAVx5 you can login through the Nextcloud flow and automatically generate an app token)
|
||||||
|
- experimental OpenID Connect support
|
||||||
|
|
||||||
## Installation
|
## Installation
|
||||||
|
|
||||||
@@ -121,6 +122,23 @@ Since push messages are currently not encrypted you might potentially want to en
|
|||||||
allowed_push_servers = ["https://your-instance-ntfy.sh"]
|
allowed_push_servers = ["https://your-instance-ntfy.sh"]
|
||||||
```
|
```
|
||||||
|
|
||||||
|
### OpenID Connect
|
||||||
|
|
||||||
|
There's experimental support to log in through an OIDC IdP.
|
||||||
|
Currently, the `preferred_username` is used as a user id (which is suboptimal, so you should be aware of that) and cannot be configured.
|
||||||
|
|
||||||
|
```toml
|
||||||
|
[frontend.oidc]
|
||||||
|
name = "e.g. Authelia"
|
||||||
|
issuer = "https://auth.your.domain"
|
||||||
|
client_id = "rustical"
|
||||||
|
client_secret = "secret"
|
||||||
|
scopes = ["openid", "profile"]
|
||||||
|
allow_sign_up = false
|
||||||
|
```
|
||||||
|
|
||||||
|
On the IdP side you have to create a client with the redirect uri `/frontend/login/oidc/callback` (subject to change).
|
||||||
|
|
||||||
## Debugging
|
## Debugging
|
||||||
|
|
||||||
Set the log level with following environment variables:
|
Set the log level with following environment variables:
|
||||||
@@ -144,7 +162,7 @@ opentelemetry = true
|
|||||||
- provides the REPORT method
|
- provides the REPORT method
|
||||||
- Calendaring Extensions to WebDAV (CalDAV): [RFC 4791](https://datatracker.ietf.org/doc/html/rfc4791)
|
- Calendaring Extensions to WebDAV (CalDAV): [RFC 4791](https://datatracker.ietf.org/doc/html/rfc4791)
|
||||||
- Scheduling Extensions to CalDAV: [RFC 6638](https://datatracker.ietf.org/doc/html/rfc6638)
|
- Scheduling Extensions to CalDAV: [RFC 6638](https://datatracker.ietf.org/doc/html/rfc6638)
|
||||||
- not sure yet whether to implement this
|
- not sur`e yet whether to implement this
|
||||||
- Collection Synchronization WebDAV [RFC 6578](https://datatracker.ietf.org/doc/html/rfc6578)
|
- Collection Synchronization WebDAV [RFC 6578](https://datatracker.ietf.org/doc/html/rfc6578)
|
||||||
- We need to implement sync-token, etc.
|
- We need to implement sync-token, etc.
|
||||||
- This is important for more efficient synchronisation
|
- This is important for more efficient synchronisation
|
||||||
|
|||||||
@@ -7,6 +7,7 @@ repository.workspace = true
|
|||||||
publish = false
|
publish = false
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
|
openidconnect.workspace = true
|
||||||
askama.workspace = true
|
askama.workspace = true
|
||||||
askama_web.workspace = true
|
askama_web.workspace = true
|
||||||
actix-session = { workspace = true }
|
actix-session = { workspace = true }
|
||||||
@@ -19,3 +20,4 @@ rust-embed.workspace = true
|
|||||||
futures-core.workspace = true
|
futures-core.workspace = true
|
||||||
hex.workspace = true
|
hex.workspace = true
|
||||||
mime_guess.workspace = true
|
mime_guess.workspace = true
|
||||||
|
reqwest.workspace = true
|
||||||
|
|||||||
@@ -9,4 +9,9 @@
|
|||||||
<input type="password" name="password" placeholder="password">
|
<input type="password" name="password" placeholder="password">
|
||||||
<button type="submit">Login</button>
|
<button type="submit">Login</button>
|
||||||
</form>
|
</form>
|
||||||
|
|
||||||
|
{% if let Some(OidcProviderData {name, redirect_url}) = oidc_data %}
|
||||||
|
<a href="{{ redirect_url }}">Login with {{ name }}</a>
|
||||||
|
{% endif %}
|
||||||
|
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
|
|||||||
@@ -1,9 +1,20 @@
|
|||||||
|
use openidconnect::{ClientId, ClientSecret, IssuerUrl, Scope};
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
|
|
||||||
fn default_enabled() -> bool {
|
fn default_enabled() -> bool {
|
||||||
true
|
true
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Deserialize, Serialize, Debug, Clone)]
|
||||||
|
pub struct OidcConfig {
|
||||||
|
pub name: String,
|
||||||
|
pub issuer: IssuerUrl,
|
||||||
|
pub client_id: ClientId,
|
||||||
|
pub client_secret: Option<ClientSecret>,
|
||||||
|
pub scopes: Vec<Scope>,
|
||||||
|
pub allow_sign_up: bool,
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(Deserialize, Serialize, Debug, Clone)]
|
#[derive(Deserialize, Serialize, Debug, Clone)]
|
||||||
#[serde(deny_unknown_fields)]
|
#[serde(deny_unknown_fields)]
|
||||||
pub struct FrontendConfig {
|
pub struct FrontendConfig {
|
||||||
@@ -12,4 +23,6 @@ pub struct FrontendConfig {
|
|||||||
pub secret_key: [u8; 64],
|
pub secret_key: [u8; 64],
|
||||||
#[serde(default = "default_enabled")]
|
#[serde(default = "default_enabled")]
|
||||||
pub enabled: bool,
|
pub enabled: bool,
|
||||||
|
#[serde(default)]
|
||||||
|
pub oidc: Option<OidcConfig>,
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -12,6 +12,7 @@ use actix_web::{
|
|||||||
use askama::Template;
|
use askama::Template;
|
||||||
use askama_web::WebTemplate;
|
use askama_web::WebTemplate;
|
||||||
use assets::{Assets, EmbedService};
|
use assets::{Assets, EmbedService};
|
||||||
|
use oidc::{route_get_oidc, route_get_oidc_callback};
|
||||||
use routes::{
|
use routes::{
|
||||||
addressbook::{route_addressbook, route_addressbook_restore},
|
addressbook::{route_addressbook, route_addressbook_restore},
|
||||||
calendar::{route_calendar, route_calendar_restore},
|
calendar::{route_calendar, route_calendar_restore},
|
||||||
@@ -25,6 +26,7 @@ use std::sync::Arc;
|
|||||||
|
|
||||||
mod assets;
|
mod assets;
|
||||||
mod config;
|
mod config;
|
||||||
|
mod oidc;
|
||||||
mod routes;
|
mod routes;
|
||||||
|
|
||||||
pub use config::FrontendConfig;
|
pub use config::FrontendConfig;
|
||||||
@@ -130,6 +132,7 @@ pub fn configure_frontend<AP: AuthenticationProvider, CS: CalendarStore, AS: Add
|
|||||||
.app_data(Data::from(auth_provider))
|
.app_data(Data::from(auth_provider))
|
||||||
.app_data(Data::from(cal_store.clone()))
|
.app_data(Data::from(cal_store.clone()))
|
||||||
.app_data(Data::from(addr_store.clone()))
|
.app_data(Data::from(addr_store.clone()))
|
||||||
|
.app_data(Data::new(frontend_config.clone()))
|
||||||
.service(EmbedService::<Assets>::new("/assets".to_owned()))
|
.service(EmbedService::<Assets>::new("/assets".to_owned()))
|
||||||
.service(web::resource("").route(web::method(Method::GET).to(route_root)))
|
.service(web::resource("").route(web::method(Method::GET).to(route_root)))
|
||||||
.service(
|
.service(
|
||||||
@@ -158,6 +161,16 @@ pub fn configure_frontend<AP: AuthenticationProvider, CS: CalendarStore, AS: Add
|
|||||||
.name("frontend_login")
|
.name("frontend_login")
|
||||||
.route(web::method(Method::GET).to(route_get_login))
|
.route(web::method(Method::GET).to(route_get_login))
|
||||||
.route(web::method(Method::POST).to(route_post_login::<AP>)),
|
.route(web::method(Method::POST).to(route_post_login::<AP>)),
|
||||||
|
)
|
||||||
|
.service(
|
||||||
|
web::resource("/login/oidc")
|
||||||
|
.name("frontend_login_oidc")
|
||||||
|
.route(web::method(Method::GET).to(route_get_oidc)),
|
||||||
|
)
|
||||||
|
.service(
|
||||||
|
web::resource("/login/oidc/callback")
|
||||||
|
.name("frontend_oidc_callback")
|
||||||
|
.route(web::method(Method::GET).to(route_get_oidc_callback::<AP>)),
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
242
crates/frontend/src/oidc/mod.rs
Normal file
242
crates/frontend/src/oidc/mod.rs
Normal file
@@ -0,0 +1,242 @@
|
|||||||
|
use crate::{FrontendConfig, config::OidcConfig};
|
||||||
|
use actix_session::{Session, SessionInsertError};
|
||||||
|
use actix_web::{
|
||||||
|
HttpRequest, HttpResponse, Responder, ResponseError,
|
||||||
|
body::BoxBody,
|
||||||
|
error::UrlGenerationError,
|
||||||
|
http::StatusCode,
|
||||||
|
web::{Data, Query, Redirect},
|
||||||
|
};
|
||||||
|
use openidconnect::{
|
||||||
|
AuthenticationFlow, AuthorizationCode, ClaimsVerificationError, ConfigurationError, CsrfToken,
|
||||||
|
EmptyAdditionalClaims, EndpointMaybeSet, EndpointNotSet, EndpointSet, IssuerUrl, Nonce,
|
||||||
|
OAuth2TokenResponse, PkceCodeChallenge, PkceCodeVerifier, RedirectUrl, TokenResponse,
|
||||||
|
UserInfoClaims,
|
||||||
|
core::{CoreClient, CoreGenderClaim, CoreProviderMetadata, CoreResponseType},
|
||||||
|
url::ParseError,
|
||||||
|
};
|
||||||
|
use rustical_store::auth::{AuthenticationProvider, User, user::PrincipalType::Individual};
|
||||||
|
use serde::{Deserialize, Serialize};
|
||||||
|
|
||||||
|
#[derive(Debug, thiserror::Error)]
|
||||||
|
pub enum OidcError {
|
||||||
|
#[error("Cannot generate redirect url, something's not configured correctly")]
|
||||||
|
OidcParseError(#[from] ParseError),
|
||||||
|
|
||||||
|
#[error("Cannot generate redirect url, something's not configured correctly")]
|
||||||
|
ActixUrlGenerationError(#[from] UrlGenerationError),
|
||||||
|
|
||||||
|
#[error("RustiCal is not configured correctly for OIDC")]
|
||||||
|
IncorrectConfiguration,
|
||||||
|
|
||||||
|
#[error(transparent)]
|
||||||
|
OidcConfigurationError(#[from] ConfigurationError),
|
||||||
|
|
||||||
|
#[error(transparent)]
|
||||||
|
OidcClaimsVerificationError(#[from] ClaimsVerificationError),
|
||||||
|
|
||||||
|
#[error(transparent)]
|
||||||
|
SessionInsertError(#[from] SessionInsertError),
|
||||||
|
|
||||||
|
#[error(transparent)]
|
||||||
|
StoreError(#[from] rustical_store::Error),
|
||||||
|
|
||||||
|
#[error("{0}")]
|
||||||
|
Other(&'static str),
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ResponseError for OidcError {
|
||||||
|
fn status_code(&self) -> StatusCode {
|
||||||
|
StatusCode::INTERNAL_SERVER_ERROR
|
||||||
|
}
|
||||||
|
|
||||||
|
fn error_response(&self) -> HttpResponse<BoxBody> {
|
||||||
|
HttpResponse::build(self.status_code()).body(self.to_string())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(crate) struct OidcProviderData<'a> {
|
||||||
|
pub name: &'a str,
|
||||||
|
pub redirect_url: String,
|
||||||
|
}
|
||||||
|
|
||||||
|
const SESSION_KEY_OIDC_STATE: &str = "oidc_state";
|
||||||
|
|
||||||
|
#[derive(Debug, Deserialize, Serialize)]
|
||||||
|
struct OidcState {
|
||||||
|
state: CsrfToken,
|
||||||
|
nonce: Nonce,
|
||||||
|
pkce_verifier: PkceCodeVerifier,
|
||||||
|
}
|
||||||
|
|
||||||
|
fn get_http_client() -> reqwest::Client {
|
||||||
|
reqwest::ClientBuilder::new()
|
||||||
|
// Following redirects opens the client up to SSRF vulnerabilities.
|
||||||
|
.redirect(reqwest::redirect::Policy::none())
|
||||||
|
.build()
|
||||||
|
.expect("Something went wrong :(")
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn get_oidc_client(
|
||||||
|
OidcConfig {
|
||||||
|
issuer,
|
||||||
|
client_id,
|
||||||
|
client_secret,
|
||||||
|
..
|
||||||
|
}: OidcConfig,
|
||||||
|
http_client: &reqwest::Client,
|
||||||
|
redirect_uri: RedirectUrl,
|
||||||
|
) -> Result<
|
||||||
|
CoreClient<
|
||||||
|
EndpointSet,
|
||||||
|
EndpointNotSet,
|
||||||
|
EndpointNotSet,
|
||||||
|
EndpointNotSet,
|
||||||
|
EndpointMaybeSet,
|
||||||
|
EndpointMaybeSet,
|
||||||
|
>,
|
||||||
|
OidcError,
|
||||||
|
> {
|
||||||
|
let provider_metadata = CoreProviderMetadata::discover_async(issuer, http_client)
|
||||||
|
.await
|
||||||
|
.map_err(|_| OidcError::Other("Failed to discover OpenID provider"))?;
|
||||||
|
|
||||||
|
Ok(CoreClient::from_provider_metadata(
|
||||||
|
provider_metadata.clone(),
|
||||||
|
client_id.clone(),
|
||||||
|
client_secret.clone(),
|
||||||
|
)
|
||||||
|
.set_redirect_uri(redirect_uri))
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Endpoint that redirects to the authorize endpoint of the OIDC service
|
||||||
|
pub async fn route_get_oidc(
|
||||||
|
req: HttpRequest,
|
||||||
|
config: Data<FrontendConfig>,
|
||||||
|
session: Session,
|
||||||
|
) -> Result<impl Responder, OidcError> {
|
||||||
|
let oidc_config = config
|
||||||
|
.oidc
|
||||||
|
.clone()
|
||||||
|
.ok_or(OidcError::IncorrectConfiguration)?;
|
||||||
|
|
||||||
|
let http_client = get_http_client();
|
||||||
|
let oidc_client = get_oidc_client(
|
||||||
|
oidc_config.clone(),
|
||||||
|
&http_client,
|
||||||
|
RedirectUrl::new(req.url_for_static("frontend_oidc_callback")?.to_string())?,
|
||||||
|
)
|
||||||
|
.await?;
|
||||||
|
|
||||||
|
let (pkce_challenge, pkce_verifier) = PkceCodeChallenge::new_random_sha256();
|
||||||
|
|
||||||
|
let (auth_url, csrf_token, nonce) = oidc_client
|
||||||
|
.authorize_url(
|
||||||
|
AuthenticationFlow::<CoreResponseType>::AuthorizationCode,
|
||||||
|
CsrfToken::new_random,
|
||||||
|
Nonce::new_random,
|
||||||
|
)
|
||||||
|
.add_scopes(oidc_config.scopes.clone())
|
||||||
|
.set_pkce_challenge(pkce_challenge)
|
||||||
|
.url();
|
||||||
|
|
||||||
|
session.insert(
|
||||||
|
SESSION_KEY_OIDC_STATE,
|
||||||
|
OidcState {
|
||||||
|
state: csrf_token,
|
||||||
|
nonce,
|
||||||
|
pkce_verifier,
|
||||||
|
},
|
||||||
|
)?;
|
||||||
|
|
||||||
|
Ok(Redirect::to(auth_url.to_string()).see_other())
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, Deserialize)]
|
||||||
|
pub struct AuthCallbackQuery {
|
||||||
|
code: AuthorizationCode,
|
||||||
|
iss: IssuerUrl,
|
||||||
|
// scope: String,
|
||||||
|
// state: String,
|
||||||
|
}
|
||||||
|
|
||||||
|
pub async fn route_get_oidc_callback<AP: AuthenticationProvider>(
|
||||||
|
req: HttpRequest,
|
||||||
|
config: Data<FrontendConfig>,
|
||||||
|
session: Session,
|
||||||
|
auth_provider: Data<AP>,
|
||||||
|
Query(AuthCallbackQuery { code, iss }): Query<AuthCallbackQuery>,
|
||||||
|
) -> Result<impl Responder, OidcError> {
|
||||||
|
let oidc_config = config
|
||||||
|
.oidc
|
||||||
|
.clone()
|
||||||
|
.ok_or(OidcError::IncorrectConfiguration)?;
|
||||||
|
assert_eq!(iss, oidc_config.issuer);
|
||||||
|
let oidc_state = session
|
||||||
|
.remove_as::<OidcState>(SESSION_KEY_OIDC_STATE)
|
||||||
|
.ok_or(OidcError::Other("No local OIDC state"))?
|
||||||
|
.map_err(|_| OidcError::Other("Error parsing OIDC state"))?;
|
||||||
|
|
||||||
|
let http_client = get_http_client();
|
||||||
|
let oidc_client = get_oidc_client(
|
||||||
|
oidc_config.clone(),
|
||||||
|
&http_client,
|
||||||
|
RedirectUrl::new(req.url_for_static("frontend_oidc_callback")?.to_string())?,
|
||||||
|
)
|
||||||
|
.await?;
|
||||||
|
|
||||||
|
let token_response = oidc_client
|
||||||
|
.exchange_code(code)?
|
||||||
|
.set_pkce_verifier(oidc_state.pkce_verifier)
|
||||||
|
.request_async(&http_client)
|
||||||
|
.await
|
||||||
|
.map_err(|_| OidcError::Other("Error requesting token"))?;
|
||||||
|
let id_claims = token_response
|
||||||
|
.id_token()
|
||||||
|
.ok_or(OidcError::Other("OIDC provider did not return an ID token"))?
|
||||||
|
.claims(&oidc_client.id_token_verifier(), &oidc_state.nonce)?;
|
||||||
|
|
||||||
|
let user_info_claims: UserInfoClaims<EmptyAdditionalClaims, CoreGenderClaim> = oidc_client
|
||||||
|
.user_info(
|
||||||
|
token_response.access_token().clone(),
|
||||||
|
Some(id_claims.subject().clone()),
|
||||||
|
)?
|
||||||
|
.request_async(&http_client)
|
||||||
|
.await
|
||||||
|
.map_err(|_| OidcError::Other("Error fetching user info"))?;
|
||||||
|
|
||||||
|
let user_id = user_info_claims
|
||||||
|
.preferred_username()
|
||||||
|
.ok_or(OidcError::Other("Missing preferred_username claim"))?
|
||||||
|
.to_string();
|
||||||
|
|
||||||
|
let mut user = auth_provider.get_principal(&user_id).await?;
|
||||||
|
if user.is_none() {
|
||||||
|
let new_user = User {
|
||||||
|
id: user_id,
|
||||||
|
displayname: None,
|
||||||
|
app_tokens: vec![],
|
||||||
|
password: None,
|
||||||
|
principal_type: Individual,
|
||||||
|
memberships: vec![],
|
||||||
|
};
|
||||||
|
|
||||||
|
auth_provider.insert_principal(new_user.clone()).await?;
|
||||||
|
user = Some(new_user);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Complete login flow
|
||||||
|
if let Some(user) = user {
|
||||||
|
session.insert("user", user.id.clone())?;
|
||||||
|
|
||||||
|
Ok(
|
||||||
|
Redirect::to(req.url_for("frontend_user", &[user.id])?.to_string())
|
||||||
|
.temporary()
|
||||||
|
.respond_to(&req)
|
||||||
|
.map_into_boxed_body(),
|
||||||
|
)
|
||||||
|
} else {
|
||||||
|
// Add user provisioning
|
||||||
|
Ok(HttpResponse::build(StatusCode::UNAUTHORIZED).body("User does not exist"))
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,3 +1,4 @@
|
|||||||
|
use crate::{FrontendConfig, oidc::OidcProviderData};
|
||||||
use actix_session::Session;
|
use actix_session::Session;
|
||||||
use actix_web::{
|
use actix_web::{
|
||||||
HttpRequest, HttpResponse, Responder,
|
HttpRequest, HttpResponse, Responder,
|
||||||
@@ -11,10 +12,21 @@ use serde::Deserialize;
|
|||||||
|
|
||||||
#[derive(Template, WebTemplate)]
|
#[derive(Template, WebTemplate)]
|
||||||
#[template(path = "pages/login.html")]
|
#[template(path = "pages/login.html")]
|
||||||
struct LoginPage;
|
struct LoginPage<'a> {
|
||||||
|
oidc_data: Option<OidcProviderData<'a>>,
|
||||||
|
}
|
||||||
|
|
||||||
pub async fn route_get_login() -> impl Responder {
|
pub async fn route_get_login(req: HttpRequest, config: Data<FrontendConfig>) -> impl Responder {
|
||||||
LoginPage
|
LoginPage {
|
||||||
|
oidc_data: config.oidc.as_ref().map(|oidc| OidcProviderData {
|
||||||
|
name: &oidc.name,
|
||||||
|
redirect_url: req
|
||||||
|
.url_for_static("frontend_login_oidc")
|
||||||
|
.unwrap()
|
||||||
|
.to_string(),
|
||||||
|
}),
|
||||||
|
}
|
||||||
|
.respond_to(&req)
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Deserialize)]
|
#[derive(Deserialize)]
|
||||||
|
|||||||
@@ -2,7 +2,7 @@ use argon2::password_hash::SaltString;
|
|||||||
use clap::{Parser, ValueEnum};
|
use clap::{Parser, ValueEnum};
|
||||||
use password_hash::PasswordHasher;
|
use password_hash::PasswordHasher;
|
||||||
use pbkdf2::Params;
|
use pbkdf2::Params;
|
||||||
use rand::{rngs::OsRng, RngCore};
|
use rand::{RngCore, rngs::OsRng};
|
||||||
use rustical_frontend::FrontendConfig;
|
use rustical_frontend::FrontendConfig;
|
||||||
use rustical_store::auth::TomlUserStoreConfig;
|
use rustical_store::auth::TomlUserStoreConfig;
|
||||||
|
|
||||||
@@ -35,6 +35,7 @@ pub fn cmd_gen_config(_args: GenConfigArgs) -> anyhow::Result<()> {
|
|||||||
frontend: FrontendConfig {
|
frontend: FrontendConfig {
|
||||||
secret_key: generate_frontend_secret(),
|
secret_key: generate_frontend_secret(),
|
||||||
enabled: true,
|
enabled: true,
|
||||||
|
oidc: None,
|
||||||
},
|
},
|
||||||
dav_push: DavPushConfig::default(),
|
dav_push: DavPushConfig::default(),
|
||||||
nextcloud_login: Default::default(),
|
nextcloud_login: Default::default(),
|
||||||
|
|||||||
Reference in New Issue
Block a user