OIDC example connects, now to fix my kanidm setup

This commit is contained in:
Daniel Flanagan 2024-08-07 19:41:30 -05:00
parent ac4dfca0ac
commit 9d3c7efbba
6 changed files with 51 additions and 116 deletions

77
Cargo.lock generated
View file

@ -1127,20 +1127,6 @@ dependencies = [
"native-tls", "native-tls",
] ]
[[package]]
name = "hyper-rustls"
version = "0.24.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ec3efd23720e2049821a693cbc7e65ea87c72f1c58ff2f9522ff332b1491e590"
dependencies = [
"futures-util",
"http 0.2.12",
"hyper 0.14.30",
"rustls 0.21.12",
"tokio",
"tokio-rustls 0.24.1",
]
[[package]] [[package]]
name = "hyper-rustls" name = "hyper-rustls"
version = "0.27.2" version = "0.27.2"
@ -1151,10 +1137,10 @@ dependencies = [
"http 1.1.0", "http 1.1.0",
"hyper 1.4.1", "hyper 1.4.1",
"hyper-util", "hyper-util",
"rustls 0.23.12", "rustls",
"rustls-pki-types", "rustls-pki-types",
"tokio", "tokio",
"tokio-rustls 0.26.0", "tokio-rustls",
"tower-service", "tower-service",
] ]
@ -1634,7 +1620,6 @@ dependencies = [
"getrandom 0.2.15", "getrandom 0.2.15",
"http 0.2.12", "http 0.2.12",
"rand 0.8.5", "rand 0.8.5",
"reqwest 0.11.27",
"serde", "serde",
"serde_json", "serde_json",
"serde_path_to_error", "serde_path_to_error",
@ -2202,7 +2187,6 @@ dependencies = [
"http 0.2.12", "http 0.2.12",
"http-body 0.4.6", "http-body 0.4.6",
"hyper 0.14.30", "hyper 0.14.30",
"hyper-rustls 0.24.2",
"hyper-tls 0.5.0", "hyper-tls 0.5.0",
"ipnet", "ipnet",
"js-sys", "js-sys",
@ -2213,7 +2197,6 @@ dependencies = [
"once_cell", "once_cell",
"percent-encoding 2.3.1", "percent-encoding 2.3.1",
"pin-project-lite", "pin-project-lite",
"rustls 0.21.12",
"rustls-pemfile 1.0.4", "rustls-pemfile 1.0.4",
"serde", "serde",
"serde_json", "serde_json",
@ -2222,14 +2205,12 @@ dependencies = [
"system-configuration", "system-configuration",
"tokio", "tokio",
"tokio-native-tls", "tokio-native-tls",
"tokio-rustls 0.24.1",
"tokio-socks", "tokio-socks",
"tower-service", "tower-service",
"url 2.5.2", "url 2.5.2",
"wasm-bindgen", "wasm-bindgen",
"wasm-bindgen-futures", "wasm-bindgen-futures",
"web-sys", "web-sys",
"webpki-roots",
"winreg 0.50.0", "winreg 0.50.0",
] ]
@ -2249,7 +2230,7 @@ dependencies = [
"http-body 1.0.1", "http-body 1.0.1",
"http-body-util", "http-body-util",
"hyper 1.4.1", "hyper 1.4.1",
"hyper-rustls 0.27.2", "hyper-rustls",
"hyper-tls 0.6.0", "hyper-tls 0.6.0",
"hyper-util", "hyper-util",
"ipnet", "ipnet",
@ -2496,18 +2477,6 @@ dependencies = [
"windows-sys 0.52.0", "windows-sys 0.52.0",
] ]
[[package]]
name = "rustls"
version = "0.21.12"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3f56a14d1f48b391359b22f731fd4bd7e43c97f3c50eee276f3aa09c94784d3e"
dependencies = [
"log 0.4.22",
"ring",
"rustls-webpki 0.101.7",
"sct",
]
[[package]] [[package]]
name = "rustls" name = "rustls"
version = "0.23.12" version = "0.23.12"
@ -2516,7 +2485,7 @@ checksum = "c58f8c84392efc0a126acce10fa59ff7b3d2ac06ab451a33f2741989b806b044"
dependencies = [ dependencies = [
"once_cell", "once_cell",
"rustls-pki-types", "rustls-pki-types",
"rustls-webpki 0.102.6", "rustls-webpki",
"subtle", "subtle",
"zeroize", "zeroize",
] ]
@ -2546,16 +2515,6 @@ 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 = "976295e77ce332211c0d24d92c0e83e50f5c5f046d11082cea19f3df13a3562d" checksum = "976295e77ce332211c0d24d92c0e83e50f5c5f046d11082cea19f3df13a3562d"
[[package]]
name = "rustls-webpki"
version = "0.101.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8b6275d1ee7a1cd780b64aca7726599a1dbc893b1e64144529e55c3c2f745765"
dependencies = [
"ring",
"untrusted",
]
[[package]] [[package]]
name = "rustls-webpki" name = "rustls-webpki"
version = "0.102.6" version = "0.102.6"
@ -2609,16 +2568,6 @@ version = "1.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49" checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49"
[[package]]
name = "sct"
version = "0.7.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "da046153aa2352493d6cb7da4b6e5c0c057d8a1d0a9aa8560baffdd945acd414"
dependencies = [
"ring",
"untrusted",
]
[[package]] [[package]]
name = "sec1" name = "sec1"
version = "0.7.3" version = "0.7.3"
@ -3073,23 +3022,13 @@ dependencies = [
"tokio", "tokio",
] ]
[[package]]
name = "tokio-rustls"
version = "0.24.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c28327cf380ac148141087fbfb9de9d7bd4e84ab5d2c28fbc911d753de8a7081"
dependencies = [
"rustls 0.21.12",
"tokio",
]
[[package]] [[package]]
name = "tokio-rustls" name = "tokio-rustls"
version = "0.26.0" version = "0.26.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0c7bc40d0e5a97695bb96e27995cd3a08538541b0a846f65bba7a359f36700d4" checksum = "0c7bc40d0e5a97695bb96e27995cd3a08538541b0a846f65bba7a359f36700d4"
dependencies = [ dependencies = [
"rustls 0.23.12", "rustls",
"rustls-pki-types", "rustls-pki-types",
"tokio", "tokio",
] ]
@ -3515,12 +3454,6 @@ dependencies = [
"wasm-bindgen", "wasm-bindgen",
] ]
[[package]]
name = "webpki-roots"
version = "0.25.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5f20c57d8d7db6d3b86154206ae5d8fba62dd39573114de97c2cb0578251f8e1"
[[package]] [[package]]
name = "websocket" name = "websocket"
version = "0.18.0" version = "0.18.0"

