Start on OIDC integration

This is absurdly insecure and jank to start but I just want a POC before I polish it
This commit is contained in:
Tyler Beckman 2023-11-13 23:19:55 -07:00
parent db70e8c52a
commit 657e5e6088
Signed by: Ty
GPG key ID: 2813440C772555A4
8 changed files with 638 additions and 5 deletions

2
.gitignore vendored
View file

@ -4,3 +4,5 @@
.idea/ .idea/
.vscode/ .vscode/
result result
/config
/database

381
Cargo.lock generated
View file

@ -55,6 +55,21 @@ version = "0.2.16"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0942ffc6dcaadf03badf6e6a2d0228460359d5e34b57ccdc720b7382dfbd5ec5" checksum = "0942ffc6dcaadf03badf6e6a2d0228460359d5e34b57ccdc720b7382dfbd5ec5"
[[package]]
name = "android-tzdata"
version = "0.1.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e999941b234f3131b00bc13c22d06e8c5ff726d1b6318ac7eb276997bbb4fef0"
[[package]]
name = "android_system_properties"
version = "0.1.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "819e7219dbd41043ac279b19830f2efc897156490d7fd6ea916720117ee66311"
dependencies = [
"libc",
]
[[package]] [[package]]
name = "anstream" name = "anstream"
version = "0.6.4" version = "0.6.4"
@ -164,7 +179,7 @@ dependencies = [
"fuzzy-matcher", "fuzzy-matcher",
"indicatif", "indicatif",
"interim", "interim",
"itertools", "itertools 0.11.0",
"log", "log",
"ratatui", "ratatui",
"rpassword", "rpassword",
@ -199,7 +214,7 @@ dependencies = [
"generic-array", "generic-array",
"hex", "hex",
"interim", "interim",
"itertools", "itertools 0.11.0",
"lazy_static", "lazy_static",
"log", "log",
"memchr", "memchr",
@ -256,6 +271,7 @@ dependencies = [
"eyre", "eyre",
"fs-err", "fs-err",
"http", "http",
"openidconnect",
"rand 0.8.5", "rand 0.8.5",
"reqwest", "reqwest",
"semver", "semver",
@ -367,6 +383,12 @@ dependencies = [
"rustc-demangle", "rustc-demangle",
] ]
[[package]]
name = "base16ct"
version = "0.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4c7f02d4ea65f2c1853089ffd8d2787bdbc63de2f0d29dedbcf8ccdfa0ccd4cf"
[[package]] [[package]]
name = "base64" name = "base64"
version = "0.13.1" version = "0.13.1"
@ -517,6 +539,21 @@ dependencies = [
"cpufeatures", "cpufeatures",
] ]
[[package]]
name = "chrono"
version = "0.4.31"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7f2c685bad3eb3d45a01354cedb7d5faa66194d1d58ba6e267a8de788f79db38"
dependencies = [
"android-tzdata",
"iana-time-zone",
"js-sys",
"num-traits",
"serde",
"wasm-bindgen",
"windows-targets 0.48.5",
]
[[package]] [[package]]
name = "cipher" name = "cipher"
version = "0.3.0" version = "0.3.0"
@ -746,6 +783,18 @@ dependencies = [
"winapi", "winapi",
] ]
[[package]]
name = "crypto-bigint"
version = "0.5.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "28f85c3514d2a6e64160359b45a3918c3b4178bcbf4ae5d03ab2d02e521c479a"
dependencies = [
"generic-array",
"rand_core 0.6.4",
"subtle",
"zeroize",
]
[[package]] [[package]]
name = "crypto-common" name = "crypto-common"
version = "0.1.6" version = "0.1.6"
@ -823,6 +872,41 @@ dependencies = [
"syn 2.0.38", "syn 2.0.38",
] ]
[[package]]
name = "darling"
version = "0.20.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0209d94da627ab5605dcccf08bb18afa5009cfbef48d8a8b7d7bdbc79be25c5e"
dependencies = [
"darling_core",
"darling_macro",
]
[[package]]
name = "darling_core"
version = "0.20.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "177e3443818124b357d8e76f53be906d60937f0d3a90773a664fa63fa253e621"
dependencies = [
"fnv",
"ident_case",
"proc-macro2",
"quote",
"strsim",
"syn 2.0.38",
]
[[package]]
name = "darling_macro"
version = "0.20.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "836a9bbc7ad63342d6d6e7b815ccab164bc77a2d95d84bc3117a8c0d5c98e2d5"
dependencies = [
"darling_core",
"quote",
"syn 2.0.38",
]
[[package]] [[package]]
name = "der" name = "der"
version = "0.7.8" version = "0.7.8"
@ -934,6 +1018,26 @@ version = "1.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9ea835d29036a4087793836fa931b08837ad5e957da9e23886b29586fb9b6650" checksum = "9ea835d29036a4087793836fa931b08837ad5e957da9e23886b29586fb9b6650"
[[package]]
name = "dyn-clone"
version = "1.0.16"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "545b22097d44f8a9581187cdf93de7a71e4722bf51200cfaba810865b49a495d"
[[package]]
name = "ecdsa"
version = "0.16.8"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a4b1e0c257a9e9f25f90ff76d7a68360ed497ee519c8e428d1825ef0000799d4"
dependencies = [
"der",
"digest 0.10.7",
"elliptic-curve",
"rfc6979",
"signature 2.1.0",
"spki",
]
[[package]] [[package]]
name = "ed25519" name = "ed25519"
version = "1.5.3" version = "1.5.3"
@ -989,6 +1093,27 @@ dependencies = [
"serde", "serde",
] ]
[[package]]
name = "elliptic-curve"
version = "0.13.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d97ca172ae9dc9f9b779a6e3a65d308f2af74e5b8c921299075bdb4a0370e914"
dependencies = [
"base16ct",
"crypto-bigint",
"digest 0.10.7",
"ff",
"generic-array",
"group",
"hkdf",
"pem-rfc7468",
"pkcs8",
"rand_core 0.6.4",
"sec1",
"subtle",
"zeroize",
]
[[package]] [[package]]
name = "encode_unicode" name = "encode_unicode"
version = "0.3.6" version = "0.3.6"
@ -1076,6 +1201,16 @@ version = "2.0.1"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "25cbce373ec4653f1a01a31e8a5e5ec0c622dc27ff9c4e6606eefef5cbbed4a5" checksum = "25cbce373ec4653f1a01a31e8a5e5ec0c622dc27ff9c4e6606eefef5cbbed4a5"
[[package]]
name = "ff"
version = "0.13.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ded41244b729663b1e574f1b4fb731469f69f79c17667b5d776b16cda0479449"
dependencies = [
"rand_core 0.6.4",
"subtle",
]
[[package]] [[package]]
name = "fiat-crypto" name = "fiat-crypto"
version = "0.2.1" version = "0.2.1"
@ -1286,8 +1421,10 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "be4136b2a15dd319360be1c07d9933517ccf0be8f16bf62a3bee4f0d618df427" checksum = "be4136b2a15dd319360be1c07d9933517ccf0be8f16bf62a3bee4f0d618df427"
dependencies = [ dependencies = [
"cfg-if", "cfg-if",
"js-sys",
"libc", "libc",
"wasi 0.11.0+wasi-snapshot-preview1", "wasi 0.11.0+wasi-snapshot-preview1",
"wasm-bindgen",
] ]
[[package]] [[package]]
@ -1296,6 +1433,17 @@ version = "0.28.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6fb8d784f27acf97159b40fc4db5ecd8aa23b9ad5ef69cdd136d3bc80665f0c0" checksum = "6fb8d784f27acf97159b40fc4db5ecd8aa23b9ad5ef69cdd136d3bc80665f0c0"
[[package]]
name = "group"
version = "0.13.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f0f9ef7462f7c099f518d754361858f86d8a07af53ba9af0fe635bbccb151a63"
dependencies = [
"ff",
"rand_core 0.6.4",
"subtle",
]
[[package]] [[package]]
name = "h2" name = "h2"
version = "0.3.21" version = "0.3.21"
@ -1472,6 +1620,35 @@ dependencies = [
"tokio-rustls", "tokio-rustls",
] ]
[[package]]
name = "iana-time-zone"
version = "0.1.58"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8326b86b6cff230b97d0d312a6c40a60726df3332e721f72a1b035f451663b20"
dependencies = [
"android_system_properties",
"core-foundation-sys",
"iana-time-zone-haiku",
"js-sys",
"wasm-bindgen",
"windows-core",
]
[[package]]
name = "iana-time-zone-haiku"
version = "0.1.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f31827a206f56af32e590ba56d5d2d085f558508192593743f16b2306495269f"
dependencies = [
"cc",
]
[[package]]
name = "ident_case"
version = "1.0.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b9e0384b61958566e926dc50660321d12159025e767c18e043daf26b70104c39"
[[package]] [[package]]
name = "idna" name = "idna"
version = "0.4.0" version = "0.4.0"
@ -1496,6 +1673,7 @@ checksum = "bd070e393353796e801d209ad339e89596eb4c8d430d18ede6a1cced8fafbd99"
dependencies = [ dependencies = [
"autocfg", "autocfg",
"hashbrown 0.12.3", "hashbrown 0.12.3",
"serde",
] ]
[[package]] [[package]]
@ -1506,6 +1684,7 @@ checksum = "8adf3ddd720272c6ea8bf59463c04e0f93d0bbf7c5439b691bca2987e0270897"
dependencies = [ dependencies = [
"equivalent", "equivalent",
"hashbrown 0.14.2", "hashbrown 0.14.2",
"serde",
] ]
[[package]] [[package]]
@ -1581,6 +1760,15 @@ dependencies = [
"nom", "nom",
] ]
[[package]]
name = "itertools"
version = "0.10.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b0fd2260e829bddf4cb6ea802289de2f86d6a7a690192fbe91b3f46e0f2c8473"
dependencies = [
"either",
]
[[package]] [[package]]
name = "itertools" name = "itertools"
version = "0.11.0" version = "0.11.0"
@ -1940,6 +2128,26 @@ version = "0.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "830b246a0e5f20af87141b25c173cd1b609bd7779a4617d6ec582abaf90870f3" checksum = "830b246a0e5f20af87141b25c173cd1b609bd7779a4617d6ec582abaf90870f3"
[[package]]
name = "oauth2"
version = "4.4.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c38841cdd844847e3e7c8d29cef9dcfed8877f8f56f9071f77843ecf3baf937f"
dependencies = [
"base64 0.13.1",
"chrono",
"getrandom 0.2.10",
"http",
"rand 0.8.5",
"reqwest",
"serde",
"serde_json",
"serde_path_to_error",
"sha2 0.10.8",
"thiserror",
"url",
]
[[package]] [[package]]
name = "objc" name = "objc"
version = "0.2.7" version = "0.2.7"
@ -1990,6 +2198,38 @@ version = "0.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "624a8340c38c1b80fd549087862da4ba43e08858af025b236e509b6649fc13d5" checksum = "624a8340c38c1b80fd549087862da4ba43e08858af025b236e509b6649fc13d5"
[[package]]
name = "openidconnect"
version = "3.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "62d6050f6a84b81f23c569f5607ad883293e57491036e318fafe6fc4895fadb1"
dependencies = [
"base64 0.13.1",
"chrono",
"dyn-clone",
"ed25519-dalek 2.0.0",
"hmac",
"http",
"itertools 0.10.5",
"log",
"oauth2",
"p256",
"p384",
"rand 0.8.5",
"rsa",
"serde",
"serde-value",
"serde_derive",
"serde_json",
"serde_path_to_error",
"serde_plain",
"serde_with",
"sha2 0.10.8",
"subtle",
"thiserror",
"url",
]
[[package]] [[package]]
name = "openssl-probe" name = "openssl-probe"
version = "0.1.5" version = "0.1.5"
@ -2002,6 +2242,15 @@ version = "0.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "04744f49eae99ab78e0d5c0b603ab218f515ea8cfe5a456d7629ad883a3b6e7d" checksum = "04744f49eae99ab78e0d5c0b603ab218f515ea8cfe5a456d7629ad883a3b6e7d"
[[package]]
name = "ordered-float"
version = "2.10.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "68f19d67e5a2795c94e73e0bb1cc1a7edeb2e28efd39e2e1c9b7a40c1108b11c"
dependencies = [
"num-traits",
]
[[package]] [[package]]
name = "os_pipe" name = "os_pipe"
version = "1.1.4" version = "1.1.4"
@ -2018,6 +2267,30 @@ version = "0.1.1"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b15813163c1d831bf4a13c3610c05c0d03b39feb07f7e09fa234dac9b15aaf39" checksum = "b15813163c1d831bf4a13c3610c05c0d03b39feb07f7e09fa234dac9b15aaf39"
[[package]]
name = "p256"
version = "0.13.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c9863ad85fa8f4460f9c48cb909d38a0d689dba1f6f6988a5e3e0d31071bcd4b"
dependencies = [
"ecdsa",
"elliptic-curve",
"primeorder",
"sha2 0.10.8",
]
[[package]]
name = "p384"
version = "0.13.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "70786f51bcc69f6a4c0360e063a4cac5419ef7c5cd5b3c99ad70f3be5ba79209"
dependencies = [
"ecdsa",
"elliptic-curve",
"primeorder",
"sha2 0.10.8",
]
[[package]] [[package]]
name = "parking_lot" name = "parking_lot"
version = "0.12.1" version = "0.12.1"
@ -2207,6 +2480,15 @@ dependencies = [
"yansi", "yansi",
] ]
[[package]]
name = "primeorder"
version = "0.13.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c7dbe9ed3b56368bd99483eb32fe9c17fdd3730aebadc906918ce78d54c7eeb4"
dependencies = [
"elliptic-curve",
]
[[package]] [[package]]
name = "proc-macro2" name = "proc-macro2"
version = "1.0.69" version = "1.0.69"
@ -2306,7 +2588,7 @@ dependencies = [
"cassowary", "cassowary",
"crossterm", "crossterm",
"indoc", "indoc",
"itertools", "itertools 0.11.0",
"lru", "lru",
"paste", "paste",
"strum", "strum",
@ -2433,9 +2715,20 @@ dependencies = [
"wasm-bindgen", "wasm-bindgen",
"wasm-bindgen-futures", "wasm-bindgen-futures",
"web-sys", "web-sys",
"webpki-roots 0.25.2",
"winreg", "winreg",
] ]
[[package]]
name = "rfc6979"
version = "0.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f8dd2a808d456c4a54e300a23e9f5a67e122c3024119acbfd73e3bf664491cb2"
dependencies = [
"hmac",
"subtle",
]
[[package]] [[package]]
name = "ring" name = "ring"
version = "0.16.20" version = "0.16.20"
@ -2692,6 +2985,20 @@ dependencies = [
"untrusted 0.9.0", "untrusted 0.9.0",
] ]
[[package]]
name = "sec1"
version = "0.7.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d3e97a565f76233a6003f9f5c54be1d9c5bdfa3eccfb189469f11ec4901c47dc"
dependencies = [
"base16ct",
"der",
"generic-array",
"pkcs8",
"subtle",
"zeroize",
]
[[package]] [[package]]
name = "security-framework" name = "security-framework"
version = "2.9.2" version = "2.9.2"
@ -2730,6 +3037,16 @@ dependencies = [
"serde_derive", "serde_derive",
] ]
[[package]]
name = "serde-value"
version = "0.7.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f3a1a3341211875ef120e117ea7fd5228530ae7e7036a779fdc9117be6b3282c"
dependencies = [
"ordered-float",
"serde",
]
[[package]] [[package]]
name = "serde_derive" name = "serde_derive"
version = "1.0.171" version = "1.0.171"
@ -2762,6 +3079,15 @@ dependencies = [
"serde", "serde",
] ]
[[package]]
name = "serde_plain"
version = "1.0.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9ce1fc6db65a611022b23a0dec6975d63fb80a302cb3388835ff02c097258d50"
dependencies = [
"serde",
]
[[package]] [[package]]
name = "serde_regex" name = "serde_regex"
version = "1.1.0" version = "1.1.0"
@ -2784,6 +3110,35 @@ dependencies = [
"serde", "serde",
] ]
[[package]]
name = "serde_with"
version = "3.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "64cd236ccc1b7a29e7e2739f27c0b2dd199804abc4290e32f59f3b68d6405c23"
dependencies = [
"base64 0.21.5",
"chrono",
"hex",
"indexmap 1.9.3",
"indexmap 2.0.2",
"serde",
"serde_json",
"serde_with_macros",
"time",
]
[[package]]
name = "serde_with_macros"
version = "3.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "93634eb5f75a2323b16de4748022ac4297f9e76b6dced2be287a099f41b5e788"
dependencies = [
"darling",
"proc-macro2",
"quote",
"syn 2.0.38",
]
[[package]] [[package]]
name = "sha1" name = "sha1"
version = "0.10.6" version = "0.10.6"
@ -2959,7 +3314,7 @@ version = "0.2.2"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6b7b278788e7be4d0d29c0f39497a0eef3fba6bbc8e70d8bf7fde46edeaa9e85" checksum = "6b7b278788e7be4d0d29c0f39497a0eef3fba6bbc8e70d8bf7fde46edeaa9e85"
dependencies = [ dependencies = [
"itertools", "itertools 0.11.0",
"nom", "nom",
"unicode_categories", "unicode_categories",
] ]
@ -3019,7 +3374,7 @@ dependencies = [
"tracing", "tracing",
"url", "url",
"uuid", "uuid",
"webpki-roots", "webpki-roots 0.24.0",
] ]
[[package]] [[package]]
@ -3695,6 +4050,7 @@ dependencies = [
"form_urlencoded", "form_urlencoded",
"idna", "idna",
"percent-encoding", "percent-encoding",
"serde",
] ]
[[package]] [[package]]
@ -3902,6 +4258,12 @@ dependencies = [
"rustls-webpki", "rustls-webpki",
] ]
[[package]]
name = "webpki-roots"
version = "0.25.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "14247bb57be4f377dfb94c72830b8ce8fc6beac03cf4bf7b9732eadd414123fc"
[[package]] [[package]]
name = "whoami" name = "whoami"
version = "1.4.1" version = "1.4.1"
@ -3952,6 +4314,15 @@ version = "0.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f"
[[package]]
name = "windows-core"
version = "0.51.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f1f8cf84f35d2db49a46868f947758c7a1138116f7fac3bc844f43ade1292e64"
dependencies = [
"windows-targets 0.48.5",
]
[[package]] [[package]]
name = "windows-sys" name = "windows-sys"
version = "0.45.0" version = "0.45.0"

