feat: add oauth flow base
Signed-off-by: Nikolaos Karaolidis <nick@karaolidis.com>
This commit is contained in:
412
Cargo.lock
generated
412
Cargo.lock
generated
@@ -100,6 +100,48 @@ version = "1.7.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "69f7f8c3906b62b754cd5326047894316021dcfe5a194c8ea52bdd94934a3457"
|
||||
|
||||
[[package]]
|
||||
name = "arrayref"
|
||||
version = "0.3.9"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "76a2e8124351fda1ef8aaaa3bbd7ebbcb486bbcd4225aca0aa0d84bb2db8fecb"
|
||||
|
||||
[[package]]
|
||||
name = "arrayvec"
|
||||
version = "0.5.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "23b62fc65de8e4e7f52534fb52b0f3ed04746ae267519eef2a83941e8085068b"
|
||||
|
||||
[[package]]
|
||||
name = "async-lock"
|
||||
version = "2.8.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "287272293e9d8c41773cec55e365490fe034813a2f172f502d6ddcf75b2f582b"
|
||||
dependencies = [
|
||||
"event-listener 2.5.3",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "async-session"
|
||||
version = "3.0.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "07da4ce523b4e2ebaaf330746761df23a465b951a83d84bbce4233dabedae630"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"async-lock",
|
||||
"async-trait",
|
||||
"base64 0.13.1",
|
||||
"bincode",
|
||||
"blake3",
|
||||
"chrono",
|
||||
"hmac 0.11.0",
|
||||
"log",
|
||||
"rand 0.8.5",
|
||||
"serde",
|
||||
"serde_json",
|
||||
"sha2 0.9.9",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "async-trait"
|
||||
version = "0.1.88"
|
||||
@@ -180,6 +222,28 @@ dependencies = [
|
||||
"tracing",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "axum-extra"
|
||||
version = "0.10.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "460fc6f625a1f7705c6cf62d0d070794e94668988b1c38111baeec177c715f7b"
|
||||
dependencies = [
|
||||
"axum",
|
||||
"axum-core",
|
||||
"bytes",
|
||||
"futures-util",
|
||||
"headers",
|
||||
"http",
|
||||
"http-body",
|
||||
"http-body-util",
|
||||
"mime",
|
||||
"pin-project-lite",
|
||||
"serde",
|
||||
"tower",
|
||||
"tower-layer",
|
||||
"tower-service",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "backtrace"
|
||||
version = "0.3.74"
|
||||
@@ -187,7 +251,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8d82cb332cdfaed17ae235a638438ac4d4839913cc2af585c3c6746e8f8bee1a"
|
||||
dependencies = [
|
||||
"addr2line",
|
||||
"cfg-if",
|
||||
"cfg-if 1.0.0",
|
||||
"libc",
|
||||
"miniz_oxide",
|
||||
"object",
|
||||
@@ -201,6 +265,12 @@ version = "0.2.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "4c7f02d4ea65f2c1853089ffd8d2787bdbc63de2f0d29dedbcf8ccdfa0ccd4cf"
|
||||
|
||||
[[package]]
|
||||
name = "base64"
|
||||
version = "0.13.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9e1b586273c5702936fe7b7d6896644d8be71e6314cfe09d3167c95f712589e8"
|
||||
|
||||
[[package]]
|
||||
name = "base64"
|
||||
version = "0.21.7"
|
||||
@@ -219,6 +289,15 @@ version = "1.7.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "89e25b6adfb930f02d1981565a6e5d9c547ac15a96606256d3b59040e5cd4ca3"
|
||||
|
||||
[[package]]
|
||||
name = "bincode"
|
||||
version = "1.3.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b1f45e9417d87227c7a56d22e471c6206462cba514c7590c09aff4cf6d1ddcad"
|
||||
dependencies = [
|
||||
"serde",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "bitflags"
|
||||
version = "2.9.0"
|
||||
@@ -228,6 +307,30 @@ dependencies = [
|
||||
"serde",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "blake3"
|
||||
version = "0.3.8"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b64485778c4f16a6a5a9d335e80d449ac6c70cdd6a06d2af18a6f6f775a125b3"
|
||||
dependencies = [
|
||||
"arrayref",
|
||||
"arrayvec",
|
||||
"cc",
|
||||
"cfg-if 0.1.10",
|
||||
"constant_time_eq",
|
||||
"crypto-mac 0.8.0",
|
||||
"digest 0.9.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "block-buffer"
|
||||
version = "0.9.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "4152116fd6e9dadb291ae18fc1ec3575ed6d84c29642d97890f4b4a3417297e4"
|
||||
dependencies = [
|
||||
"generic-array",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "block-buffer"
|
||||
version = "0.10.4"
|
||||
@@ -264,6 +367,12 @@ dependencies = [
|
||||
"shlex",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "cfg-if"
|
||||
version = "0.1.10"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "4785bdd1c96b2a846b2bd7cc02e86b6b3dbf14e7e53446c4f54c92a361040822"
|
||||
|
||||
[[package]]
|
||||
name = "cfg-if"
|
||||
version = "1.0.0"
|
||||
@@ -352,6 +461,12 @@ version = "0.9.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c2459377285ad874054d797f3ccebf984978aa39129f6eafde5cdc8315b612f8"
|
||||
|
||||
[[package]]
|
||||
name = "constant_time_eq"
|
||||
version = "0.1.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "245097e9a4535ee1e3e3931fcfcd55a796a44c643e8596ff6566d68f09b87bbc"
|
||||
|
||||
[[package]]
|
||||
name = "core-foundation-sys"
|
||||
version = "0.8.7"
|
||||
@@ -419,16 +534,36 @@ dependencies = [
|
||||
"typenum",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "crypto-mac"
|
||||
version = "0.8.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b584a330336237c1eecd3e94266efb216c56ed91225d634cb2991c5f3fd1aeab"
|
||||
dependencies = [
|
||||
"generic-array",
|
||||
"subtle",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "crypto-mac"
|
||||
version = "0.11.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "25fab6889090c8133f3deb8f73ba3c65a7f456f66436fc012a1b1e272b1e103e"
|
||||
dependencies = [
|
||||
"generic-array",
|
||||
"subtle",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "curve25519-dalek"
|
||||
version = "4.1.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "97fb8b7c4503de7d6ae7b42ab72a5a59857b4c937ec27a3d4539dba95b5ab2be"
|
||||
dependencies = [
|
||||
"cfg-if",
|
||||
"cfg-if 1.0.0",
|
||||
"cpufeatures",
|
||||
"curve25519-dalek-derive",
|
||||
"digest",
|
||||
"digest 0.10.7",
|
||||
"fiat-crypto",
|
||||
"rustc_version",
|
||||
"subtle",
|
||||
@@ -519,13 +654,22 @@ version = "0.2.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "3c877555693c14d2f84191cfd3ad8582790fc52b5e2274b40b59cf5f5cea25c7"
|
||||
|
||||
[[package]]
|
||||
name = "digest"
|
||||
version = "0.9.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d3dd60d1080a57a05ab032377049e0591415d2b31afd7028356dbf3cc6dcb066"
|
||||
dependencies = [
|
||||
"generic-array",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "digest"
|
||||
version = "0.10.7"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9ed9a281f7bc9b7576e61468ba615a66a5c8cfdff42420a70aa82701a3b1e292"
|
||||
dependencies = [
|
||||
"block-buffer",
|
||||
"block-buffer 0.10.4",
|
||||
"const-oid",
|
||||
"crypto-common",
|
||||
"subtle",
|
||||
@@ -561,7 +705,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ee27f32b5c5292967d2d4a9d7f1e0b0aed2c15daded5a60300e4abb9d8020bca"
|
||||
dependencies = [
|
||||
"der",
|
||||
"digest",
|
||||
"digest 0.10.7",
|
||||
"elliptic-curve",
|
||||
"rfc6979",
|
||||
"signature",
|
||||
@@ -587,7 +731,7 @@ dependencies = [
|
||||
"curve25519-dalek",
|
||||
"ed25519",
|
||||
"serde",
|
||||
"sha2",
|
||||
"sha2 0.10.8",
|
||||
"subtle",
|
||||
"zeroize",
|
||||
]
|
||||
@@ -609,7 +753,7 @@ checksum = "b5e6043086bf7973472e0c7dff2142ea0b680d30e18d9cc40f267efbf222bd47"
|
||||
dependencies = [
|
||||
"base16ct",
|
||||
"crypto-bigint",
|
||||
"digest",
|
||||
"digest 0.10.7",
|
||||
"ff",
|
||||
"generic-array",
|
||||
"group",
|
||||
@@ -644,11 +788,17 @@ version = "0.8.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "136d1b5283a1ab77bd9257427ffd09d8667ced0570b6f938942bc7568ed5b943"
|
||||
dependencies = [
|
||||
"cfg-if",
|
||||
"cfg-if 1.0.0",
|
||||
"home",
|
||||
"windows-sys 0.48.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "event-listener"
|
||||
version = "2.5.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "0206175f82b8d6bf6652ff7d71a1e27fd2e4efde587fd368662814d6ec1d9ce0"
|
||||
|
||||
[[package]]
|
||||
name = "event-listener"
|
||||
version = "5.4.0"
|
||||
@@ -660,12 +810,6 @@ dependencies = [
|
||||
"pin-project-lite",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "fallible-iterator"
|
||||
version = "0.2.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "4443176a9f2c162692bd3d352d745ef9413eec5782a80d8fd6f8a1ac692a07f7"
|
||||
|
||||
[[package]]
|
||||
name = "fastrand"
|
||||
version = "2.3.0"
|
||||
@@ -764,17 +908,6 @@ version = "0.3.31"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9e5c1b78ca4aae1ac06c48a526a655760685149f0d465d21f37abfe57ce075c6"
|
||||
|
||||
[[package]]
|
||||
name = "futures-macro"
|
||||
version = "0.3.31"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "162ee34ebcb7c64a8abebc059ce0fee27c2262618d7b60ed8faf72fef13c3650"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.100",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "futures-sink"
|
||||
version = "0.3.31"
|
||||
@@ -795,7 +928,6 @@ checksum = "9fa08315bb612088cc391249efdc3bc77536f16c91f6cf495e6fbe85b20a4a81"
|
||||
dependencies = [
|
||||
"futures-core",
|
||||
"futures-io",
|
||||
"futures-macro",
|
||||
"futures-sink",
|
||||
"futures-task",
|
||||
"memchr",
|
||||
@@ -821,7 +953,7 @@ version = "0.2.15"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c4567c8db10ae91089c99af84c68c38da3ec2f087c3f82960bcdbf3656b6f4d7"
|
||||
dependencies = [
|
||||
"cfg-if",
|
||||
"cfg-if 1.0.0",
|
||||
"js-sys",
|
||||
"libc",
|
||||
"wasi 0.11.0+wasi-snapshot-preview1",
|
||||
@@ -834,7 +966,7 @@ version = "0.3.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "73fea8450eea4bac3940448fb7ae50d91f034f941199fcd9d909a5a07aa455f0"
|
||||
dependencies = [
|
||||
"cfg-if",
|
||||
"cfg-if 1.0.0",
|
||||
"js-sys",
|
||||
"libc",
|
||||
"r-efi",
|
||||
@@ -885,6 +1017,30 @@ dependencies = [
|
||||
"hashbrown 0.15.2",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "headers"
|
||||
version = "0.4.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "322106e6bd0cba2d5ead589ddb8150a13d7c4217cf80d7c4f682ca994ccc6aa9"
|
||||
dependencies = [
|
||||
"base64 0.21.7",
|
||||
"bytes",
|
||||
"headers-core",
|
||||
"http",
|
||||
"httpdate",
|
||||
"mime",
|
||||
"sha1",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "headers-core"
|
||||
version = "0.3.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "54b4a22553d4242c49fddb9ba998a99962b5cc6f22cb5a3482bec22522403ce4"
|
||||
dependencies = [
|
||||
"http",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "heck"
|
||||
version = "0.5.0"
|
||||
@@ -903,7 +1059,17 @@ version = "0.12.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "7b5f8eb2ad728638ea2c7d47a21db23b7b58a72ed6a38256b8a1849f15fbbdf7"
|
||||
dependencies = [
|
||||
"hmac",
|
||||
"hmac 0.12.1",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "hmac"
|
||||
version = "0.11.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "2a2a2320eb7ec0ebe8da8f744d7812d9fc4cb4d09344ac01898dbcb6a20ae69b"
|
||||
dependencies = [
|
||||
"crypto-mac 0.11.0",
|
||||
"digest 0.9.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -912,7 +1078,7 @@ version = "0.12.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "6c49c37c09c17a53d937dfbb742eb3a961d65a994e6bcdcf37e7399d0cc8ab5e"
|
||||
dependencies = [
|
||||
"digest",
|
||||
"digest 0.10.7",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -1369,8 +1535,8 @@ version = "0.10.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d89e7ee0cfbedfc4da3340218492196241d89eefb6dab27de5df917a6d2e78cf"
|
||||
dependencies = [
|
||||
"cfg-if",
|
||||
"digest",
|
||||
"cfg-if 1.0.0",
|
||||
"digest 0.10.7",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -1473,7 +1639,7 @@ dependencies = [
|
||||
"serde",
|
||||
"serde_json",
|
||||
"serde_path_to_error",
|
||||
"sha2",
|
||||
"sha2 0.10.8",
|
||||
"thiserror 1.0.69",
|
||||
"url",
|
||||
]
|
||||
@@ -1493,6 +1659,12 @@ version = "1.21.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d75b0bedcc4fe52caa0e03d9f1151a323e4aa5e2d78ba3580400cd3c9e2bc4bc"
|
||||
|
||||
[[package]]
|
||||
name = "opaque-debug"
|
||||
version = "0.3.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c08d65885ee38876c4f86fa503fb49d7b507c2b62552df7c70b2fce627e06381"
|
||||
|
||||
[[package]]
|
||||
name = "openidconnect"
|
||||
version = "4.0.0"
|
||||
@@ -1503,7 +1675,7 @@ dependencies = [
|
||||
"chrono",
|
||||
"dyn-clone",
|
||||
"ed25519-dalek",
|
||||
"hmac",
|
||||
"hmac 0.12.1",
|
||||
"http",
|
||||
"itertools",
|
||||
"log",
|
||||
@@ -1518,7 +1690,7 @@ dependencies = [
|
||||
"serde_path_to_error",
|
||||
"serde_plain",
|
||||
"serde_with",
|
||||
"sha2",
|
||||
"sha2 0.10.8",
|
||||
"subtle",
|
||||
"thiserror 1.0.69",
|
||||
"url",
|
||||
@@ -1542,7 +1714,7 @@ dependencies = [
|
||||
"ecdsa",
|
||||
"elliptic-curve",
|
||||
"primeorder",
|
||||
"sha2",
|
||||
"sha2 0.10.8",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -1554,7 +1726,7 @@ dependencies = [
|
||||
"ecdsa",
|
||||
"elliptic-curve",
|
||||
"primeorder",
|
||||
"sha2",
|
||||
"sha2 0.10.8",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -1579,7 +1751,7 @@ version = "0.9.10"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1e401f977ab385c9e4e3ab30627d6f26d00e2c73eef317493c4ec6d468726cf8"
|
||||
dependencies = [
|
||||
"cfg-if",
|
||||
"cfg-if 1.0.0",
|
||||
"libc",
|
||||
"redox_syscall",
|
||||
"smallvec",
|
||||
@@ -1601,24 +1773,6 @@ version = "2.3.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e3148f5046208a5d56bcfc03053e3ca6334e51da8dfb19b6cdc8b306fae3283e"
|
||||
|
||||
[[package]]
|
||||
name = "phf"
|
||||
version = "0.11.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1fd6780a80ae0c52cc120a26a1a42c1ae51b247a253e4e06113d23d2c2edd078"
|
||||
dependencies = [
|
||||
"phf_shared",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "phf_shared"
|
||||
version = "0.11.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "67eabc2ef2a60eb7faa00097bd1ffdb5bd28e62bf39990626a582201b7a754e5"
|
||||
dependencies = [
|
||||
"siphasher",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "pin-project-lite"
|
||||
version = "0.2.16"
|
||||
@@ -1658,49 +1812,6 @@ version = "0.3.32"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "7edddbd0b52d732b21ad9a5fab5c704c14cd949e5e9a1ec5929a24fded1b904c"
|
||||
|
||||
[[package]]
|
||||
name = "postgres"
|
||||
version = "0.19.10"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "363e6dfbdd780d3aa3597b6eb430db76bb315fa9bad7fae595bb8def808b8470"
|
||||
dependencies = [
|
||||
"bytes",
|
||||
"fallible-iterator",
|
||||
"futures-util",
|
||||
"log",
|
||||
"tokio",
|
||||
"tokio-postgres",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "postgres-protocol"
|
||||
version = "0.6.8"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "76ff0abab4a9b844b93ef7b81f1efc0a366062aaef2cd702c76256b5dc075c54"
|
||||
dependencies = [
|
||||
"base64 0.22.1",
|
||||
"byteorder",
|
||||
"bytes",
|
||||
"fallible-iterator",
|
||||
"hmac",
|
||||
"md-5",
|
||||
"memchr",
|
||||
"rand 0.9.0",
|
||||
"sha2",
|
||||
"stringprep",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "postgres-types"
|
||||
version = "0.2.9"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "613283563cd90e1dfc3518d548caee47e0e725455ed619881f5cf21f36de4b48"
|
||||
dependencies = [
|
||||
"bytes",
|
||||
"fallible-iterator",
|
||||
"postgres-protocol",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "powerfmt"
|
||||
version = "0.2.0"
|
||||
@@ -1921,7 +2032,7 @@ version = "0.4.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f8dd2a808d456c4a54e300a23e9f5a67e122c3024119acbfd73e3bf664491cb2"
|
||||
dependencies = [
|
||||
"hmac",
|
||||
"hmac 0.12.1",
|
||||
"subtle",
|
||||
]
|
||||
|
||||
@@ -1932,7 +2043,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a4689e6c2294d81e88dc6261c768b63bc4fcdb852be6d1352498b114f61383b7"
|
||||
dependencies = [
|
||||
"cc",
|
||||
"cfg-if",
|
||||
"cfg-if 1.0.0",
|
||||
"getrandom 0.2.15",
|
||||
"libc",
|
||||
"untrusted",
|
||||
@@ -1946,7 +2057,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "78928ac1ed176a5ca1d17e578a1825f3d81ca54cf41053a592584b020cfd691b"
|
||||
dependencies = [
|
||||
"const-oid",
|
||||
"digest",
|
||||
"digest 0.10.7",
|
||||
"num-bigint-dig",
|
||||
"num-integer",
|
||||
"num-traits",
|
||||
@@ -2196,9 +2307,22 @@ version = "0.10.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e3bf829a2d51ab4a5ddf1352d8470c140cadc8301b2ae1789db023f01cedd6ba"
|
||||
dependencies = [
|
||||
"cfg-if",
|
||||
"cfg-if 1.0.0",
|
||||
"cpufeatures",
|
||||
"digest",
|
||||
"digest 0.10.7",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "sha2"
|
||||
version = "0.9.9"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "4d58a1e1bf39749807d89cf2d98ac2dfa0ff1cb3faa38fbb64dd88ac8013d800"
|
||||
dependencies = [
|
||||
"block-buffer 0.9.0",
|
||||
"cfg-if 1.0.0",
|
||||
"cpufeatures",
|
||||
"digest 0.9.0",
|
||||
"opaque-debug",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -2207,9 +2331,9 @@ version = "0.10.8"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "793db75ad2bcafc3ffa7c68b215fee268f537982cd901d132f89c6343f3a3dc8"
|
||||
dependencies = [
|
||||
"cfg-if",
|
||||
"cfg-if 1.0.0",
|
||||
"cpufeatures",
|
||||
"digest",
|
||||
"digest 0.10.7",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -2224,16 +2348,10 @@ version = "2.2.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "77549399552de45a898a580c1b41d445bf730df867cc44e6c0233bbc4b8329de"
|
||||
dependencies = [
|
||||
"digest",
|
||||
"digest 0.10.7",
|
||||
"rand_core 0.6.4",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "siphasher"
|
||||
version = "1.0.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "56199f7ddabf13fe5074ce809e7d3f42b42ae711800501b5b16ea82ad029c39d"
|
||||
|
||||
[[package]]
|
||||
name = "slab"
|
||||
version = "0.4.9"
|
||||
@@ -2304,7 +2422,7 @@ dependencies = [
|
||||
"crc",
|
||||
"crossbeam-queue",
|
||||
"either",
|
||||
"event-listener",
|
||||
"event-listener 5.4.0",
|
||||
"futures-core",
|
||||
"futures-intrusive",
|
||||
"futures-io",
|
||||
@@ -2318,9 +2436,10 @@ dependencies = [
|
||||
"percent-encoding",
|
||||
"serde",
|
||||
"serde_json",
|
||||
"sha2",
|
||||
"sha2 0.10.8",
|
||||
"smallvec",
|
||||
"thiserror 2.0.12",
|
||||
"time",
|
||||
"tokio",
|
||||
"tokio-stream",
|
||||
"tracing",
|
||||
@@ -2355,7 +2474,7 @@ dependencies = [
|
||||
"quote",
|
||||
"serde",
|
||||
"serde_json",
|
||||
"sha2",
|
||||
"sha2 0.10.8",
|
||||
"sqlx-core",
|
||||
"sqlx-mysql",
|
||||
"sqlx-postgres",
|
||||
@@ -2378,7 +2497,7 @@ dependencies = [
|
||||
"byteorder",
|
||||
"bytes",
|
||||
"crc",
|
||||
"digest",
|
||||
"digest 0.10.7",
|
||||
"dotenvy",
|
||||
"either",
|
||||
"futures-channel",
|
||||
@@ -2388,7 +2507,7 @@ dependencies = [
|
||||
"generic-array",
|
||||
"hex",
|
||||
"hkdf",
|
||||
"hmac",
|
||||
"hmac 0.12.1",
|
||||
"itoa",
|
||||
"log",
|
||||
"md-5",
|
||||
@@ -2399,11 +2518,12 @@ dependencies = [
|
||||
"rsa",
|
||||
"serde",
|
||||
"sha1",
|
||||
"sha2",
|
||||
"sha2 0.10.8",
|
||||
"smallvec",
|
||||
"sqlx-core",
|
||||
"stringprep",
|
||||
"thiserror 2.0.12",
|
||||
"time",
|
||||
"tracing",
|
||||
"whoami",
|
||||
]
|
||||
@@ -2426,7 +2546,7 @@ dependencies = [
|
||||
"futures-util",
|
||||
"hex",
|
||||
"hkdf",
|
||||
"hmac",
|
||||
"hmac 0.12.1",
|
||||
"home",
|
||||
"itoa",
|
||||
"log",
|
||||
@@ -2436,11 +2556,12 @@ dependencies = [
|
||||
"rand 0.8.5",
|
||||
"serde",
|
||||
"serde_json",
|
||||
"sha2",
|
||||
"sha2 0.10.8",
|
||||
"smallvec",
|
||||
"sqlx-core",
|
||||
"stringprep",
|
||||
"thiserror 2.0.12",
|
||||
"time",
|
||||
"tracing",
|
||||
"whoami",
|
||||
]
|
||||
@@ -2464,6 +2585,7 @@ dependencies = [
|
||||
"serde",
|
||||
"serde_urlencoded",
|
||||
"sqlx-core",
|
||||
"time",
|
||||
"tracing",
|
||||
"url",
|
||||
]
|
||||
@@ -2685,32 +2807,6 @@ dependencies = [
|
||||
"syn 2.0.100",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "tokio-postgres"
|
||||
version = "0.7.13"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "6c95d533c83082bb6490e0189acaa0bbeef9084e60471b696ca6988cd0541fb0"
|
||||
dependencies = [
|
||||
"async-trait",
|
||||
"byteorder",
|
||||
"bytes",
|
||||
"fallible-iterator",
|
||||
"futures-channel",
|
||||
"futures-util",
|
||||
"log",
|
||||
"parking_lot",
|
||||
"percent-encoding",
|
||||
"phf",
|
||||
"pin-project-lite",
|
||||
"postgres-protocol",
|
||||
"postgres-types",
|
||||
"rand 0.9.0",
|
||||
"socket2",
|
||||
"tokio",
|
||||
"tokio-util",
|
||||
"whoami",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "tokio-rustls"
|
||||
version = "0.26.2"
|
||||
@@ -2732,19 +2828,6 @@ dependencies = [
|
||||
"tokio",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "tokio-util"
|
||||
version = "0.7.14"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "6b9590b93e6fcc1739458317cccd391ad3955e2bde8913edf6f95f9e65a8f034"
|
||||
dependencies = [
|
||||
"bytes",
|
||||
"futures-core",
|
||||
"futures-sink",
|
||||
"pin-project-lite",
|
||||
"tokio",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "tower"
|
||||
version = "0.5.2"
|
||||
@@ -2914,14 +2997,14 @@ checksum = "accd4ea62f7bb7a82fe23066fb0957d48ef677f6eeb8215f372f52e48bb32426"
|
||||
name = "veil"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"async-session",
|
||||
"axum",
|
||||
"axum-extra",
|
||||
"clap",
|
||||
"log",
|
||||
"log4rs",
|
||||
"openidconnect",
|
||||
"postgres",
|
||||
"serde",
|
||||
"serde_derive",
|
||||
"serde_yaml",
|
||||
"sqlx",
|
||||
"tokio",
|
||||
@@ -2969,7 +3052,7 @@ version = "0.2.100"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1edc8929d7499fc4e8f0be2262a241556cfc54a0bea223790e71446f2aab1ef5"
|
||||
dependencies = [
|
||||
"cfg-if",
|
||||
"cfg-if 1.0.0",
|
||||
"once_cell",
|
||||
"rustversion",
|
||||
"wasm-bindgen-macro",
|
||||
@@ -2995,7 +3078,7 @@ version = "0.4.50"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "555d470ec0bc3bb57890405e5d4322cc9ea83cebb085523ced7be4144dac1e61"
|
||||
dependencies = [
|
||||
"cfg-if",
|
||||
"cfg-if 1.0.0",
|
||||
"js-sys",
|
||||
"once_cell",
|
||||
"wasm-bindgen",
|
||||
@@ -3071,7 +3154,6 @@ checksum = "6994d13118ab492c3c80c1f81928718159254c53c472bf9ce36f8dae4add02a7"
|
||||
dependencies = [
|
||||
"redox_syscall",
|
||||
"wasite",
|
||||
"web-sys",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
@@ -14,14 +14,14 @@ lto = true
|
||||
codegen-units = 1
|
||||
|
||||
[dependencies]
|
||||
async-session = "3.0.0"
|
||||
axum = "0.8.1"
|
||||
axum-extra = { version = "0.10.0", features = ["typed-header"] }
|
||||
clap = { version = "4.5.32", features = ["derive"] }
|
||||
log = "0.4.27"
|
||||
log4rs = "1.3.0"
|
||||
openidconnect = "4.0.0"
|
||||
postgres = "0.19.10"
|
||||
openidconnect = { version = "4.0.0", features = ["reqwest"] }
|
||||
serde = "1.0.219"
|
||||
serde_derive = "1.0.219"
|
||||
serde_yaml = "0.9.34"
|
||||
sqlx = { version = "0.8.3", features = ["postgres", "runtime-tokio"] }
|
||||
sqlx = { version = "0.8.3", features = ["postgres", "runtime-tokio", "time"] }
|
||||
tokio = { version = "1.44.1", features = ["rt-multi-thread"] }
|
||||
|
@@ -11,6 +11,8 @@ RUN cargo fetch
|
||||
RUN rm -rf src
|
||||
|
||||
COPY src ./src
|
||||
COPY migrations ./migrations
|
||||
COPY .sqlx ./.sqlx
|
||||
|
||||
RUN cargo build --target=x86_64-unknown-linux-musl --release
|
||||
|
||||
|
@@ -70,6 +70,9 @@ metadata:
|
||||
name: veil-config
|
||||
data:
|
||||
default.yml: |
|
||||
server:
|
||||
host: https://app.veil.local
|
||||
|
||||
database:
|
||||
host: postgresql
|
||||
port: 5432
|
||||
@@ -77,10 +80,11 @@ data:
|
||||
password: veil
|
||||
database: veil
|
||||
|
||||
oidc:
|
||||
oauth:
|
||||
issuer_url: "https://id.veil.local"
|
||||
client_id: "veil"
|
||||
client_secret: "insecure_secret"
|
||||
insecure: true
|
||||
log4rs.yml: |
|
||||
appenders:
|
||||
stdout:
|
||||
@@ -167,7 +171,7 @@ data:
|
||||
- client_id: "veil"
|
||||
client_secret: "$pbkdf2-sha512$310000$c8p78n7pUMln0jzvd4aK4Q$JNRBzwAo0ek5qKn50cFzzvE9RXV88h1wJn5KGiHrD0YKtZaR/nCb2CJPOsKaPK0hjf.9yHxzQGZziziccp6Yng" # The digest of 'insecure_secret'.
|
||||
redirect_uris:
|
||||
- "https://app.veil.local/oauth2/callback"
|
||||
- "https://app.veil.local/api/auth/callback"
|
||||
authorization_policy: "one_factor"
|
||||
users.yml: |
|
||||
users:
|
||||
|
0
migrations/20250327112746_init.sql
Normal file
0
migrations/20250327112746_init.sql
Normal file
@@ -1,3 +1,4 @@
|
||||
use clap::Parser;
|
||||
use serde::Deserialize;
|
||||
use std::{
|
||||
fs,
|
||||
@@ -5,8 +6,9 @@ use std::{
|
||||
path::PathBuf,
|
||||
};
|
||||
|
||||
#[derive(Deserialize)]
|
||||
#[derive(Clone, Deserialize)]
|
||||
pub struct ServerConfig {
|
||||
pub host: String,
|
||||
#[serde(default = "default_address")]
|
||||
pub address: std::net::IpAddr,
|
||||
#[serde(default = "default_port")]
|
||||
@@ -23,17 +25,7 @@ const fn default_port() -> u16 {
|
||||
51821
|
||||
}
|
||||
|
||||
impl Default for ServerConfig {
|
||||
fn default() -> Self {
|
||||
Self {
|
||||
address: default_address(),
|
||||
port: default_port(),
|
||||
subpath: String::default(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Deserialize)]
|
||||
#[derive(Clone, Deserialize)]
|
||||
pub struct DatabaseConfig {
|
||||
pub user: String,
|
||||
pub password: String,
|
||||
@@ -43,18 +35,19 @@ pub struct DatabaseConfig {
|
||||
}
|
||||
|
||||
#[derive(Debug, Deserialize, Clone)]
|
||||
pub struct OidcConfig {
|
||||
pub struct OAuthConfig {
|
||||
pub issuer_url: String,
|
||||
pub client_id: String,
|
||||
pub client_secret: String,
|
||||
#[serde(default)]
|
||||
pub insecure: bool,
|
||||
}
|
||||
|
||||
#[derive(Deserialize)]
|
||||
#[derive(Clone, Deserialize)]
|
||||
pub struct Config {
|
||||
#[serde(default)]
|
||||
pub server: ServerConfig,
|
||||
pub database: DatabaseConfig,
|
||||
pub oidc: OidcConfig,
|
||||
pub oauth: OAuthConfig,
|
||||
}
|
||||
|
||||
impl Config {
|
||||
@@ -64,3 +57,14 @@ impl Config {
|
||||
Ok(config)
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Parser, Debug)]
|
||||
#[command(version, about, long_about = None, author)]
|
||||
pub struct Args {
|
||||
/// Path to the YAML config file
|
||||
#[arg(short, long, value_name = "FILE", default_value = "config.yaml")]
|
||||
pub config: PathBuf,
|
||||
/// Path to the log4rs config file
|
||||
#[arg(short, long, value_name = "FILE", default_value = "log4rs.yaml")]
|
||||
pub log_config: PathBuf,
|
||||
}
|
||||
|
44
src/main.rs
44
src/main.rs
@@ -2,56 +2,38 @@
|
||||
#![allow(clippy::missing_docs_in_private_items)]
|
||||
|
||||
mod config;
|
||||
mod models;
|
||||
mod routes;
|
||||
mod state;
|
||||
|
||||
use axum::{Extension, serve};
|
||||
use axum::serve;
|
||||
use clap::Parser;
|
||||
use log::info;
|
||||
use log4rs::config::Deserializers;
|
||||
use sqlx::postgres::PgPoolOptions;
|
||||
use std::{net::SocketAddr, path::PathBuf};
|
||||
use std::net::SocketAddr;
|
||||
use tokio::net::TcpListener;
|
||||
|
||||
use config::Config;
|
||||
|
||||
#[derive(Parser, Debug)]
|
||||
#[command(version, about, long_about = None, author)]
|
||||
struct Args {
|
||||
/// Path to the YAML config file
|
||||
#[arg(short, long, value_name = "FILE", default_value = "config.yaml")]
|
||||
config: PathBuf,
|
||||
/// Path to the log4rs config file
|
||||
#[arg(short, long, value_name = "FILE", default_value = "log4rs.yaml")]
|
||||
log_config: PathBuf,
|
||||
}
|
||||
use config::{Args, Config};
|
||||
use state::State;
|
||||
|
||||
#[tokio::main]
|
||||
async fn main() {
|
||||
let args = Args::parse();
|
||||
log4rs::init_file(args.log_config, Deserializers::default()).unwrap();
|
||||
let config = Config::from_yaml(&args.config).unwrap();
|
||||
let state = State::from_config(config.clone()).await.unwrap();
|
||||
|
||||
let pgpool = PgPoolOptions::new()
|
||||
.max_connections(5)
|
||||
.connect(&format!(
|
||||
"postgres://{}:{}@{}:{}/{}",
|
||||
config.database.user,
|
||||
config.database.password,
|
||||
config.database.host,
|
||||
config.database.port,
|
||||
config.database.database
|
||||
))
|
||||
sqlx::migrate!("./migrations")
|
||||
.run(&state.pg_pool)
|
||||
.await
|
||||
.unwrap();
|
||||
.expect("Failed to run migrations");
|
||||
|
||||
let routes = routes::routes();
|
||||
let app = axum::Router::new()
|
||||
.nest(&format!("{}/api", config.server.subpath), routes)
|
||||
.layer(Extension(pgpool));
|
||||
let routes = routes::routes(state);
|
||||
let app = axum::Router::new().nest(&format!("{}/api", config.server.subpath), routes);
|
||||
|
||||
let addr = SocketAddr::from((config.server.address, config.server.port));
|
||||
let listener = TcpListener::bind(addr).await.unwrap();
|
||||
|
||||
info!("Listening on {addr}.");
|
||||
info!("Listening on {}", listener.local_addr().unwrap());
|
||||
serve(listener, app).await.unwrap();
|
||||
}
|
||||
|
0
src/models/mod.rs
Normal file
0
src/models/mod.rs
Normal file
317
src/routes/auth.rs
Normal file
317
src/routes/auth.rs
Normal file
@@ -0,0 +1,317 @@
|
||||
use std::{borrow::Cow, convert::Infallible};
|
||||
|
||||
use async_session::{MemoryStore, Session, SessionStore};
|
||||
use axum::{
|
||||
RequestPartsExt, Router,
|
||||
extract::{self, FromRef, FromRequestParts, OptionalFromRequestParts},
|
||||
http::{HeaderMap, StatusCode, header, request::Parts},
|
||||
response::{IntoResponse, Redirect, Response},
|
||||
routing,
|
||||
};
|
||||
use axum_extra::{TypedHeader, headers::Cookie, typed_header::TypedHeaderRejectionReason};
|
||||
use log::error;
|
||||
use openidconnect::{
|
||||
AccessTokenHash, AuthorizationCode, CsrfToken, EndUserEmail, EndUserUsername, Nonce,
|
||||
OAuth2TokenResponse, PkceCodeChallenge, PkceCodeVerifier, Scope, SubjectIdentifier,
|
||||
TokenResponse, core::CoreAuthenticationFlow, reqwest,
|
||||
};
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
use crate::state::{OAuthClient, State};
|
||||
|
||||
static COOKIE_NAME: &str = "veil_session";
|
||||
|
||||
#[derive(Clone, Deserialize, Serialize)]
|
||||
pub struct User {
|
||||
pub subject: SubjectIdentifier,
|
||||
pub username: EndUserUsername,
|
||||
pub email: Option<EndUserEmail>,
|
||||
}
|
||||
|
||||
async fn login(
|
||||
extract::State(oauth_client): extract::State<OAuthClient>,
|
||||
extract::State(session_store): extract::State<MemoryStore>,
|
||||
) -> Result<impl IntoResponse, StatusCode> {
|
||||
let (pkce_challenge, pkce_verifier) = PkceCodeChallenge::new_random_sha256();
|
||||
|
||||
let (auth_url, csrf_token, nonce) = oauth_client
|
||||
.authorize_url(
|
||||
CoreAuthenticationFlow::AuthorizationCode,
|
||||
CsrfToken::new_random,
|
||||
Nonce::new_random,
|
||||
)
|
||||
.set_pkce_challenge(pkce_challenge)
|
||||
.set_redirect_uri(Cow::Borrowed(oauth_client.redirect_uri().ok_or_else(
|
||||
|| {
|
||||
error!("missing redirect URI");
|
||||
StatusCode::INTERNAL_SERVER_ERROR
|
||||
},
|
||||
)?))
|
||||
.add_scope(Scope::new("profile".to_string()))
|
||||
.add_scope(Scope::new("email".to_string()))
|
||||
.add_scope(Scope::new("groups".to_string()))
|
||||
.url();
|
||||
|
||||
let mut session = Session::new();
|
||||
session
|
||||
.insert("pkce_verifier", pkce_verifier)
|
||||
.map_err(|e| {
|
||||
error!("failed to insert pkce_verifier into session: {e}");
|
||||
StatusCode::INTERNAL_SERVER_ERROR
|
||||
})?;
|
||||
session.insert("csrf_token", csrf_token).map_err(|e| {
|
||||
error!("failed to insert csrf_token into session: {e}");
|
||||
StatusCode::INTERNAL_SERVER_ERROR
|
||||
})?;
|
||||
session.insert("nonce", nonce).map_err(|e| {
|
||||
error!("failed to insert nonce into session: {e}");
|
||||
StatusCode::INTERNAL_SERVER_ERROR
|
||||
})?;
|
||||
|
||||
let cookie = session_store
|
||||
.store_session(session)
|
||||
.await
|
||||
.map_err(|e| {
|
||||
error!("failed to store session: {e}");
|
||||
StatusCode::INTERNAL_SERVER_ERROR
|
||||
})?
|
||||
.ok_or_else(|| {
|
||||
error!("failed to retrieve stored session cookie");
|
||||
StatusCode::INTERNAL_SERVER_ERROR
|
||||
})?;
|
||||
|
||||
let cookie =
|
||||
format!("{COOKIE_NAME}={cookie}; HttpOnly; SameSite=Lax; HttpOnly; Secure; Path=/");
|
||||
|
||||
let mut headers = HeaderMap::new();
|
||||
headers.insert(
|
||||
header::SET_COOKIE,
|
||||
cookie.parse().map_err(|e| {
|
||||
error!("failed to parse cookie: {e}");
|
||||
StatusCode::INTERNAL_SERVER_ERROR
|
||||
})?,
|
||||
);
|
||||
|
||||
Ok((headers, Redirect::to(auth_url.as_str())))
|
||||
}
|
||||
|
||||
#[derive(Debug, Deserialize)]
|
||||
#[allow(dead_code)]
|
||||
struct CallbackParams {
|
||||
code: String,
|
||||
state: String,
|
||||
}
|
||||
|
||||
async fn callback(
|
||||
extract::Query(params): extract::Query<CallbackParams>,
|
||||
extract::State(http_client): extract::State<reqwest::Client>,
|
||||
extract::State(oauth_client): extract::State<OAuthClient>,
|
||||
extract::State(session_store): extract::State<MemoryStore>,
|
||||
TypedHeader(cookies): TypedHeader<Cookie>,
|
||||
) -> Result<impl IntoResponse, StatusCode> {
|
||||
let cookie = cookies
|
||||
.get(COOKIE_NAME)
|
||||
.ok_or(StatusCode::UNAUTHORIZED)?
|
||||
.to_string();
|
||||
|
||||
let session = session_store
|
||||
.load_session(cookie)
|
||||
.await
|
||||
.map_err(|e| {
|
||||
error!("failed to load session: {e}");
|
||||
StatusCode::INTERNAL_SERVER_ERROR
|
||||
})?
|
||||
.ok_or(StatusCode::UNAUTHORIZED)?;
|
||||
|
||||
let csrf_token = session
|
||||
.get::<CsrfToken>("csrf_token")
|
||||
.ok_or_else(|| {
|
||||
error!("failed to get csrf_token from session");
|
||||
StatusCode::INTERNAL_SERVER_ERROR
|
||||
})?
|
||||
.clone();
|
||||
|
||||
let pkce_verifier = session
|
||||
.get::<PkceCodeVerifier>("pkce_verifier")
|
||||
.ok_or_else(|| {
|
||||
error!("failed to get pkce_verifier from session");
|
||||
StatusCode::INTERNAL_SERVER_ERROR
|
||||
})?;
|
||||
|
||||
let nonce = session
|
||||
.get::<Nonce>("nonce")
|
||||
.ok_or_else(|| {
|
||||
error!("failed to get nonce from session");
|
||||
StatusCode::INTERNAL_SERVER_ERROR
|
||||
})?
|
||||
.clone();
|
||||
|
||||
session_store.destroy_session(session).await.map_err(|e| {
|
||||
error!("failed to destroy session: {e}");
|
||||
StatusCode::INTERNAL_SERVER_ERROR
|
||||
})?;
|
||||
|
||||
if *csrf_token.secret() != params.state {
|
||||
error!("csrf_token mismatch");
|
||||
return Err(StatusCode::INTERNAL_SERVER_ERROR);
|
||||
}
|
||||
|
||||
let token_response = oauth_client
|
||||
.exchange_code(AuthorizationCode::new(params.code))
|
||||
.map_err(|e| {
|
||||
error!("failed to exchange code: {e}");
|
||||
StatusCode::INTERNAL_SERVER_ERROR
|
||||
})?
|
||||
.set_pkce_verifier(pkce_verifier)
|
||||
.request_async(&http_client)
|
||||
.await
|
||||
.map_err(|e| {
|
||||
error!("failed to request token: {e}");
|
||||
StatusCode::INTERNAL_SERVER_ERROR
|
||||
})?;
|
||||
|
||||
let id_token = token_response.id_token().ok_or_else(|| {
|
||||
error!("missing id_token in token response");
|
||||
StatusCode::INTERNAL_SERVER_ERROR
|
||||
})?;
|
||||
|
||||
let id_token_verifier = oauth_client.id_token_verifier();
|
||||
let claims = id_token.claims(&id_token_verifier, &nonce).map_err(|e| {
|
||||
error!("failed to verify id_token: {e}");
|
||||
StatusCode::INTERNAL_SERVER_ERROR
|
||||
})?;
|
||||
|
||||
if let Some(expected_access_token_hash) = claims.access_token_hash() {
|
||||
let actual_access_token_hash = AccessTokenHash::from_token(
|
||||
token_response.access_token(),
|
||||
id_token.signing_alg().map_err(|e| {
|
||||
error!("failed to get signing algorithm: {e}");
|
||||
StatusCode::INTERNAL_SERVER_ERROR
|
||||
})?,
|
||||
id_token.signing_key(&id_token_verifier).map_err(|e| {
|
||||
error!("failed to get signing key: {e}");
|
||||
StatusCode::INTERNAL_SERVER_ERROR
|
||||
})?,
|
||||
)
|
||||
.map_err(|e| {
|
||||
error!("failed to compute access token hash: {e}");
|
||||
StatusCode::INTERNAL_SERVER_ERROR
|
||||
})?;
|
||||
|
||||
if actual_access_token_hash != *expected_access_token_hash {
|
||||
error!("access token hash mismatch");
|
||||
return Err(StatusCode::INTERNAL_SERVER_ERROR);
|
||||
}
|
||||
}
|
||||
|
||||
let user = User {
|
||||
subject: claims.subject().to_owned(),
|
||||
username: claims.preferred_username().cloned().ok_or_else(|| {
|
||||
error!("missing preferred_username in claims");
|
||||
StatusCode::INTERNAL_SERVER_ERROR
|
||||
})?,
|
||||
email: claims.email().cloned(),
|
||||
};
|
||||
|
||||
let mut session = Session::new();
|
||||
session.insert("user", user).map_err(|e| {
|
||||
error!("failed to insert user into session: {e}");
|
||||
StatusCode::INTERNAL_SERVER_ERROR
|
||||
})?;
|
||||
|
||||
Ok(StatusCode::OK)
|
||||
}
|
||||
|
||||
async fn logout(
|
||||
extract::State(session_store): extract::State<MemoryStore>,
|
||||
TypedHeader(cookies): TypedHeader<Cookie>,
|
||||
) -> Result<impl IntoResponse, StatusCode> {
|
||||
let cookie = cookies.get(COOKIE_NAME).ok_or(StatusCode::UNAUTHORIZED)?;
|
||||
|
||||
let Some(session) = session_store
|
||||
.load_session(cookie.to_string())
|
||||
.await
|
||||
.map_err(|e| {
|
||||
error!("failed to load session: {e}");
|
||||
StatusCode::INTERNAL_SERVER_ERROR
|
||||
})?
|
||||
else {
|
||||
return Ok(StatusCode::OK);
|
||||
};
|
||||
|
||||
session_store.destroy_session(session).await.map_err(|e| {
|
||||
error!("failed to destroy session: {e}");
|
||||
StatusCode::INTERNAL_SERVER_ERROR
|
||||
})?;
|
||||
|
||||
Ok(StatusCode::OK)
|
||||
}
|
||||
|
||||
pub fn routes(state: State) -> Router {
|
||||
Router::new()
|
||||
.route("/auth/login", routing::get(login))
|
||||
.route("/auth/callback", routing::get(callback))
|
||||
.route("/auth/logout", routing::get(logout))
|
||||
.with_state(state)
|
||||
}
|
||||
|
||||
impl<S> FromRequestParts<S> for User
|
||||
where
|
||||
MemoryStore: FromRef<S>,
|
||||
S: Send + Sync,
|
||||
{
|
||||
type Rejection = Response;
|
||||
|
||||
async fn from_request_parts(parts: &mut Parts, state: &S) -> Result<Self, Self::Rejection> {
|
||||
let store = MemoryStore::from_ref(state);
|
||||
|
||||
let cookies = parts.extract::<TypedHeader<Cookie>>().await.map_err(|e| {
|
||||
if *e.name() == header::COOKIE {
|
||||
if matches!(e.reason(), TypedHeaderRejectionReason::Missing) {
|
||||
StatusCode::UNAUTHORIZED.into_response()
|
||||
} else {
|
||||
error!("failed to extract cookies: {e}");
|
||||
StatusCode::INTERNAL_SERVER_ERROR.into_response()
|
||||
}
|
||||
} else {
|
||||
error!("failed to extract cookies: {e}");
|
||||
StatusCode::INTERNAL_SERVER_ERROR.into_response()
|
||||
}
|
||||
})?;
|
||||
|
||||
let session_cookie = cookies
|
||||
.get(COOKIE_NAME)
|
||||
.ok_or_else(|| StatusCode::UNAUTHORIZED.into_response())?;
|
||||
|
||||
let session = store
|
||||
.load_session(session_cookie.to_string())
|
||||
.await
|
||||
.map_err(|e| {
|
||||
error!("failed to load session: {e}");
|
||||
StatusCode::INTERNAL_SERVER_ERROR.into_response()
|
||||
})?
|
||||
.ok_or_else(|| StatusCode::UNAUTHORIZED.into_response())?;
|
||||
|
||||
let user = session
|
||||
.get::<Self>("user")
|
||||
.ok_or_else(|| StatusCode::UNAUTHORIZED.into_response())?;
|
||||
|
||||
Ok(user)
|
||||
}
|
||||
}
|
||||
|
||||
impl<S> OptionalFromRequestParts<S> for User
|
||||
where
|
||||
MemoryStore: FromRef<S>,
|
||||
S: Send + Sync,
|
||||
{
|
||||
type Rejection = Infallible;
|
||||
|
||||
async fn from_request_parts(
|
||||
parts: &mut Parts,
|
||||
state: &S,
|
||||
) -> Result<Option<Self>, Self::Rejection> {
|
||||
(<Self as FromRequestParts<S>>::from_request_parts(parts, state).await)
|
||||
.map_or(Ok(None), |user| Ok(Some(user)))
|
||||
}
|
||||
}
|
@@ -1,9 +1,13 @@
|
||||
use axum::{Router, http::StatusCode, routing};
|
||||
use axum::{Router, http::StatusCode, response::IntoResponse, routing};
|
||||
|
||||
pub async fn get() -> Result<StatusCode, StatusCode> {
|
||||
use crate::state::State;
|
||||
|
||||
pub async fn get() -> Result<impl IntoResponse, StatusCode> {
|
||||
Ok(StatusCode::OK)
|
||||
}
|
||||
|
||||
pub fn routes() -> Router {
|
||||
Router::new().route("/", routing::get(get))
|
||||
pub fn routes(state: State) -> Router {
|
||||
Router::new()
|
||||
.route("/health", routing::get(get))
|
||||
.with_state(state)
|
||||
}
|
||||
|
@@ -1,9 +1,13 @@
|
||||
mod auth;
|
||||
mod health;
|
||||
|
||||
use axum::Router;
|
||||
|
||||
pub fn routes() -> Router {
|
||||
let health = health::routes();
|
||||
use crate::state::State;
|
||||
|
||||
Router::new().merge(health)
|
||||
pub fn routes(state: State) -> Router {
|
||||
let auth = auth::routes(state.clone());
|
||||
let health = health::routes(state);
|
||||
|
||||
Router::new().merge(auth).merge(health)
|
||||
}
|
||||
|
139
src/state.rs
Normal file
139
src/state.rs
Normal file
@@ -0,0 +1,139 @@
|
||||
use async_session::MemoryStore;
|
||||
use axum::extract::FromRef;
|
||||
use log::error;
|
||||
use openidconnect::{
|
||||
ClientId, ClientSecret, EmptyAdditionalClaims, EndpointMaybeSet, EndpointNotSet, EndpointSet,
|
||||
IssuerUrl, RedirectUrl, StandardErrorResponse,
|
||||
core::{
|
||||
CoreAuthDisplay, CoreAuthPrompt, CoreErrorResponseType, CoreGenderClaim, CoreJsonWebKey,
|
||||
CoreJweContentEncryptionAlgorithm, CoreProviderMetadata, CoreRevocableToken,
|
||||
CoreRevocationErrorResponse, CoreTokenIntrospectionResponse, CoreTokenResponse,
|
||||
},
|
||||
reqwest,
|
||||
};
|
||||
use tokio::{
|
||||
spawn,
|
||||
time::{Duration, sleep},
|
||||
};
|
||||
|
||||
use crate::config::Config;
|
||||
|
||||
pub type OAuthClient<
|
||||
HasAuthUrl = EndpointSet,
|
||||
HasDeviceAuthUrl = EndpointNotSet,
|
||||
HasIntrospectionUrl = EndpointNotSet,
|
||||
HasRevocationUrl = EndpointNotSet,
|
||||
HasTokenUrl = EndpointMaybeSet,
|
||||
HasUserInfoUrl = EndpointMaybeSet,
|
||||
> = openidconnect::Client<
|
||||
EmptyAdditionalClaims,
|
||||
CoreAuthDisplay,
|
||||
CoreGenderClaim,
|
||||
CoreJweContentEncryptionAlgorithm,
|
||||
CoreJsonWebKey,
|
||||
CoreAuthPrompt,
|
||||
StandardErrorResponse<CoreErrorResponseType>,
|
||||
CoreTokenResponse,
|
||||
CoreTokenIntrospectionResponse,
|
||||
CoreRevocableToken,
|
||||
CoreRevocationErrorResponse,
|
||||
HasAuthUrl,
|
||||
HasDeviceAuthUrl,
|
||||
HasIntrospectionUrl,
|
||||
HasRevocationUrl,
|
||||
HasTokenUrl,
|
||||
HasUserInfoUrl,
|
||||
>;
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct State {
|
||||
pub pg_pool: sqlx::PgPool,
|
||||
pub oauth_http_client: reqwest::Client,
|
||||
pub oauth_client: OAuthClient,
|
||||
pub session_store: async_session::MemoryStore,
|
||||
}
|
||||
|
||||
impl State {
|
||||
pub async fn from_config(config: Config) -> Result<Self, Box<dyn std::error::Error>> {
|
||||
let pg_pool = sqlx::postgres::PgPoolOptions::new()
|
||||
.max_connections(5)
|
||||
.connect(&format!(
|
||||
"postgres://{}:{}@{}:{}/{}",
|
||||
config.database.user,
|
||||
config.database.password,
|
||||
config.database.host,
|
||||
config.database.port,
|
||||
config.database.database
|
||||
))
|
||||
.await?;
|
||||
|
||||
let mut http_client =
|
||||
reqwest::ClientBuilder::new().redirect(reqwest::redirect::Policy::none());
|
||||
|
||||
if config.oauth.insecure {
|
||||
http_client = http_client.danger_accept_invalid_certs(true);
|
||||
}
|
||||
|
||||
let http_client = http_client.build()?;
|
||||
|
||||
let provider_metadata = CoreProviderMetadata::discover_async(
|
||||
IssuerUrl::new(config.oauth.issuer_url)?,
|
||||
&http_client,
|
||||
)
|
||||
.await?;
|
||||
|
||||
let oauth_client = openidconnect::core::CoreClient::from_provider_metadata(
|
||||
provider_metadata,
|
||||
ClientId::new(config.oauth.client_id),
|
||||
Some(ClientSecret::new(config.oauth.client_secret)),
|
||||
)
|
||||
.set_redirect_uri(RedirectUrl::new(format!(
|
||||
"{}{}/api/auth/callback",
|
||||
config.server.host, config.server.subpath
|
||||
))?);
|
||||
|
||||
let session_store = MemoryStore::new();
|
||||
|
||||
let session_store_clone = session_store.clone();
|
||||
spawn(async move {
|
||||
loop {
|
||||
match session_store_clone.cleanup().await {
|
||||
Ok(()) => {}
|
||||
Err(e) => error!("Failed to clean up session store: {e}"),
|
||||
}
|
||||
sleep(Duration::from_secs(60)).await;
|
||||
}
|
||||
});
|
||||
|
||||
Ok(Self {
|
||||
pg_pool,
|
||||
oauth_http_client: http_client,
|
||||
oauth_client,
|
||||
session_store,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
impl FromRef<State> for sqlx::PgPool {
|
||||
fn from_ref(state: &State) -> Self {
|
||||
state.pg_pool.clone()
|
||||
}
|
||||
}
|
||||
|
||||
impl FromRef<State> for reqwest::Client {
|
||||
fn from_ref(state: &State) -> Self {
|
||||
state.oauth_http_client.clone()
|
||||
}
|
||||
}
|
||||
|
||||
impl FromRef<State> for OAuthClient {
|
||||
fn from_ref(state: &State) -> Self {
|
||||
state.oauth_client.clone()
|
||||
}
|
||||
}
|
||||
|
||||
impl FromRef<State> for async_session::MemoryStore {
|
||||
fn from_ref(state: &State) -> Self {
|
||||
state.session_store.clone()
|
||||
}
|
||||
}
|
Reference in New Issue
Block a user