View file

@ -15,11 +15,12 @@ reqwest-tracing = "0.5.0"
serde = { version = "1.0.197", features = ["derive"] } serde = { version = "1.0.197", features = ["derive"] }
serde_json = "1.0.115" serde_json = "1.0.115"
serde_with = "3.7.0" serde_with = "3.7.0"
tokio = { workspace = true }
tracing = "0.1.40" tracing = "0.1.40"
tracing-subscriber = { version = "0.3.18", features = ["env-filter"] } tracing-subscriber = { version = "0.3.18", features = ["env-filter"] }
urlencoding = "2.1.3" urlencoding = "2.1.3"
openidconnect = { version = "3.5.0", default_features = false }
http_client = { workspace = true } http_client = { workspace = true }
discord_bot = { workspace = true } discord_bot = { workspace = true }
thiserror = { workspace = true } thiserror = { workspace = true }
openidconnect = "3.5.0" tokio = { workspace = true }

View file

@ -6,65 +6,58 @@ use color_eyre::{eyre::eyre, Result};
use openidconnect::core::{ use openidconnect::core::{
CoreAuthenticationFlow, CoreClient, CoreProviderMetadata, CoreUserInfoClaims, CoreAuthenticationFlow, CoreClient, CoreProviderMetadata, CoreUserInfoClaims,
}; };
use openidconnect::AuthorizationCode;
use openidconnect::{ use openidconnect::{
AccessTokenHash, ClientId, CsrfToken, HttpResponse, IssuerUrl, Nonce, PkceCodeChallenge, AccessTokenHash, ClientId, CsrfToken, HttpResponse, IssuerUrl, Nonce, PkceCodeChallenge,
RedirectUrl, Scope, RedirectUrl, Scope,
}; };
use openidconnect::{AuthorizationCode, PolicyUrl};
const ISSUER_URL: &str = "https://localhost:8443/oauth2/openid/yourcloud"; const ISSUER_URL: &str = "https://localhost:8443/oauth2/openid/yourcloud";
const CLIENT_ID: &str = "yourcloud"; const CLIENT_ID: &str = "yourcloud";
const REDIRECT_URL: &str = "http://localhost:3000/oauth2/handler"; const REDIRECT_URL: &str = "http://localhost:3000/oauth2/handler";
const SCOPES: [&str; 3] = ["read", "write", "email"]; const SCOPES: [&str; 3] = ["read", "write", "email"];
#[derive(Debug, Error)] use http_client::{Client, Error};
pub enum Error {
/// Error returned by reqwest crate.
#[error("request failed")]
Reqwest(#[source] reqwest::Error),
/// I/O error.
#[error("I/O error")]
Io(#[source] std::io::Error),
/// Other error.
#[error("Other error: {}", _0)]
Other(String),
}
fn http_client(request: openidconnect::HttpRequest) -> Result<HttpResponse, Error> { pub async fn async_http_client(request: openidconnect::HttpRequest) -> Result<HttpResponse, Error> {
let client = reqwest::Client::builder() let builder = {
// Following redirects opens the client up to SSRF vulnerabilities. let mut builder = Client::builder();
.redirect(Policy::none()) builder.client_builder = builder
.build() .client_builder
.map_err(Error::Reqwest)?; .danger_accept_invalid_certs(true) // hitting localhost
.redirect(http_client::reqwest::redirect::Policy::none());
builder
};
let mut request_builder = client let mut request_builder = builder
.request(request.method, request.url.as_str()) .build()?
.build_request(request.method, request.url.as_str())?
.body(request.body); .body(request.body);
for (name, value) in &request.headers { for (name, value) in &request.headers {
request_builder = request_builder.header(name.as_str(), value.as_bytes()); request_builder = request_builder.header(name.as_str(), value.as_bytes());
} }
let mut response = client let response = request_builder.send().await?;
.execute(request_builder.build().map_err(Error::Reqwest)?)
.map_err(Error::Reqwest)?;
let mut body = Vec::new(); let status_code = response.status();
response.read_to_end(&mut body).map_err(Error::Io)?; let headers = response.headers().to_owned();
let chunks = response.bytes().await.map_err(Error::Reqwest)?;
{
Ok(HttpResponse { Ok(HttpResponse {
status_code: response.status(), status_code,
headers: response.headers().to_owned(), headers,
body, body: chunks.to_vec(),
}) })
}
} }
fn main() -> Result<()> { #[tokio::main]
async fn main() -> Result<()> {
// Use OpenID Connect Discovery to fetch the provider metadata. // Use OpenID Connect Discovery to fetch the provider metadata.
use openidconnect::{OAuth2TokenResponse, TokenResponse}; use openidconnect::{OAuth2TokenResponse, TokenResponse};
let provider_metadata = let provider_metadata = CoreProviderMetadata::discover_async(
CoreProviderMetadata::discover(&IssuerUrl::new(ISSUER_URL.to_string())?, http_client)?; IssuerUrl::new(ISSUER_URL.to_string())?,
async_http_client,
)
.await?;
// Create an OpenID Connect client by specifying the client ID, client secret, authorization URL // Create an OpenID Connect client by specifying the client ID, client secret, authorization URL
// and token URL. // and token URL.
@ -114,7 +107,8 @@ fn main() -> Result<()> {
.exchange_code(AuthorizationCode::new(authorization_code)) .exchange_code(AuthorizationCode::new(authorization_code))
// Set the PKCE code verifier. // Set the PKCE code verifier.
.set_pkce_verifier(pkce_verifier) .set_pkce_verifier(pkce_verifier)
.request(http_client)?; .request_async(async_http_client)
.await?;
// Extract the ID token claims after verifying its authenticity and nonce. // Extract the ID token claims after verifying its authenticity and nonce.
let id_token = token_response let id_token = token_response
@ -151,7 +145,8 @@ fn main() -> Result<()> {
let userinfo: CoreUserInfoClaims = client let userinfo: CoreUserInfoClaims = client
.user_info(token_response.access_token().to_owned(), None) .user_info(token_response.access_token().to_owned(), None)
.map_err(|err| eyre!("No user info endpoint: {:?}", err))? .map_err(|err| eyre!("No user info endpoint: {:?}", err))?
.request(http_client) .request_async(async_http_client)
.await
.map_err(|err| eyre!("Failed requesting user info: {:?}", err))?; .map_err(|err| eyre!("Failed requesting user info: {:?}", err))?;
println!("{:#?}", userinfo); println!("{:#?}", userinfo);

View file

@ -1,6 +1,8 @@
#!/usr/bin/env nix-shell #!/usr/bin/env nix-shell
#!nix-shell -i bash -p ripgrep oath-toolkit #!nix-shell -i bash -p ripgrep oath-toolkit
set -euo pipefail
FAKE_PASSWORD="f0rTkN0x1s_cool" FAKE_PASSWORD="f0rTkN0x1s_cool"
function kdrun { function kdrun {
podman exec -it kanidm "$@" podman exec -it kanidm "$@"
@ -13,7 +15,7 @@ echo "admin password: $admin_password"
echo "idm_admin password: $idm_password" echo "idm_admin password: $idm_password"
# start a kanidm client in the background for an hour # start a kanidm client in the background for an hour
podman kill kanidm-client || true podman kill kanidm-client &>/dev/null || true
sleep 0.2 sleep 0.2
podman run -itd --rm \ podman run -itd --rm \

View file

@ -5,4 +5,4 @@ podman run -itd --rm \
-v "$PWD/chain.pem:/data/chain.pem:ro" \ -v "$PWD/chain.pem:/data/chain.pem:ro" \
-v "$PWD/key.pem:/data/key.pem:ro" \ -v "$PWD/key.pem:/data/key.pem:ro" \
--name kanidm \ --name kanidm \
docker.io/kanidm/server:latest docker.io/kanidm/server:latest &>/dev/null || true

View file

@ -1,3 +1,4 @@
pub use reqwest;
pub use reqwest::header::{HeaderMap, HeaderValue}; pub use reqwest::header::{HeaderMap, HeaderValue};
pub use reqwest::{Body, ClientBuilder, Method, Response, Url}; pub use reqwest::{Body, ClientBuilder, Method, Response, Url};
pub use reqwest_middleware::ClientBuilder as MiddlewareClientBuilder; pub use reqwest_middleware::ClientBuilder as MiddlewareClientBuilder;
@ -16,6 +17,8 @@ pub struct Client {
} }
pub struct Builder { pub struct Builder {
pub client_builder: ClientBuilder,
// middleware_client_builder: MiddlewareClientBuilder,
middleware: Option<Box<Vec<Arc<dyn Middleware>>>>, middleware: Option<Box<Vec<Arc<dyn Middleware>>>>,
default_headers: Option<HeaderMap>, default_headers: Option<HeaderMap>,
base_url: Option<Url>, base_url: Option<Url>,
@ -33,6 +36,7 @@ impl Default for Builder {
middleware.push(Arc::new(TracingMiddleware::default())); middleware.push(Arc::new(TracingMiddleware::default()));
Builder { Builder {
client_builder: ClientBuilder::new(),
name: None, name: None,
base_url: None, base_url: None,
default_headers: None, default_headers: None,
@ -70,7 +74,7 @@ impl Builder {
} }
pub fn build(mut self) -> Result<Client> { pub fn build(mut self) -> Result<Client> {
let mut builder = reqwest::Client::builder(); let mut builder = self.client_builder;
if let Some(headers) = self.default_headers { if let Some(headers) = self.default_headers {
builder = builder.default_headers(headers); builder = builder.default_headers(headers);