From 56e735893f3633d2c713acb2f34339e0cb36166e Mon Sep 17 00:00:00 2001 From: "Victor \"multun\" Collod" Date: Thu, 25 Apr 2024 11:53:16 +0200 Subject: [PATCH] gateway: use a fork of openidconnect-rs with leniant update_at parsing --- gateway/Cargo.lock | 237 ++++++++++++++++++----- gateway/Cargo.toml | 8 +- gateway/actix_auth/Cargo.toml | 2 +- gateway/actix_auth/src/providers/oidc.rs | 57 ++++-- 4 files changed, 230 insertions(+), 74 deletions(-) diff --git a/gateway/Cargo.lock b/gateway/Cargo.lock index f2fc1653212..b4b7f1b2990 100644 --- a/gateway/Cargo.lock +++ b/gateway/Cargo.lock @@ -202,7 +202,7 @@ dependencies = [ "tokio-rustls 0.23.4", "tokio-util", "tracing", - "webpki-roots", + "webpki-roots 0.22.6", ] [[package]] @@ -326,7 +326,7 @@ dependencies = [ "futures-util", "log", "openidconnect", - "reqwest", + "reqwest 0.12.4", "serde", "thiserror", "url", @@ -545,8 +545,8 @@ dependencies = [ "bytes", "futures-util", "http 0.2.12", - "http-body", - "hyper", + "http-body 0.4.6", + "hyper 0.14.28", "itoa", "matchit", "memchr", @@ -571,7 +571,7 @@ dependencies = [ "bytes", "futures-util", "http 0.2.12", - "http-body", + "http-body 0.4.6", "mime", "rustversion", "tower-layer", @@ -599,12 +599,6 @@ 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.20.0" @@ -617,6 +611,12 @@ version = "0.21.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9d297deb1925b89f2ccc13d7635fa0714f12c87adce1c75356b39ca9b7178567" +[[package]] +name = "base64" +version = "0.22.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9475866fec1451be56a3c2400fd081ff546538961565ccb5b7142cbd22bc7a51" + [[package]] name = "base64ct" version = "1.6.0" @@ -1336,6 +1336,29 @@ dependencies = [ "pin-project-lite", ] +[[package]] +name = "http-body" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1cac85db508abc24a2e48553ba12a996e87244a0395ce011e62b37158745d643" +dependencies = [ + "bytes", + "http 1.1.0", +] + +[[package]] +name = "http-body-util" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0475f8b2ac86659c21b64320d5d653f9efe42acd2a4e560073ec61a155a34f1d" +dependencies = [ + "bytes", + "futures-core", + "http 1.1.0", + "http-body 1.0.0", + "pin-project-lite", +] + [[package]] name = "http-range" version = "0.1.5" @@ -1382,7 +1405,7 @@ dependencies = [ "futures-util", "h2", "http 0.2.12", - "http-body", + "http-body 0.4.6", "httparse", "httpdate", "itoa", @@ -1394,18 +1417,40 @@ dependencies = [ "want", ] +[[package]] +name = "hyper" +version = "1.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fe575dd17d0862a9a33781c8c4696a55c320909004a67a00fb286ba8b1bc496d" +dependencies = [ + "bytes", + "futures-channel", + "futures-util", + "http 1.1.0", + "http-body 1.0.0", + "httparse", + "itoa", + "pin-project-lite", + "smallvec", + "tokio", + "want", +] + [[package]] name = "hyper-rustls" -version = "0.24.2" +version = "0.26.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ec3efd23720e2049821a693cbc7e65ea87c72f1c58ff2f9522ff332b1491e590" +checksum = "a0bea761b46ae2b24eb4aef630d8d1c398157b6fc29e6350ecf090a0b70c952c" dependencies = [ "futures-util", - "http 0.2.12", - "hyper", - "rustls 0.21.10", + "http 1.1.0", + "hyper 1.3.1", + "hyper-util", + "rustls 0.22.4", + "rustls-pki-types", "tokio", - "tokio-rustls 0.24.1", + "tokio-rustls 0.25.0", + "tower-service", ] [[package]] @@ -1414,12 +1459,32 @@ version = "0.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bbb958482e8c7be4bc3cf272a766a2b0bf1a6755e7a6ae777f017a31d11b13b1" dependencies = [ - "hyper", + "hyper 0.14.28", "pin-project-lite", "tokio", "tokio-io-timeout", ] +[[package]] +name = "hyper-util" +version = "0.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ca38ef113da30126bbff9cd1705f9273e15d45498615d138b0c20279ac7a76aa" +dependencies = [ + "bytes", + "futures-channel", + "futures-util", + "http 1.1.0", + "http-body 1.0.0", + "hyper 1.3.1", + "pin-project-lite", + "socket2", + "tokio", + "tower", + "tower-service", + "tracing", +] + [[package]] name = "iana-time-zone" version = "0.1.60" @@ -1725,16 +1790,16 @@ dependencies = [ [[package]] name = "oauth2" -version = "4.4.2" +version = "5.0.0-alpha.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c38841cdd844847e3e7c8d29cef9dcfed8877f8f56f9071f77843ecf3baf937f" +checksum = "098af5a5110b4deacf3200682963713b143ae9d28762b739bdb7b98429dfaf68" dependencies = [ - "base64 0.13.1", + "base64 0.21.7", "chrono", "getrandom", - "http 0.2.12", + "http 1.1.0", "rand", - "reqwest", + "reqwest 0.12.4", "serde", "serde_json", "serde_path_to_error", @@ -1766,16 +1831,15 @@ checksum = "c08d65885ee38876c4f86fa503fb49d7b507c2b62552df7c70b2fce627e06381" [[package]] name = "openidconnect" -version = "3.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f47e80a9cfae4462dd29c41e987edd228971d6565553fbc14b8a11e666d91590" +version = "4.0.0-alpha.2" +source = "git+https://github.com/multun/openidconnect-rs.git?branch=updated-at-string#964d84bf68006b002d4dc7bd19438584ddf6d1c8" dependencies = [ - "base64 0.13.1", + "base64 0.21.7", "chrono", "dyn-clone", "ed25519-dalek", "hmac", - "http 0.2.12", + "http 1.1.0", "itertools 0.10.5", "log", "oauth2", @@ -1785,7 +1849,6 @@ dependencies = [ "rsa", "serde", "serde-value", - "serde_derive", "serde_json", "serde_path_to_error", "serde_plain", @@ -1833,7 +1896,7 @@ dependencies = [ "opentelemetry-http", "opentelemetry-semantic-conventions", "opentelemetry_sdk", - "reqwest", + "reqwest 0.11.27", "rmp", "thiserror", "url", @@ -1849,7 +1912,7 @@ dependencies = [ "bytes", "http 0.2.12", "opentelemetry", - "reqwest", + "reqwest 0.11.27", ] [[package]] @@ -2322,9 +2385,45 @@ dependencies = [ "futures-util", "h2", "http 0.2.12", - "http-body", - "hyper", + "http-body 0.4.6", + "hyper 0.14.28", + "ipnet", + "js-sys", + "log", + "mime", + "once_cell", + "percent-encoding", + "pin-project-lite", + "serde", + "serde_json", + "serde_urlencoded", + "sync_wrapper", + "system-configuration", + "tokio", + "tower-service", + "url", + "wasm-bindgen", + "wasm-bindgen-futures", + "web-sys", + "winreg 0.50.0", +] + +[[package]] +name = "reqwest" +version = "0.12.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "566cafdd92868e0939d3fb961bd0dc25fcfaaed179291093b3d43e6b3150ea10" +dependencies = [ + "base64 0.22.0", + "bytes", + "futures-core", + "futures-util", + "http 1.1.0", + "http-body 1.0.0", + "http-body-util", + "hyper 1.3.1", "hyper-rustls", + "hyper-util", "ipnet", "js-sys", "log", @@ -2332,22 +2431,23 @@ dependencies = [ "once_cell", "percent-encoding", "pin-project-lite", - "rustls 0.21.10", + "rustls 0.22.4", "rustls-native-certs", "rustls-pemfile", + "rustls-pki-types", "serde", "serde_json", "serde_urlencoded", "sync_wrapper", - "system-configuration", "tokio", - "tokio-rustls 0.24.1", + "tokio-rustls 0.25.0", "tower-service", "url", "wasm-bindgen", "wasm-bindgen-futures", "web-sys", - "winreg", + "webpki-roots 0.26.1", + "winreg 0.52.0", ] [[package]] @@ -2450,44 +2550,55 @@ dependencies = [ [[package]] name = "rustls" -version = "0.21.10" +version = "0.22.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f9d5a6813c0759e4609cd494e8e725babae6a2ca7b62a5536a13daaec6fcb7ba" +checksum = "bf4ef73721ac7bcd79b2b315da7779d8fc09718c6b3d2d1b2d94850eb8c18432" dependencies = [ "log", "ring 0.17.8", + "rustls-pki-types", "rustls-webpki", - "sct", + "subtle", + "zeroize", ] [[package]] name = "rustls-native-certs" -version = "0.6.3" +version = "0.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a9aace74cb666635c918e9c12bc0d348266037aa8eb599b5cba565709a8dff00" +checksum = "8f1fb85efa936c42c6d5fc28d2629bb51e4b2f4b8a5211e297d599cc5a093792" dependencies = [ "openssl-probe", "rustls-pemfile", + "rustls-pki-types", "schannel", "security-framework", ] [[package]] name = "rustls-pemfile" -version = "1.0.4" +version = "2.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1c74cae0a4cf6ccbbf5f359f08efdf8ee7e1dc532573bf0db71968cb56b1448c" +checksum = "29993a25686778eb88d4189742cd713c9bce943bc54251a33509dc63cbacf73d" dependencies = [ - "base64 0.21.7", + "base64 0.22.0", + "rustls-pki-types", ] +[[package]] +name = "rustls-pki-types" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "beb461507cee2c2ff151784c52762cf4d9ff6a61f3e80968600ed24fa837fa54" + [[package]] name = "rustls-webpki" -version = "0.101.7" +version = "0.102.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8b6275d1ee7a1cd780b64aca7726599a1dbc893b1e64144529e55c3c2f745765" +checksum = "f3bce581c0dd41bce533ce695a1437fa16a7ab5ac3ccfa99fe1a620a7885eabf" dependencies = [ "ring 0.17.8", + "rustls-pki-types", "untrusted 0.9.0", ] @@ -2964,11 +3075,12 @@ dependencies = [ [[package]] name = "tokio-rustls" -version = "0.24.1" +version = "0.25.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c28327cf380ac148141087fbfb9de9d7bd4e84ab5d2c28fbc911d753de8a7081" +checksum = "775e0c0f0adb3a2f22a00c4745d728b479985fc15ee7ca6a2608388c5569860f" dependencies = [ - "rustls 0.21.10", + "rustls 0.22.4", + "rustls-pki-types", "tokio", ] @@ -3045,8 +3157,8 @@ dependencies = [ "futures-util", "h2", "http 0.2.12", - "http-body", - "hyper", + "http-body 0.4.6", + "hyper 0.14.28", "hyper-timeout", "percent-encoding", "pin-project", @@ -3336,6 +3448,15 @@ dependencies = [ "webpki", ] +[[package]] +name = "webpki-roots" +version = "0.26.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b3de34ae270483955a94f4b21bdaaeb83d508bb84a01435f393818edb0012009" +dependencies = [ + "rustls-pki-types", +] + [[package]] name = "winapi" version = "0.3.9" @@ -3527,6 +3648,16 @@ dependencies = [ "windows-sys 0.48.0", ] +[[package]] +name = "winreg" +version = "0.52.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a277a57398d4bfa075df44f501a17cfdf8542d224f0d36095a2adc7aee4ef0a5" +dependencies = [ + "cfg-if", + "windows-sys 0.48.0", +] + [[package]] name = "yansi" version = "1.0.1" diff --git a/gateway/Cargo.toml b/gateway/Cargo.toml index 166ffdcec2e..17b46f36a40 100644 --- a/gateway/Cargo.toml +++ b/gateway/Cargo.toml @@ -51,8 +51,12 @@ phf = "0.11" # actix_auth actix-web-httpauth = "0.8" -openidconnect = { version = "3.4", default-features = false } -reqwest = { version = "0.11", default-features = false, features = [ + +# switch to https://github.com/ramosbugs/openidconnect-rs/pull/165 if it ever lands +openidconnect = { git = "https://github.com/multun/openidconnect-rs.git", branch = "updated-at-string", features = [ + "accept-string-epoch", +] } +reqwest = { version = "0.12", default-features = false, features = [ "rustls-tls-native-roots", ] } url = "2.4" diff --git a/gateway/actix_auth/Cargo.toml b/gateway/actix_auth/Cargo.toml index 2eeff817531..eebb3cde6b7 100644 --- a/gateway/actix_auth/Cargo.toml +++ b/gateway/actix_auth/Cargo.toml @@ -19,7 +19,7 @@ serde.workspace = true thiserror.workspace = true # oidc -openidconnect = { workspace = true, features = ["jwk-alg", "reqwest"] } +openidconnect = { workspace = true, features = ["reqwest"] } reqwest = { workspace = true, features = ["json"] } url.workspace = true diff --git a/gateway/actix_auth/src/providers/oidc.rs b/gateway/actix_auth/src/providers/oidc.rs index be03cd78ab8..2b283cbd208 100644 --- a/gateway/actix_auth/src/providers/oidc.rs +++ b/gateway/actix_auth/src/providers/oidc.rs @@ -5,11 +5,11 @@ use actix_web::{http::StatusCode, web, FromRequest, HttpRequest, HttpResponse}; use futures_util::future::LocalBoxFuture; use log::error; use openidconnect::core::{CoreAuthenticationFlow, CoreClient, CoreProviderMetadata}; -use openidconnect::reqwest::async_http_client; use openidconnect::{ AccessTokenHash, AuthorizationCode, ClientId, ClientSecret, CsrfToken, DiscoveryError, IssuerUrl, LocalizedClaim, Nonce, OAuth2TokenResponse, RedirectUrl, Scope, TokenResponse, }; +use openidconnect::{EndpointMaybeSet, EndpointNotSet, EndpointSet, HttpClientError}; use serde::{Deserialize, Serialize}; use thiserror::Error; @@ -51,7 +51,16 @@ impl OidcConfig { #[derive(Clone)] pub struct OidcProvider { - pub client: Box, + pub client: Box< + CoreClient< + EndpointSet, + EndpointNotSet, + EndpointNotSet, + EndpointNotSet, + EndpointMaybeSet, + EndpointMaybeSet, + >, + >, pub post_login_url: Box, pub profile_scope_override: Option, pub username_whitelist: Option>, @@ -60,11 +69,11 @@ pub struct OidcProvider { impl OidcProvider { pub async fn from_config( config: &OidcConfig, - ) -> Result>> { + ) -> Result>> { + let client = reqwest::Client::new(); // Use OpenID Connect Discovery to fetch the provider metadata. let provider_metadata = - CoreProviderMetadata::discover_async(*config.issuer_url.clone(), async_http_client) - .await?; + CoreProviderMetadata::discover_async(*config.issuer_url.clone(), &client).await?; let client = Box::new( CoreClient::from_provider_metadata( @@ -167,10 +176,15 @@ impl SessionProvider for OidcProvider { } // Now you can exchange it for an access token and ID token. + let client = reqwest::Client::new(); let token_response = self .client .exchange_code(AuthorizationCode::new(info.code.clone())) - .request_async(async_http_client) + .map_err(|err| { + error!("invalid configuration state during code exchange: {err:?}"); + CallbackError::CodeExchangeError + })? + .request_async(&client) .await .map_err(|err| { error!("unable to exchange code: {err:?}"); @@ -181,23 +195,30 @@ impl SessionProvider for OidcProvider { let id_token = token_response .id_token() .ok_or(CallbackError::MissingIDToken)?; - let claims = id_token - .claims(&self.client.id_token_verifier(), nonce) - .map_err(|err| { - error!("unable to verify ID token signature: {err:?}"); - CallbackError::InvalidIDTokenSignature - })?; + let id_token_verifier = self.client.id_token_verifier(); + let claims = id_token.claims(&id_token_verifier, nonce).map_err(|err| { + error!("unable to verify ID token signature: {err:?}"); + CallbackError::InvalidIDTokenSignature + })?; // Verify the access token hash to ensure that the access token hasn't been substituted for // another user's. if let Some(expected_access_token_hash) = claims.access_token_hash() { // this should never fail, as claims verification alreay validates the signing alg - let signing_alg = &id_token - .signing_alg() - .expect("failed to get the signing alg"); - let actual_access_token_hash = - AccessTokenHash::from_token(token_response.access_token(), signing_alg) - .map_err(|_| CallbackError::InvalidAccessTokenSignature)?; + let signing_alg = id_token.signing_alg().map_err(|err| { + error!("cannot fetch ID token signing alg: {err:?}"); + CallbackError::InvalidIDTokenSignature + })?; + let signing_key = id_token.signing_key(&id_token_verifier).map_err(|err| { + error!("cannot fetch ID token signing key: {err:?}"); + CallbackError::InvalidIDTokenSignature + })?; + let actual_access_token_hash = AccessTokenHash::from_token( + token_response.access_token(), + signing_alg, + signing_key, + ) + .map_err(|_| CallbackError::InvalidAccessTokenSignature)?; if actual_access_token_hash != *expected_access_token_hash { return Err(CallbackError::InvalidAccessTokenSignature.into()); }