Draft: SSO login (OAuth 2.0 + OpenID Connect) #1012
14 changed files with 515 additions and 174 deletions
341
Cargo.lock
generated
341
Cargo.lock
generated
|
@ -65,6 +65,12 @@ version = "1.0.6"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "8901269c6307e8d93993578286ac0edf7f195079ffff5ebdeea6a59ffb7e36bc"
|
checksum = "8901269c6307e8d93993578286ac0edf7f195079ffff5ebdeea6a59ffb7e36bc"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "anyhow"
|
||||||
|
version = "1.0.80"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "5ad32ce52e4161730f7098c077cd2ed6229b5804ccf99e5366be1ab72a98b4e1"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "arc-swap"
|
name = "arc-swap"
|
||||||
version = "1.6.0"
|
version = "1.6.0"
|
||||||
|
@ -150,6 +156,17 @@ version = "1.1.1"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "5f093eed78becd229346bf859eec0aa4dd7ddde0757287b2b4107a1f09c80002"
|
checksum = "5f093eed78becd229346bf859eec0aa4dd7ddde0757287b2b4107a1f09c80002"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "async-recursion"
|
||||||
|
version = "1.0.5"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "5fd55a5ba1179988837d24ab4c7cc8ed6efdeff578ede0416b4225a5fca35bd0"
|
||||||
|
dependencies = [
|
||||||
|
"proc-macro2",
|
||||||
|
"quote",
|
||||||
|
"syn 2.0.51",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "async-trait"
|
name = "async-trait"
|
||||||
version = "0.1.77"
|
version = "0.1.77"
|
||||||
|
@ -320,6 +337,15 @@ version = "0.21.7"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "9d297deb1925b89f2ccc13d7635fa0714f12c87adce1c75356b39ca9b7178567"
|
checksum = "9d297deb1925b89f2ccc13d7635fa0714f12c87adce1c75356b39ca9b7178567"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "base64-url"
|
||||||
|
version = "2.0.2"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "fb9fb9fb058cc3063b5fc88d9a21eefa2735871498a04e1650da76ed511c8569"
|
||||||
|
dependencies = [
|
||||||
|
"base64 0.21.7",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "base64ct"
|
name = "base64ct"
|
||||||
version = "1.6.0"
|
version = "1.6.0"
|
||||||
|
@ -462,6 +488,33 @@ dependencies = [
|
||||||
"windows-targets 0.52.3",
|
"windows-targets 0.52.3",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "ciborium"
|
||||||
|
version = "0.2.2"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "42e69ffd6f0917f5c029256a24d0161db17cea3997d185db0d35926308770f0e"
|
||||||
|
dependencies = [
|
||||||
|
"ciborium-io",
|
||||||
|
"ciborium-ll",
|
||||||
|
"serde",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "ciborium-io"
|
||||||
|
version = "0.2.2"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "05afea1e0a06c9be33d539b876f1ce3692f4afea2cb41f740e7743225ed1c757"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "ciborium-ll"
|
||||||
|
version = "0.2.2"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "57663b653d948a338bfb3eeba9bb2fd5fcfaecb9e199e87e1eda4d9e8b240fd9"
|
||||||
|
dependencies = [
|
||||||
|
"ciborium-io",
|
||||||
|
"half",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "clang-sys"
|
name = "clang-sys"
|
||||||
version = "1.7.0"
|
version = "1.7.0"
|
||||||
|
@ -542,6 +595,7 @@ dependencies = [
|
||||||
"lru-cache",
|
"lru-cache",
|
||||||
"nix",
|
"nix",
|
||||||
"num_cpus",
|
"num_cpus",
|
||||||
|
"openid-client",
|
||||||
"openidconnect",
|
"openidconnect",
|
||||||
"opentelemetry",
|
"opentelemetry",
|
||||||
"opentelemetry-jaeger",
|
"opentelemetry-jaeger",
|
||||||
|
@ -671,6 +725,12 @@ version = "0.8.19"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "248e3bacc7dc6baa3b21e405ee045c3047101a49145e7e9eca583ab4c2ca5345"
|
checksum = "248e3bacc7dc6baa3b21e405ee045c3047101a49145e7e9eca583ab4c2ca5345"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "crunchy"
|
||||||
|
version = "0.2.2"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "7a81dae078cea95a014a339291cec439d2f232ebe854a9d672b796c6afafa9b7"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "crypto-bigint"
|
name = "crypto-bigint"
|
||||||
version = "0.5.5"
|
version = "0.5.5"
|
||||||
|
@ -927,6 +987,16 @@ version = "1.0.1"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "5443807d6dff69373d433ab9ef5378ad8df50ca6298caf15de6e52e24aaf54d5"
|
checksum = "5443807d6dff69373d433ab9ef5378ad8df50ca6298caf15de6e52e24aaf54d5"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "errno"
|
||||||
|
version = "0.3.8"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "a258e46cdc063eb8519c00b9fc845fc47bcfca4130e2f08e88665ceda8474245"
|
||||||
|
dependencies = [
|
||||||
|
"libc",
|
||||||
|
"windows-sys 0.52.0",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "fallible-iterator"
|
name = "fallible-iterator"
|
||||||
version = "0.2.0"
|
version = "0.2.0"
|
||||||
|
@ -939,6 +1009,12 @@ 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 = "7360491ce676a36bf9bb3c56c1aa791658183a54d2744120f27285738d90465a"
|
checksum = "7360491ce676a36bf9bb3c56c1aa791658183a54d2744120f27285738d90465a"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "fastrand"
|
||||||
|
version = "2.0.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "25cbce373ec4653f1a01a31e8a5e5ec0c622dc27ff9c4e6606eefef5cbbed4a5"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "fdeflate"
|
name = "fdeflate"
|
||||||
version = "0.3.4"
|
version = "0.3.4"
|
||||||
|
@ -994,6 +1070,21 @@ version = "1.0.7"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1"
|
checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "foreign-types"
|
||||||
|
version = "0.3.2"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "f6f339eb8adc052cd2ca78910fda869aefa38d22d5cb648e6485e4d3fc06f3b1"
|
||||||
|
dependencies = [
|
||||||
|
"foreign-types-shared",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "foreign-types-shared"
|
||||||
|
version = "0.1.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "00b0228411908ca8685dba7fc2cdd70ec9990a6e753e89b6ac91a84c40fbaf4b"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "form_urlencoded"
|
name = "form_urlencoded"
|
||||||
version = "1.2.1"
|
version = "1.2.1"
|
||||||
|
@ -1178,6 +1269,16 @@ dependencies = [
|
||||||
"tracing",
|
"tracing",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "half"
|
||||||
|
version = "2.4.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "b5eceaaeec696539ddaf7b333340f1af35a5aa87ae3e4f3ead0532f72affab2e"
|
||||||
|
dependencies = [
|
||||||
|
"cfg-if",
|
||||||
|
"crunchy",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "hashbrown"
|
name = "hashbrown"
|
||||||
version = "0.12.3"
|
version = "0.12.3"
|
||||||
|
@ -1395,6 +1496,19 @@ dependencies = [
|
||||||
"tokio-rustls",
|
"tokio-rustls",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "hyper-tls"
|
||||||
|
version = "0.5.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "d6183ddfa99b85da61a140bea0efc93fdf56ceaa041b37d553518030827f9905"
|
||||||
|
dependencies = [
|
||||||
|
"bytes",
|
||||||
|
"hyper",
|
||||||
|
"native-tls",
|
||||||
|
"tokio",
|
||||||
|
"tokio-native-tls",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "iana-time-zone"
|
name = "iana-time-zone"
|
||||||
version = "0.1.60"
|
version = "0.1.60"
|
||||||
|
@ -1536,6 +1650,24 @@ version = "1.0.10"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "b1a46d1a171d865aa5f83f92695765caa047a9b4cbae2cbf37dbd613a793fd4c"
|
checksum = "b1a46d1a171d865aa5f83f92695765caa047a9b4cbae2cbf37dbd613a793fd4c"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "josekit"
|
||||||
|
version = "0.8.5"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "cd20997283339a19226445db97d632c8dc7adb6b8172537fe0e9e540fb141df2"
|
||||||
|
dependencies = [
|
||||||
|
"anyhow",
|
||||||
|
"base64 0.21.7",
|
||||||
|
"flate2",
|
||||||
|
"once_cell",
|
||||||
|
"openssl",
|
||||||
|
"regex",
|
||||||
|
"serde",
|
||||||
|
"serde_json",
|
||||||
|
"thiserror",
|
||||||
|
"time",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "jpeg-decoder"
|
name = "jpeg-decoder"
|
||||||
version = "0.3.1"
|
version = "0.3.1"
|
||||||
|
@ -1584,6 +1716,35 @@ dependencies = [
|
||||||
"simple_asn1",
|
"simple_asn1",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "jwt-compact"
|
||||||
|
version = "0.8.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "25cb2458ca54de48ef237ac0d68d4e80e512ae81d6aeb9775f4c835da0d193d3"
|
||||||
|
dependencies = [
|
||||||
|
"anyhow",
|
||||||
|
"base64ct",
|
||||||
|
"chrono",
|
||||||
|
"ciborium",
|
||||||
|
"hmac",
|
||||||
|
"rand_core",
|
||||||
|
"serde",
|
||||||
|
"serde_json",
|
||||||
|
"sha2",
|
||||||
|
"smallvec",
|
||||||
|
"subtle",
|
||||||
|
"zeroize",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "keccak"
|
||||||
|
version = "0.1.5"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "ecc2af9a1119c51f12a14607e783cb977bde58bc069ff0c3da1095e635d70654"
|
||||||
|
dependencies = [
|
||||||
|
"cpufeatures",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "konst"
|
name = "konst"
|
||||||
version = "0.3.8"
|
version = "0.3.8"
|
||||||
|
@ -1696,6 +1857,12 @@ version = "0.5.6"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "0717cef1bc8b636c6e1c1bbdefc09e6322da8a9321966e8928ef80d20f7f770f"
|
checksum = "0717cef1bc8b636c6e1c1bbdefc09e6322da8a9321966e8928ef80d20f7f770f"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "linux-raw-sys"
|
||||||
|
version = "0.4.13"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "01cda141df6706de531b6c46c3a33ecca755538219bd484262fa09410c13539c"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "lock_api"
|
name = "lock_api"
|
||||||
version = "0.4.11"
|
version = "0.4.11"
|
||||||
|
@ -1721,6 +1888,12 @@ dependencies = [
|
||||||
"linked-hash-map",
|
"linked-hash-map",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "lru_time_cache"
|
||||||
|
version = "0.11.11"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "9106e1d747ffd48e6be5bb2d97fa706ed25b144fbee4d5c02eae110cd8d6badd"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "lz4-sys"
|
name = "lz4-sys"
|
||||||
version = "1.9.4"
|
version = "1.9.4"
|
||||||
|
@ -1822,6 +1995,24 @@ dependencies = [
|
||||||
"windows-sys 0.48.0",
|
"windows-sys 0.48.0",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "native-tls"
|
||||||
|
version = "0.2.11"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "07226173c32f2926027b63cce4bcd8076c3552846cbe7925f3aaffeac0a3b92e"
|
||||||
|
dependencies = [
|
||||||
|
"lazy_static",
|
||||||
|
"libc",
|
||||||
|
"log",
|
||||||
|
"openssl",
|
||||||
|
"openssl-probe",
|
||||||
|
"openssl-sys",
|
||||||
|
"schannel",
|
||||||
|
"security-framework",
|
||||||
|
"security-framework-sys",
|
||||||
|
"tempfile",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "nix"
|
name = "nix"
|
||||||
version = "0.26.4"
|
version = "0.26.4"
|
||||||
|
@ -1964,6 +2155,32 @@ version = "1.19.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "3fdb12b2476b595f9358c5161aa467c2438859caa136dec86c26fdd2efe17b92"
|
checksum = "3fdb12b2476b595f9358c5161aa467c2438859caa136dec86c26fdd2efe17b92"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "openid-client"
|
||||||
|
version = "0.1.2"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "ca96ef162fe17a8487ed6b8ffef725c0417b2554363f6a768770de1e326e6916"
|
||||||
|
dependencies = [
|
||||||
|
"async-recursion",
|
||||||
|
"base64 0.21.7",
|
||||||
|
"base64-url",
|
||||||
|
"josekit",
|
||||||
|
"jwt-compact",
|
||||||
|
"lazy_static",
|
||||||
|
"lru_time_cache",
|
||||||
|
"querystring",
|
||||||
|
"rand",
|
||||||
|
"regex",
|
||||||
|
"reqwest",
|
||||||
|
"serde",
|
||||||
|
"serde_json",
|
||||||
|
"sha2",
|
||||||
|
"sha3",
|
||||||
|
"tokio",
|
||||||
|
"url",
|
||||||
|
"urlencoding",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "openidconnect"
|
name = "openidconnect"
|
||||||
version = "3.5.0"
|
version = "3.5.0"
|
||||||
|
@ -1996,12 +2213,60 @@ dependencies = [
|
||||||
"url",
|
"url",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "openssl"
|
||||||
|
version = "0.10.64"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "95a0481286a310808298130d22dd1fef0fa571e05a8f44ec801801e84b216b1f"
|
||||||
|
dependencies = [
|
||||||
|
"bitflags 2.4.2",
|
||||||
|
"cfg-if",
|
||||||
|
"foreign-types",
|
||||||
|
"libc",
|
||||||
|
"once_cell",
|
||||||
|
"openssl-macros",
|
||||||
|
"openssl-sys",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "openssl-macros"
|
||||||
|
version = "0.1.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "a948666b637a0f465e8564c73e89d4dde00d72d4d473cc972f390fc3dcee7d9c"
|
||||||
|
dependencies = [
|
||||||
|
"proc-macro2",
|
||||||
|
"quote",
|
||||||
|
"syn 2.0.51",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "openssl-probe"
|
name = "openssl-probe"
|
||||||
version = "0.1.5"
|
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 = "ff011a302c396a5197692431fc1948019154afc178baf7d8e37367442a4601cf"
|
checksum = "ff011a302c396a5197692431fc1948019154afc178baf7d8e37367442a4601cf"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "openssl-src"
|
||||||
|
version = "300.2.3+3.2.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "5cff92b6f71555b61bb9315f7c64da3ca43d87531622120fea0195fc761b4843"
|
||||||
|
dependencies = [
|
||||||
|
"cc",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "openssl-sys"
|
||||||
|
version = "0.9.101"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "dda2b0f344e78efc2facf7d195d098df0dd72151b26ab98da807afc26c198dff"
|
||||||
|
dependencies = [
|
||||||
|
"cc",
|
||||||
|
"libc",
|
||||||
|
"openssl-src",
|
||||||
|
"pkg-config",
|
||||||
|
"vcpkg",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "opentelemetry"
|
name = "opentelemetry"
|
||||||
version = "0.18.0"
|
version = "0.18.0"
|
||||||
|
@ -2364,6 +2629,12 @@ dependencies = [
|
||||||
"yansi",
|
"yansi",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "querystring"
|
||||||
|
version = "1.1.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "9318ead08c799aad12a55a3e78b82e0b6167271ffd1f627b758891282f739187"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "quick-error"
|
name = "quick-error"
|
||||||
version = "1.2.3"
|
version = "1.2.3"
|
||||||
|
@ -2489,10 +2760,12 @@ dependencies = [
|
||||||
"http-body 0.4.6",
|
"http-body 0.4.6",
|
||||||
"hyper",
|
"hyper",
|
||||||
"hyper-rustls",
|
"hyper-rustls",
|
||||||
|
"hyper-tls",
|
||||||
"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",
|
||||||
|
@ -2505,6 +2778,7 @@ dependencies = [
|
||||||
"sync_wrapper",
|
"sync_wrapper",
|
||||||
"system-configuration",
|
"system-configuration",
|
||||||
"tokio",
|
"tokio",
|
||||||
|
"tokio-native-tls",
|
||||||
"tokio-rustls",
|
"tokio-rustls",
|
||||||
"tokio-socks",
|
"tokio-socks",
|
||||||
"tower-service",
|
"tower-service",
|
||||||
|
@ -2817,6 +3091,19 @@ dependencies = [
|
||||||
"semver",
|
"semver",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "rustix"
|
||||||
|
version = "0.38.31"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "6ea3e1a662af26cd7a3ba09c0297a31af215563ecf42817c98df621387f4e949"
|
||||||
|
dependencies = [
|
||||||
|
"bitflags 2.4.2",
|
||||||
|
"errno",
|
||||||
|
"libc",
|
||||||
|
"linux-raw-sys",
|
||||||
|
"windows-sys 0.52.0",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "rustls"
|
name = "rustls"
|
||||||
version = "0.21.10"
|
version = "0.21.10"
|
||||||
|
@ -2995,6 +3282,7 @@ version = "1.0.114"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "c5f09b1bd632ef549eaa9f60a1f8de742bdbc698e6cee2095fc84dde5f549ae0"
|
checksum = "c5f09b1bd632ef549eaa9f60a1f8de742bdbc698e6cee2095fc84dde5f549ae0"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
|
"indexmap 2.2.3",
|
||||||
"itoa",
|
"itoa",
|
||||||
"ryu",
|
"ryu",
|
||||||
"serde",
|
"serde",
|
||||||
|
@ -3116,6 +3404,16 @@ dependencies = [
|
||||||
"digest",
|
"digest",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "sha3"
|
||||||
|
version = "0.10.8"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "75872d278a8f37ef87fa0ddbda7802605cb18344497949862c0d4dcb291eba60"
|
||||||
|
dependencies = [
|
||||||
|
"digest",
|
||||||
|
"keccak",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "sharded-slab"
|
name = "sharded-slab"
|
||||||
version = "0.1.7"
|
version = "0.1.7"
|
||||||
|
@ -3285,6 +3583,18 @@ dependencies = [
|
||||||
"libc",
|
"libc",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "tempfile"
|
||||||
|
version = "3.10.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "85b77fafb263dd9d05cbeac119526425676db3784113aa9295c88498cbf8bff1"
|
||||||
|
dependencies = [
|
||||||
|
"cfg-if",
|
||||||
|
"fastrand",
|
||||||
|
"rustix",
|
||||||
|
"windows-sys 0.52.0",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "thiserror"
|
name = "thiserror"
|
||||||
version = "1.0.57"
|
version = "1.0.57"
|
||||||
|
@ -3425,6 +3735,7 @@ dependencies = [
|
||||||
"libc",
|
"libc",
|
||||||
"mio",
|
"mio",
|
||||||
"num_cpus",
|
"num_cpus",
|
||||||
|
"parking_lot",
|
||||||
"pin-project-lite",
|
"pin-project-lite",
|
||||||
"signal-hook-registry",
|
"signal-hook-registry",
|
||||||
"socket2",
|
"socket2",
|
||||||
|
@ -3443,6 +3754,16 @@ dependencies = [
|
||||||
"syn 2.0.51",
|
"syn 2.0.51",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[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.24.1"
|
version = "0.24.1"
|
||||||
|
@ -3810,6 +4131,12 @@ dependencies = [
|
||||||
"serde",
|
"serde",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "urlencoding"
|
||||||
|
version = "2.1.3"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "daf8dba3b7eb870caf1ddeed7bc9d2a049f3cfdfae7cb521b087cc33ae4c49da"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "uuid"
|
name = "uuid"
|
||||||
version = "1.7.0"
|
version = "1.7.0"
|
||||||
|
@ -4165,6 +4492,20 @@ name = "zeroize"
|
||||||
version = "1.7.0"
|
version = "1.7.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "525b4ec142c6b68a2d10f01f7bbf6755599ca3f81ea53b8431b7dd348f5fdb2d"
|
checksum = "525b4ec142c6b68a2d10f01f7bbf6755599ca3f81ea53b8431b7dd348f5fdb2d"
|
||||||
|
dependencies = [
|
||||||
|
"zeroize_derive",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "zeroize_derive"
|
||||||
|
version = "1.4.2"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "ce36e65b0d2999d2aafac989fb249189a141aee1f53c612c1f37d72631959f69"
|
||||||
|
dependencies = [
|
||||||
|
"proc-macro2",
|
||||||
|
"quote",
|
||||||
|
"syn 2.0.51",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "zigzag"
|
name = "zigzag"
|
||||||
|
|
|
@ -117,15 +117,14 @@ lazy_static = "1.4.0"
|
||||||
async-trait = "0.1.68"
|
async-trait = "0.1.68"
|
||||||
|
|
||||||
sd-notify = { version = "0.4.1", optional = true }
|
sd-notify = { version = "0.4.1", optional = true }
|
||||||
|
|
||||||
# Used for SSO through OIDC
|
|
||||||
openidconnect = { version = "3.5.0", features = ["jwk-alg", "accept-string-booleans"] }
|
|
||||||
url = { version = "2.5.0", features = ["serde"] }
|
url = { version = "2.5.0", features = ["serde"] }
|
||||||
|
|
||||||
# Used for SSO as fallback for non-web clients
|
# Used for SSO as fallback for non-web clients
|
||||||
askama = { version = "0.12.1", features = ["with-axum"] }
|
askama = { version = "0.12.1", features = ["with-axum"] }
|
||||||
askama_axum = { version = "0.4.0", features = ["urlencode", "config"] }
|
askama_axum = { version = "0.4.0", features = ["urlencode", "config"] }
|
||||||
time = "0.3.34"
|
time = "0.3.34"
|
||||||
|
openid-client = "0.1.2"
|
||||||
|
openidconnect = "3.5.0"
|
||||||
|
|
||||||
[target.'cfg(unix)'.dependencies]
|
[target.'cfg(unix)'.dependencies]
|
||||||
nix = { version = "0.26.2", features = ["resource"] }
|
nix = { version = "0.26.2", features = ["resource"] }
|
||||||
|
@ -176,7 +175,7 @@ systemd-units = { unit-name = "matrix-conduit" }
|
||||||
|
|
||||||
[profile.dev]
|
[profile.dev]
|
||||||
lto = 'off'
|
lto = 'off'
|
||||||
incremental = false
|
incremental = true
|
||||||
|
|
||||||
[profile.release]
|
[profile.release]
|
||||||
lto = 'thin'
|
lto = 'thin'
|
||||||
|
|
|
@ -58,15 +58,41 @@ address = "127.0.0.1" # This makes sure Conduit can only be reached using the re
|
||||||
|
|
||||||
macaroon_key = "this is the key" # Currently only used in SSO as short-term login token
|
macaroon_key = "this is the key" # Currently only used in SSO as short-term login token
|
||||||
|
|
||||||
|
[[global.sso]]
|
||||||
|
id = "authentik"
|
||||||
|
name = "authentik"
|
||||||
|
|
||||||
|
issuer = "https://your.authentik.example.org/application/o/your-app-slug"
|
||||||
|
scopes = ["openid", "profile", "email"]
|
||||||
|
|
||||||
|
[global.sso.client]
|
||||||
|
id = "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"
|
||||||
|
|
||||||
|
[[global.sso]]
|
||||||
|
id = "github"
|
||||||
|
name = "Github"
|
||||||
|
subject_claim = "id"
|
||||||
|
# localpart = "{{ user.login }}"
|
||||||
|
# display_name = "{{ user.name }}"
|
||||||
|
scopes = ["read:user"]
|
||||||
|
issuer = "https://github.com"
|
||||||
|
|
||||||
|
[global.sso.client]
|
||||||
|
id = "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"
|
||||||
|
|
||||||
|
[global.sso.discover.manual]
|
||||||
|
auth = "https://github.com/login/oauth/authorize"
|
||||||
|
token = "https://github.com/login/oauth/access_token"
|
||||||
|
userinfo = "https://api.github.com/user"
|
||||||
|
|
||||||
[[global.sso]]
|
[[global.sso]]
|
||||||
id = "gitlab"
|
id = "gitlab"
|
||||||
name = "Gitlab"
|
name = "Gitlab"
|
||||||
icon = "mxc://kurosaki.cx/KKbSvyoUEYXdrzXBJoOJoLpZbqFYrCpW"
|
icon = "mxc://kurosaki.cx/KKbSvyoUEYXdrzXBJoOJoLpZbqFYrCpW"
|
||||||
|
|
||||||
issuer = "https://gitlab.com"
|
|
||||||
scopes = ["openid", "profile"]
|
scopes = ["openid", "profile"]
|
||||||
|
issuer = "https://gitlab.com"
|
||||||
|
|
||||||
[global.sso.client]
|
[global.sso.client]
|
||||||
id = "12dd00d057420beda06fd5edcd21287026dc0c66ba5c02d40c2eff8b559c6709"
|
id = "12dd00d057420beda06fd5edcd21287026dc0c66ba5c02d40c2eff8b559c6709"
|
||||||
secret = "3a806573cacf5da560b8c720cf32019255908c83e31ba78d280cf08a4eb619fd"
|
secret = "3a806573cacf5da560b8c720cf32019255908c83e31ba78d280cf08a4eb619fd"
|
||||||
auth_method = "post"
|
auth_method = "client_secret_post"
|
||||||
|
|
|
@ -52,7 +52,7 @@
|
||||||
<img src="{{ icon }}"/>
|
<img src="{{ icon }}"/>
|
||||||
{% when None %}
|
{% when None %}
|
||||||
{% endmatch %}
|
{% endmatch %}
|
||||||
<span>{{ idp.name.as_deref().unwrap_or(idp.id) }}</span>
|
<span>{{ idp.name }}</span>
|
||||||
</a>
|
</a>
|
||||||
</li>
|
</li>
|
||||||
{% endfor %}
|
{% endfor %}
|
||||||
|
|
|
@ -152,7 +152,7 @@
|
||||||
<img src="{{ icon }}"/>
|
<img src="{{ icon }}"/>
|
||||||
{% when None %}
|
{% when None %}
|
||||||
{% endmatch %}
|
{% endmatch %}
|
||||||
Optional data from {{ idp.name.as_deref().unwrap_or(idp.id) }}</h2>
|
Optional data from {{ idp.name }}</h2>
|
||||||
{% if let Some(avatar_url) = user.avatar_url %}
|
{% if let Some(avatar_url) = user.avatar_url %}
|
||||||
<label class="idp-detail idp-avatar" for="idp-avatar">
|
<label class="idp-detail idp-avatar" for="idp-avatar">
|
||||||
<div class="check-row">
|
<div class="check-row">
|
||||||
|
|
|
@ -28,7 +28,7 @@ pub async fn get_login_types_route(
|
||||||
.sso
|
.sso
|
||||||
.inner
|
.inner
|
||||||
.iter()
|
.iter()
|
||||||
.map(|p| p.config.clone().into())
|
.map(|p| p.inner.clone())
|
||||||
.collect();
|
.collect();
|
||||||
|
|
||||||
Ok(get_login_types::v3::Response::new(vec![
|
Ok(get_login_types::v3::Response::new(vec![
|
||||||
|
|
|
@ -6,10 +6,8 @@ use askama::Template;
|
||||||
use axum::{body::Full, response::IntoResponse};
|
use axum::{body::Full, response::IntoResponse};
|
||||||
use axum_extra::extract::cookie::{Cookie, SameSite};
|
use axum_extra::extract::cookie::{Cookie, SameSite};
|
||||||
use bytes::BytesMut;
|
use bytes::BytesMut;
|
||||||
use http::{HeaderValue, StatusCode};
|
use http::{header::SET_COOKIE, HeaderValue, StatusCode};
|
||||||
use openidconnect::{
|
use openidconnect::{AuthorizationCode, CsrfToken, RedirectUrl};
|
||||||
AuthorizationCode, CsrfToken,
|
|
||||||
};
|
|
||||||
use ruma::api::{
|
use ruma::api::{
|
||||||
client::{error::ErrorKind, session},
|
client::{error::ErrorKind, session},
|
||||||
OutgoingResponse,
|
OutgoingResponse,
|
||||||
|
@ -38,39 +36,30 @@ pub async fn get_sso_redirect_with_provider(
|
||||||
// State(uiaa_session): State<Option<()>>,
|
// State(uiaa_session): State<Option<()>>,
|
||||||
body: Ruma<session::sso_login_with_provider::v3::Request>,
|
body: Ruma<session::sso_login_with_provider::v3::Request>,
|
||||||
) -> axum::response::Response {
|
) -> axum::response::Response {
|
||||||
|
let redirect_url = RedirectUrl::new(body.redirect_url.clone().unwrap()).unwrap();
|
||||||
if services().sso.get_all().is_empty() {
|
if services().sso.get_all().is_empty() {
|
||||||
return Error::BadRequest(ErrorKind::NotFound, "SSO has not been configured")
|
return Error::BadRequest(ErrorKind::NotFound, "SSO has not been configured")
|
||||||
.into_response();
|
.into_response();
|
||||||
}
|
}
|
||||||
|
|
||||||
if body.idp_id.is_empty() {
|
if body.idp_id.is_empty() {
|
||||||
return get_sso_fallback_template(body.redirect_url.as_deref().unwrap_or_default())
|
return get_sso_fallback_template(redirect_url.as_str())
|
||||||
.into_response();
|
.into_response();
|
||||||
};
|
};
|
||||||
|
|
||||||
let location = Some(body.idp_id.clone());
|
let provider = match services()
|
||||||
|
.sso
|
||||||
|
.find_one(&body.idp_id)
|
||||||
|
{
|
||||||
|
Ok(provider) => provider,
|
||||||
|
Err(e) => return e.into_response(),
|
||||||
|
};
|
||||||
|
|
||||||
let (url, nonce, cookie) =
|
let (url, _, cookie) = provider.handle_redirect(redirect_url).await;
|
||||||
match services().sso.find_one(&body.idp_id).map(|provider| {
|
|
||||||
provider.handle_redirect(body.redirect_url.unwrap())
|
|
||||||
}) {
|
|
||||||
Ok(fut) => fut.await,
|
|
||||||
Err(e) => return e.into_response(),
|
|
||||||
};
|
|
||||||
|
|
||||||
let cookie = Cookie::build("openid-state", cookie)
|
|
||||||
.path("/_conduit/client/sso")
|
|
||||||
// .secure(false) //FIXME
|
|
||||||
.secure(true)
|
|
||||||
.http_only(true)
|
|
||||||
.same_site(SameSite::None)
|
|
||||||
.max_age(time::Duration::seconds(COOKIE_STATE_EXPIRATION_SECS))
|
|
||||||
.finish()
|
|
||||||
.to_string();
|
|
||||||
|
|
||||||
let mut res = session::sso_login_with_provider::v3::Response {
|
let mut res = session::sso_login_with_provider::v3::Response {
|
||||||
location,
|
location: Some(url.to_string()),
|
||||||
cookie: Some(cookie),
|
cookie: Some(cookie.to_string()),
|
||||||
}
|
}
|
||||||
.try_into_http_response::<BytesMut>()
|
.try_into_http_response::<BytesMut>()
|
||||||
.unwrap();
|
.unwrap();
|
||||||
|
@ -82,8 +71,13 @@ pub async fn get_sso_redirect_with_provider(
|
||||||
|
|
||||||
fn get_sso_fallback_template(redirect_url: &str) -> axum::response::Response {
|
fn get_sso_fallback_template(redirect_url: &str) -> axum::response::Response {
|
||||||
let server_name = services().globals.server_name().to_string();
|
let server_name = services().globals.server_name().to_string();
|
||||||
let metadata = services().sso.inner.iter().map(Into::into).collect();
|
|
||||||
let redirect_url = redirect_url.to_string();
|
let redirect_url = redirect_url.to_string();
|
||||||
|
let metadata = services()
|
||||||
|
.sso
|
||||||
|
.inner
|
||||||
|
.iter()
|
||||||
|
.map(|p| p.inner.clone())
|
||||||
|
.collect();
|
||||||
|
|
||||||
let t = templates::IdpPicker {
|
let t = templates::IdpPicker {
|
||||||
server_name,
|
server_name,
|
||||||
|
@ -146,7 +140,7 @@ pub async fn get_sso_callback(
|
||||||
Err(error) => return error.into_response(),
|
Err(error) => return error.into_response(),
|
||||||
};
|
};
|
||||||
|
|
||||||
let provider = match services().sso.find_one(macaroon.idp_id.as_ref()) {
|
let provider = match services().sso.find_one(&macaroon.idp_id) {
|
||||||
Ok(provider) => provider,
|
Ok(provider) => provider,
|
||||||
Err(error) => return error.into_response(),
|
Err(error) => return error.into_response(),
|
||||||
};
|
};
|
||||||
|
@ -155,9 +149,7 @@ pub async fn get_sso_callback(
|
||||||
let user_info = provider.handle_callback(code, macaroon.nonce).await;
|
let user_info = provider.handle_callback(code, macaroon.nonce).await;
|
||||||
|
|
||||||
(
|
(
|
||||||
axum::TypedHeader(axum::headers::Location(
|
axum::response::AppendHeaders([(SET_COOKIE, cookie)]),
|
||||||
HeaderValue::from_str(clear_cookie.as_str()).unwrap(),
|
|
||||||
)),
|
|
||||||
"Hello, World!",
|
"Hello, World!",
|
||||||
)
|
)
|
||||||
.into_response()
|
.into_response()
|
||||||
|
|
|
@ -1,6 +1,5 @@
|
||||||
use openidconnect::JsonWebKeyId;
|
|
||||||
use ruma::OwnedMxcUri;
|
use ruma::OwnedMxcUri;
|
||||||
use serde::Deserialize;
|
use serde::{Deserialize, Serialize};
|
||||||
|
|
||||||
#[derive(Clone, Debug, Deserialize)]
|
#[derive(Clone, Debug, Deserialize)]
|
||||||
pub struct ProviderConfig {
|
pub struct ProviderConfig {
|
||||||
|
@ -37,7 +36,7 @@ pub struct ProviderConfig {
|
||||||
|
|
||||||
// Should be enabled when the authorization response does not contain userinfo
|
// Should be enabled when the authorization response does not contain userinfo
|
||||||
#[serde(default)]
|
#[serde(default)]
|
||||||
pub userinfo_override: bool,
|
pub force_userinfo: bool,
|
||||||
|
|
||||||
#[serde(default)]
|
#[serde(default)]
|
||||||
pub discovery: DiscoveryConfig,
|
pub discovery: DiscoveryConfig,
|
||||||
|
@ -48,9 +47,14 @@ pub struct ClientConfig {
|
||||||
pub id: String,
|
pub id: String,
|
||||||
// Mandatory for the following `ClientAuthMethod`s:
|
// Mandatory for the following `ClientAuthMethod`s:
|
||||||
// [`Basic`,`Post`,`SharedJwt`]
|
// [`Basic`,`Post`,`SharedJwt`]
|
||||||
|
#[serde(default)]
|
||||||
pub secret: Option<String>,
|
pub secret: Option<String>,
|
||||||
|
|
||||||
|
#[serde(default)]
|
||||||
pub auth_method: AuthMethod,
|
pub auth_method: AuthMethod,
|
||||||
|
|
||||||
|
// #[serde(default)]
|
||||||
|
// pub private_jwt: Option<PrivateJwt>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone, Debug, Deserialize)]
|
#[derive(Clone, Debug, Deserialize)]
|
||||||
|
@ -59,6 +63,7 @@ pub struct Endpoints {
|
||||||
pub token: Option<url::Url>,
|
pub token: Option<url::Url>,
|
||||||
pub userinfo: Option<url::Url>,
|
pub userinfo: Option<url::Url>,
|
||||||
pub jwk: Option<url::Url>,
|
pub jwk: Option<url::Url>,
|
||||||
|
pub ok: AuthMethod,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone, Debug, Default, Deserialize)]
|
#[derive(Clone, Debug, Default, Deserialize)]
|
||||||
|
@ -70,30 +75,32 @@ pub enum DiscoveryConfig {
|
||||||
Manual(Endpoints),
|
Manual(Endpoints),
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone, Debug, Deserialize)]
|
#[derive(Clone, Debug, Default, Deserialize, Serialize)]
|
||||||
#[serde(rename_all = "snake_case")]
|
|
||||||
pub enum AuthMethod {
|
pub enum AuthMethod {
|
||||||
|
#[serde(rename = "none")]
|
||||||
None,
|
None,
|
||||||
|
|
||||||
// Provide the client combo in the Authorization header
|
// Provide the client combo in the Authorization header
|
||||||
|
#[default]
|
||||||
|
#[serde(rename = "client_secret_basic")]
|
||||||
Basic,
|
Basic,
|
||||||
// Provide the client combo as in the POST request body
|
|
||||||
|
// Provide the client combo in the POST request body
|
||||||
|
#[serde(rename = "client_secret_post")]
|
||||||
Post,
|
Post,
|
||||||
|
|
||||||
// Provide a JWT signed with client secret
|
// Provide a JWT signed with client secret
|
||||||
|
#[serde(rename = "client_secret_jwt")]
|
||||||
SharedJwt,
|
SharedJwt,
|
||||||
|
|
||||||
// Provide a JWT signed with a private key (OP needs to know the public key)
|
// Provide a JWT signed with a private key (OP needs to know the public key)
|
||||||
|
#[serde(rename = "private_key_jwt")]
|
||||||
PrivateJwt,
|
PrivateJwt,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone, Debug, Deserialize)]
|
// #[derive(Clone, Debug, Deserialize)]
|
||||||
#[serde(rename_all = "lowercase")]
|
// pub struct PrivateJwt {
|
||||||
pub enum Algorithm {
|
// pub payload:
|
||||||
Rsa,
|
// pub header: jsonwebtoken::Header,
|
||||||
EdDsa,
|
// pub key: jsonwebtoken::EncodingKey,
|
||||||
}
|
// }
|
||||||
|
|
||||||
#[derive(Clone, Debug, Deserialize)]
|
|
||||||
pub struct PrivateSigningKey {
|
|
||||||
pub kind: Algorithm,
|
|
||||||
pub path: String,
|
|
||||||
pub kid: Option<JsonWebKeyId>,
|
|
||||||
}
|
|
||||||
|
|
|
@ -24,7 +24,7 @@ use ruma::api::{
|
||||||
IncomingRequest,
|
IncomingRequest,
|
||||||
};
|
};
|
||||||
use tokio::signal;
|
use tokio::signal;
|
||||||
use tower::{ServiceBuilder, ServiceExt};
|
use tower::{ServiceBuilder};
|
||||||
use tower_http::{
|
use tower_http::{
|
||||||
cors::{self, CorsLayer},
|
cors::{self, CorsLayer},
|
||||||
trace::TraceLayer,
|
trace::TraceLayer,
|
||||||
|
|
|
@ -160,6 +160,8 @@ impl Service {
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
let macaroon_key = config.macaroon_key.clone();
|
||||||
|
|
||||||
let tls_name_override = Arc::new(RwLock::new(TlsNameMap::new()));
|
let tls_name_override = Arc::new(RwLock::new(TlsNameMap::new()));
|
||||||
|
|
||||||
let jwt_decoding_key = config
|
let jwt_decoding_key = config
|
||||||
|
@ -213,7 +215,7 @@ impl Service {
|
||||||
sync_receivers: RwLock::new(HashMap::new()),
|
sync_receivers: RwLock::new(HashMap::new()),
|
||||||
rotate: RotationHandler::new(),
|
rotate: RotationHandler::new(),
|
||||||
shutdown: AtomicBool::new(false),
|
shutdown: AtomicBool::new(false),
|
||||||
macaroon_key: config.macaroon_key.clone(),
|
macaroon_key,
|
||||||
};
|
};
|
||||||
|
|
||||||
fs::create_dir_all(s.get_media_folder())?;
|
fs::create_dir_all(s.get_media_folder())?;
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
use jsonwebtoken::{Algorithm, DecodingKey, EncodingKey, Header, Validation};
|
use jsonwebtoken::{Algorithm, DecodingKey, EncodingKey, Header, Validation};
|
||||||
use openidconnect::{Nonce, PkceCodeVerifier, RedirectUrl, CsrfToken};
|
use openidconnect::{CsrfToken, Nonce, RedirectUrl};
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
|
use time::OffsetDateTime;
|
||||||
|
|
||||||
use crate::Error;
|
use crate::Error;
|
||||||
|
|
||||||
|
@ -9,12 +10,21 @@ pub struct Macaroon {
|
||||||
pub idp_id: String,
|
pub idp_id: String,
|
||||||
pub nonce: Nonce,
|
pub nonce: Nonce,
|
||||||
pub csrf: CsrfToken,
|
pub csrf: CsrfToken,
|
||||||
pub redirect_url: Option<RedirectUrl>,
|
pub redirect_url: RedirectUrl,
|
||||||
pub pkce_verifier: Option<PkceCodeVerifier>,
|
|
||||||
pub time: i64,
|
pub time: i64,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Macaroon {
|
impl Macaroon {
|
||||||
|
pub fn new(idp_id: String, nonce: Nonce, csrf: CsrfToken, redirect_url: RedirectUrl) -> Self {
|
||||||
|
Self {
|
||||||
|
idp_id,
|
||||||
|
nonce,
|
||||||
|
csrf,
|
||||||
|
redirect_url,
|
||||||
|
time: OffsetDateTime::now_utc().unix_timestamp(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
pub fn encode(&self, macaroon: &str) -> Result<String, jsonwebtoken::errors::Error> {
|
pub fn encode(&self, macaroon: &str) -> Result<String, jsonwebtoken::errors::Error> {
|
||||||
jsonwebtoken::encode(
|
jsonwebtoken::encode(
|
||||||
&Header::default(),
|
&Header::default(),
|
||||||
|
@ -38,7 +48,7 @@ impl Macaroon {
|
||||||
|
|
||||||
Err(Error::BadRequest(
|
Err(Error::BadRequest(
|
||||||
ruma::api::client::error::ErrorKind::Unauthorized,
|
ruma::api::client::error::ErrorKind::Unauthorized,
|
||||||
"macaroon invalid",
|
"macaroon encoding",
|
||||||
))
|
))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,21 +1,15 @@
|
||||||
use std::sync::Arc;
|
use std::{collections::HashMap, sync::Arc};
|
||||||
|
|
||||||
mod session;
|
|
||||||
|
|
||||||
|
use axum_extra::extract::cookie::{Cookie, SameSite};
|
||||||
use futures_util::future::{self};
|
use futures_util::future::{self};
|
||||||
use openidconnect::{
|
use openidconnect::{
|
||||||
core::{
|
core::{CoreAuthenticationFlow, CoreClient, CoreIdTokenClaims, CoreProviderMetadata},
|
||||||
CoreAuthenticationFlow, CoreClient, CoreGenderClaim, CoreIdTokenClaims,
|
reqwest::{async_http_client, http_client},
|
||||||
CoreProviderMetadata, CoreUserInfoClaims,
|
AdditionalClaims, AuthUrl, AuthorizationCode, ClientId, ClientSecret, CsrfToken, EndUserName,
|
||||||
},
|
IdTokenClaims, IssuerUrl, Nonce, RedirectUrl, Scope, StandardClaims, TokenResponse, TokenUrl,
|
||||||
reqwest::async_http_client,
|
UserInfoUrl,
|
||||||
AccessTokenHash, AdditionalClaims, AuthUrl, AuthorizationCode, ClientId, ClientSecret,
|
|
||||||
CsrfToken, IssuerUrl, Nonce, NonceVerifier, OAuth2TokenResponse, PkceCodeChallenge,
|
|
||||||
PkceCodeVerifier, RedirectUrl, Scope, SubjectIdentifier, TokenResponse, TokenUrl,
|
|
||||||
UserInfoClaims, UserInfoUrl,
|
|
||||||
};
|
};
|
||||||
use ruma::api::client::{error::ErrorKind, session::get_login_types::v3::IdentityProvider};
|
use ruma::api::client::{error::ErrorKind, session::get_login_types::v3::IdentityProvider};
|
||||||
use time::{macros::format_description, OffsetDateTime};
|
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
config::{ClientConfig, DiscoveryConfig as Discovery, ProviderConfig},
|
config::{ClientConfig, DiscoveryConfig as Discovery, ProviderConfig},
|
||||||
|
@ -35,9 +29,10 @@ pub struct Service {
|
||||||
|
|
||||||
impl Service {
|
impl Service {
|
||||||
pub async fn build(config: &Config) -> Arc<Self> {
|
pub async fn build(config: &Config) -> Arc<Self> {
|
||||||
Arc::new(Self {
|
// let inner = future::join_all(config.sso.clone().into_iter().map(Provider::new)).await;
|
||||||
inner: future::join_all(config.sso.clone().into_iter().map(Provider::new)).await,
|
let inner = config.sso.clone().into_iter().map(Provider::new).collect();
|
||||||
})
|
|
||||||
|
Arc::new(Self { inner })
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn find_one(&self, idp_id: impl AsRef<str>) -> Result<Provider, Error> {
|
pub fn find_one(&self, idp_id: impl AsRef<str>) -> Result<Provider, Error> {
|
||||||
|
@ -60,60 +55,47 @@ pub struct Provider {
|
||||||
pub inner: IdentityProvider,
|
pub inner: IdentityProvider,
|
||||||
pub client: Arc<CoreClient>,
|
pub client: Arc<CoreClient>,
|
||||||
pub scopes: Vec<String>,
|
pub scopes: Vec<String>,
|
||||||
pub pkce: Option<bool>,
|
|
||||||
pub subject_claim: Option<String>,
|
pub subject_claim: Option<String>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Provider {
|
impl Provider {
|
||||||
pub async fn new(config: ProviderConfig) -> Self {
|
pub fn new(config: ProviderConfig) -> Self {
|
||||||
let inner = IdentityProvider {
|
|
||||||
id: config.id.clone(),
|
|
||||||
name: config.name.unwrap_or(config.id),
|
|
||||||
icon: config.icon,
|
|
||||||
brand: None,
|
|
||||||
};
|
|
||||||
|
|
||||||
Self {
|
Self {
|
||||||
inner,
|
|
||||||
client: Provider::create_client(config.discovery, config.issuer, config.client)
|
client: Provider::create_client(config.discovery, config.issuer, config.client)
|
||||||
.await
|
|
||||||
.unwrap(),
|
.unwrap(),
|
||||||
|
inner: IdentityProvider {
|
||||||
|
id: config.id.clone(),
|
||||||
|
name: config.name.unwrap_or(config.id),
|
||||||
|
icon: config.icon,
|
||||||
|
brand: None,
|
||||||
|
},
|
||||||
scopes: config.scopes,
|
scopes: config.scopes,
|
||||||
pkce: config.pkce,
|
|
||||||
subject_claim: config.subject_claim,
|
subject_claim: config.subject_claim,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn create_client(
|
fn create_client(
|
||||||
discovery: Discovery,
|
discovery: Discovery,
|
||||||
issuer: url::Url,
|
issuer: url::Url,
|
||||||
config: ClientConfig,
|
config: ClientConfig,
|
||||||
) -> Result<Arc<CoreClient>, Error> {
|
) -> Result<Arc<CoreClient>, Error> {
|
||||||
let mut base_url = url::Url::try_from(
|
let base_url = services()
|
||||||
services()
|
.globals
|
||||||
.globals
|
.well_known_client()
|
||||||
.well_known_client()
|
.as_deref()
|
||||||
.as_deref()
|
.unwrap_or(services().globals.server_name().as_str());
|
||||||
.unwrap_or(services().globals.server_name().as_str()),
|
|
||||||
)
|
|
||||||
.expect("server_name should be a valid URL");
|
|
||||||
|
|
||||||
base_url.set_path("_conduit/config/sso/callback");
|
let redirect_url =
|
||||||
let redirect_url = RedirectUrl::from_url(base_url);
|
RedirectUrl::new(format!("https://{base_url}/_conduit/config/sso/callback"))
|
||||||
|
.expect("server_name should be a valid URL");
|
||||||
|
|
||||||
let config = match discovery {
|
let config = match discovery {
|
||||||
Discovery::Automatic => {
|
Discovery::Automatic => {
|
||||||
let url = issuer.to_string();
|
let discovery = CoreProviderMetadata::discover(
|
||||||
let url = url.strip_suffix("/").unwrap();
|
&IssuerUrl::from_url(issuer),
|
||||||
|
http_client,
|
||||||
let discovery = CoreProviderMetadata::discover_async(
|
|
||||||
// https://github.com/ramosbugs/openidconnect-rs/issues/77
|
|
||||||
IssuerUrl::new(url.to_owned()).unwrap(),
|
|
||||||
async_http_client,
|
|
||||||
)
|
)
|
||||||
.await
|
|
||||||
.unwrap();
|
.unwrap();
|
||||||
// .map_err(|e| Error::BadConfig(&e.to_string()))?;
|
|
||||||
|
|
||||||
CoreClient::from_provider_metadata(
|
CoreClient::from_provider_metadata(
|
||||||
discovery,
|
discovery,
|
||||||
|
@ -136,50 +118,41 @@ impl Provider {
|
||||||
Ok(Arc::new(config))
|
Ok(Arc::new(config))
|
||||||
}
|
}
|
||||||
|
|
||||||
pub async fn handle_redirect(&self, redirect_url: &RedirectUrl) -> (url::Url, String, String) {
|
pub async fn handle_redirect(&self, redirect_url: RedirectUrl) -> (url::Url, Nonce, Cookie) {
|
||||||
let client = self.client.clone();
|
let req = self
|
||||||
let scopes = self.scopes.iter().map(ToOwned::to_owned).map(Scope::new);
|
.client
|
||||||
|
|
||||||
let mut req = client
|
|
||||||
.authorize_url(
|
.authorize_url(
|
||||||
CoreAuthenticationFlow::Implicit(true),
|
CoreAuthenticationFlow::AuthorizationCode,
|
||||||
|| CsrfToken::new_random_len(48),
|
|| CsrfToken::new_random_len(48),
|
||||||
|| Nonce::new_random_len(48),
|
|| Nonce::new_random_len(48),
|
||||||
)
|
)
|
||||||
.add_scopes(scopes);
|
.add_scopes(self.scopes.iter().map(ToOwned::to_owned).map(Scope::new));
|
||||||
|
|
||||||
let pkce_verifier = match self.pkce {
|
|
||||||
Some(true) => {
|
|
||||||
let (challenge, verifier) = PkceCodeChallenge::new_random_sha256();
|
|
||||||
req = req.set_pkce_challenge(challenge);
|
|
||||||
|
|
||||||
Some(verifier)
|
|
||||||
}
|
|
||||||
_ => None,
|
|
||||||
};
|
|
||||||
|
|
||||||
let (url, csrf, nonce) = req.url();
|
let (url, csrf, nonce) = req.url();
|
||||||
|
|
||||||
let key = services()
|
let mac = Macaroon::new(
|
||||||
.globals
|
self.inner.id.clone(),
|
||||||
.macaroon_key
|
nonce.clone(),
|
||||||
.as_deref()
|
|
||||||
.expect("macaroon key")
|
|
||||||
.to_owned();
|
|
||||||
let cookie = Macaroon {
|
|
||||||
idp_id: self.inner.id.clone(),
|
|
||||||
csrf,
|
csrf,
|
||||||
nonce: nonce.clone(),
|
redirect_url.clone(),
|
||||||
time: OffsetDateTime::now_utc().unix_timestamp(),
|
);
|
||||||
redirect_url: Some(redirect_url.clone()),
|
|
||||||
pkce_verifier,
|
|
||||||
};
|
|
||||||
let cookie = cookie.encode(&key).expect("bad key");
|
|
||||||
|
|
||||||
(url, nonce.secret().to_owned(), cookie)
|
let cookie = Cookie::build(
|
||||||
|
"sso-state",
|
||||||
|
mac.encode(&services().globals.macaroon_key.as_deref().unwrap())
|
||||||
|
.expect("bad key"),
|
||||||
|
)
|
||||||
|
.path("/_conduit/client/sso")
|
||||||
|
.secure(true)
|
||||||
|
.http_only(true)
|
||||||
|
.same_site(SameSite::None)
|
||||||
|
.max_age(time::Duration::seconds(COOKIE_STATE_EXPIRATION_SECS))
|
||||||
|
.finish();
|
||||||
|
|
||||||
|
(url, nonce, cookie)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub async fn handle_callback<Claims: AdditionalClaims>(
|
pub async fn handle_callback(
|
||||||
&self,
|
&self,
|
||||||
code: AuthorizationCode,
|
code: AuthorizationCode,
|
||||||
nonce: Nonce,
|
nonce: Nonce,
|
||||||
|
@ -196,15 +169,22 @@ impl Provider {
|
||||||
.claims(&self.client.id_token_verifier(), &nonce)
|
.claims(&self.client.id_token_verifier(), &nonce)
|
||||||
.unwrap();
|
.unwrap();
|
||||||
|
|
||||||
if let Some(expected) = claims.access_token_hash() {
|
let claims_map: HashMap<String, String> = serde_json::to_string(claims)
|
||||||
let found =
|
.map(|s| serde_json::from_str(&s).unwrap())
|
||||||
AccessTokenHash::from_token(resp.access_token(), &id_token.signing_alg().unwrap())
|
.unwrap();
|
||||||
.unwrap();
|
tracing::info!(?claims_map);
|
||||||
|
|
||||||
if &found != expected {
|
Ok(())
|
||||||
panic!()
|
|
||||||
}
|
// if let Some(expected) = claims.access_token_hash() {
|
||||||
}
|
// let found =
|
||||||
|
// AccessTokenHash::from_token(resp.access_token(), &id_token.signing_alg().unwrap())
|
||||||
|
// .unwrap();
|
||||||
|
|
||||||
|
// if &found != expected {
|
||||||
|
// panic!()
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
|
||||||
// match self.client.user_info(
|
// match self.client.user_info(
|
||||||
// resp.access_token().to_owned(),
|
// resp.access_token().to_owned(),
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
use askama::Template;
|
use askama::Template;
|
||||||
use ruma::{
|
use ruma::{
|
||||||
api::client::search::search_events::v3::UserProfile, OwnedMxcUri, OwnedServerName, OwnedUserId,
|
api::client::{search::search_events::v3::UserProfile, session::get_login_types::v3::IdentityProvider}, OwnedMxcUri, OwnedServerName, OwnedUserId,
|
||||||
};
|
};
|
||||||
|
|
||||||
use super::Provider;
|
use super::Provider;
|
||||||
|
@ -13,22 +13,6 @@ pub struct AuthConfirmation {
|
||||||
idp_name: String,
|
idp_name: String,
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct Metadata {
|
|
||||||
id: String,
|
|
||||||
name: Option<String>,
|
|
||||||
icon: Option<OwnedMxcUri>,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl From<&Provider> for Metadata {
|
|
||||||
fn from(value: &Provider) -> Self {
|
|
||||||
Self {
|
|
||||||
id: value.config.id.clone(),
|
|
||||||
name: value.config.name.clone(),
|
|
||||||
icon: value.config.icon.clone(),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Template)]
|
#[derive(Template)]
|
||||||
#[template(path = "auth_failure.html", escape = "none")]
|
#[template(path = "auth_failure.html", escape = "none")]
|
||||||
pub struct AuthFailure {
|
pub struct AuthFailure {
|
||||||
|
@ -46,7 +30,7 @@ pub struct Deactivated {}
|
||||||
#[template(path = "idp_picker.html", escape = "none")]
|
#[template(path = "idp_picker.html", escape = "none")]
|
||||||
pub struct IdpPicker {
|
pub struct IdpPicker {
|
||||||
pub server_name: String,
|
pub server_name: String,
|
||||||
pub metadata: Vec<Metadata>,
|
pub metadata: Vec<IdentityProvider>,
|
||||||
pub redirect_url: String,
|
pub redirect_url: String,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -54,7 +38,7 @@ pub struct IdpPicker {
|
||||||
#[template(path = "registration.html", escape = "none")]
|
#[template(path = "registration.html", escape = "none")]
|
||||||
pub struct Registration {
|
pub struct Registration {
|
||||||
pub server_name: OwnedServerName,
|
pub server_name: OwnedServerName,
|
||||||
pub idp: Metadata,
|
pub idp: IdentityProvider,
|
||||||
pub user: Attributes,
|
pub user: Attributes,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -5,7 +5,7 @@ use cmp::Ordering;
|
||||||
use rand::prelude::*;
|
use rand::prelude::*;
|
||||||
use ring::digest;
|
use ring::digest;
|
||||||
use ruma::{
|
use ruma::{
|
||||||
canonical_json::try_from_json_map, CanonicalJsonError, CanonicalJsonObject, MxcUri, MxcUriError,
|
canonical_json::try_from_json_map, CanonicalJsonError, CanonicalJsonObject, MxcUri, MxcUriError, OwnedMxcUri,
|
||||||
};
|
};
|
||||||
use std::{
|
use std::{
|
||||||
cmp, fmt,
|
cmp, fmt,
|
||||||
|
|
Loading…
Reference in a new issue