View file

@ -33,3 +33,4 @@ tower-http = { version = "0.4", features = ["trace"] }
reqwest = { workspace = true } reqwest = { workspace = true }
argon2 = "0.5.0" argon2 = "0.5.0"
semver = { workspace = true } semver = { workspace = true }
openidconnect = "3.4.0"

View file

@ -8,6 +8,7 @@ pub mod history;
pub mod record; pub mod record;
pub mod status; pub mod status;
pub mod user; pub mod user;
pub mod oidc;
const VERSION: &str = env!("CARGO_PKG_VERSION"); const VERSION: &str = env!("CARGO_PKG_VERSION");

View file

@ -0,0 +1,214 @@
use axum::{
extract::{State, Query},
response::Redirect,
Json,
};
use http::StatusCode;
use openidconnect::{
core::{CoreAuthenticationFlow, CoreClient, CoreProviderMetadata},
reqwest::async_http_client,
AuthorizationCode, ClientId, ClientSecret, CsrfToken, IssuerUrl, Nonce, RedirectUrl, Scope,
TokenResponse,
};
use serde::{Serialize, Deserialize};
use tracing::error;
use uuid::Uuid;
use super::{ErrorResponse, ErrorResponseStatus, RespExt};
use crate::router::AppState;
use atuin_server_database::{Database, DbError, models::{NewUser, NewSession}};
#[derive(Debug, Serialize, Deserialize)]
pub struct OIDCGetResponse {
pub login_url: String
}
pub async fn get<DB: Database>(state: State<AppState<DB>>) -> Result<Json<OIDCGetResponse>, ErrorResponseStatus<'static>> {
if state.settings.oidc_authority.is_none()
|| state.settings.oidc_client_id.is_none()
|| state.settings.oidc_client_secret.is_none()
{
return Err(ErrorResponse::reply("OIDC not supported on this server").with_status(StatusCode::BAD_REQUEST));
}
let url = state
.settings
.public_url
.strip_suffix("/")
.unwrap_or(&state.settings.public_url)
.to_string()
+ "/oidc/login";
Ok(Json(OIDCGetResponse {
// This is dynamic to allow for backwards compatibility later
// it also is relative to the client's configured server URL
login_url: url,
}))
}
#[derive(Debug, Serialize, Deserialize)]
pub struct LoginQuery {
pub callback: String,
}
pub async fn login<DB: Database>(
state: State<AppState<DB>>,
params: Query<LoginQuery>,
) -> Result<Redirect, ErrorResponseStatus<'static>> {
if state.settings.oidc_authority.is_none()
|| state.settings.oidc_client_id.is_none()
|| state.settings.oidc_client_secret.is_none()
{
return Err(ErrorResponse::reply("OIDC not supported on this server").with_status(StatusCode::BAD_REQUEST));
}
let provider_metadata = CoreProviderMetadata::discover_async(
IssuerUrl::new(state.settings.oidc_authority.clone().unwrap()).or(Err(ErrorResponse::reply("unable to parse configured oidc authority").with_status(StatusCode::INTERNAL_SERVER_ERROR)))?,
async_http_client,
)
.await
.or(Err(ErrorResponse::reply("unable to fetch oidc provider metadata").with_status(StatusCode::INTERNAL_SERVER_ERROR)))?;
let client = CoreClient::from_provider_metadata(
provider_metadata,
ClientId::new(state.settings.oidc_client_id.clone().unwrap()),
Some(ClientSecret::new(
state.settings.oidc_client_secret.clone().unwrap(),
)),
)
// Set the URL the user will be redirected to after the authorization process.
.set_redirect_uri(
RedirectUrl::new(
state
.settings
.public_url
.strip_suffix("/")
.unwrap_or(&state.settings.public_url)
.to_string()
+ "/oidc/callback",
)
.or(Err(ErrorResponse::reply("unable to parse constructed redirect uri").with_status(StatusCode::INTERNAL_SERVER_ERROR)))?,
);
let url = client
.authorize_url(
CoreAuthenticationFlow::AuthorizationCode,
|| CsrfToken::new(params.0.callback),
Nonce::new_random,
)
.add_scopes(vec![Scope::new("profile".to_string()), Scope::new("email".to_string())])
.url();
Ok(Redirect::to(url.0.as_str()))
}
#[derive(Debug, Serialize, Deserialize)]
pub struct CallbackQuery {
pub code: String,
pub state: String
}
pub async fn callback<DB: Database>(
state: State<AppState<DB>>,
params: Query<CallbackQuery>,
) -> Result<Json<String>, ErrorResponseStatus<'static>> {
if state.settings.oidc_authority.is_none()
|| state.settings.oidc_client_id.is_none()
|| state.settings.oidc_client_secret.is_none()
{
return Err(ErrorResponse::reply("OIDC not supported on this server").with_status(StatusCode::BAD_REQUEST));
}
let provider_metadata = CoreProviderMetadata::discover_async(
IssuerUrl::new(state.settings.oidc_authority.clone().unwrap()).or(Err(ErrorResponse::reply("unable to parse configured oidc authority").with_status(StatusCode::INTERNAL_SERVER_ERROR)))?,
async_http_client,
)
.await
.or(Err(ErrorResponse::reply("unable to fetch oidc provider metadata").with_status(StatusCode::INTERNAL_SERVER_ERROR)))?;
let client = CoreClient::from_provider_metadata(
provider_metadata,
ClientId::new(state.settings.oidc_client_id.clone().unwrap()),
Some(ClientSecret::new(
state.settings.oidc_client_secret.clone().unwrap(),
)),
).set_redirect_uri(
RedirectUrl::new(
state
.settings
.public_url
.strip_suffix("/")
.unwrap_or(&state.settings.public_url)
.to_string()
+ "/oidc/callback",
)
.or(Err(ErrorResponse::reply("unable to parse constructed redirect uri").with_status(StatusCode::INTERNAL_SERVER_ERROR)))?,
);
let token_response = client
.exchange_code(AuthorizationCode::new(params.0.code))
.request_async(async_http_client)
.await
.or(Err(ErrorResponse::reply("unable to exchange oidc code for token").with_status(StatusCode::INTERNAL_SERVER_ERROR)))?;
let id_token = token_response.id_token().unwrap();
// TODO actual audience validation
let claims = dbg!(id_token
.claims(&client.id_token_verifier().require_audience_match(false), |_: Option<&Nonce>| Ok(())))
.or(Err(ErrorResponse::reply("unable to validate oidc token").with_status(StatusCode::INTERNAL_SERVER_ERROR)))?;
let db = &state.0.database;
let username = claims.preferred_username().ok_or(ErrorResponse::reply("oidc token did not contain preferred_username").with_status(StatusCode::INTERNAL_SERVER_ERROR))?;
let email = claims.email().ok_or(ErrorResponse::reply("oidc token did not contain email").with_status(StatusCode::INTERNAL_SERVER_ERROR))?;
// Fetches the existing user ID, or registers them on the spot.
let user_id = match db.get_user(username).await {
Ok(u) => u.id,
Err(DbError::NotFound) => {
let new_user = NewUser {
email: email.to_string(),
username: username.to_string(),
password: "".to_string(),
};
match db.add_user(&new_user).await {
Ok(id) => id,
Err(e) => {
error!("failed to add user via oidc: {}", e);
return Err(
ErrorResponse::reply("failed to add user via oidc").with_status(StatusCode::BAD_REQUEST)
);
}
}
}
Err(DbError::Other(e)) => {
error!("failed to get user {}: {}", username.as_str(), e);
return Err(ErrorResponse::reply("database error")
.with_status(StatusCode::INTERNAL_SERVER_ERROR));
}
};
// Creates a new session for the user
let token = Uuid::new_v4().as_simple().to_string();
let new_session = NewSession {
user_id,
token: (&token).into(),
};
match db.add_session(&new_session).await {
Ok(_) => (),
Err(e) => {
error!("failed to add session from oidc: {}", e);
return Err(ErrorResponse::reply("failed to register user via oidc")
.with_status(StatusCode::BAD_REQUEST))
}
};
dbg!(claims);
dbg!(username);
dbg!(email);
// TODO - redirect to localhost + client side aspect + clean stuff up
Ok(Json("it works probably wtf".to_string()))
}

View file

@ -111,6 +111,9 @@ pub fn router<DB: Database>(database: DB, settings: Settings<DB::Settings>) -> R
.route("/user/:username", get(handlers::user::get)) .route("/user/:username", get(handlers::user::get))
.route("/account", delete(handlers::user::delete)) .route("/account", delete(handlers::user::delete))
.route("/register", post(handlers::user::register)) .route("/register", post(handlers::user::register))
.route("/oidc", get(handlers::oidc::get))
.route("/oidc/login", get(handlers::oidc::login))
.route("/oidc/callback", get(handlers::oidc::callback))
.route("/login", post(handlers::user::login)); .route("/login", post(handlers::user::login));
let path = settings.path.as_str(); let path = settings.path.as_str();

View file

@ -18,6 +18,10 @@ pub struct Settings<DbSettings> {
pub page_size: i64, pub page_size: i64,
pub register_webhook_url: Option<String>, pub register_webhook_url: Option<String>,
pub register_webhook_username: String, pub register_webhook_username: String,
pub public_url: String,
pub oidc_authority: Option<String>,
pub oidc_client_id: Option<String>,
pub oidc_client_secret: Option<String>,
#[serde(flatten)] #[serde(flatten)]
pub db_settings: DbSettings, pub db_settings: DbSettings,

37
docker-compose.dev.yml Normal file
View file

@ -0,0 +1,37 @@
version: '3.5'
services:
atuin:
restart: always
image: atuinlocal:latest
build:
context: .
dockerfile: Dockerfile
network: host
command: server start
volumes:
- "./config:/config"
links:
- postgresql:db
ports:
- 8888:8888
environment:
ATUIN_HOST: "0.0.0.0"
ATUIN_OPEN_REGISTRATION: "true"
ATUIN_DB_URI: postgres://atuin:really-insecure@db/atuin
postgresql:
image: postgres:14
restart: unless-stopped
volumes: # Don't remove permanent storage for index database files!
- "./database:/var/lib/postgresql/data/"
environment:
POSTGRES_USER: atuin
POSTGRES_PASSWORD: really-insecure
POSTGRES_DB: atuin
networks:
default:
name: atuinnet
ipam:
driver: default
config:
- subnet: 172.20.0.0/16