diff --git a/Cargo.lock b/Cargo.lock index e29d1e5..eeb7dc5 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1041,6 +1041,20 @@ version = "0.2.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "348108ab3fba42ec82ff6e9564fc4ca0247bdccdc68dd8af9764bbc79c3c8ffb" +[[package]] +name = "light-openid" +version = "0.1.0-alpha" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1745cac605f9565d6fffdcf9b18ee6e51d95b16a8304533fc88e06e30537dc6f" +dependencies = [ + "base64", + "log", + "reqwest", + "serde", + "serde_json", + "urlencoding", +] + [[package]] name = "linux-raw-sys" version = "0.3.4" @@ -1193,12 +1207,12 @@ dependencies = [ "env_logger", "futures-util", "lazy_static", + "light-openid", "log", "rand", "reqwest", "serde", "serde_json", - "urlencoding", ] [[package]] @@ -1413,9 +1427,9 @@ checksum = "a5996294f19bd3aae0453a862ad728f60e6600695733dd5df01da90c54363a3c" [[package]] name = "reqwest" -version = "0.11.16" +version = "0.11.17" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "27b71749df584b7f4cac2c426c127a7c785a5106cc98f7a8feb044115f0fa254" +checksum = "13293b639a097af28fc8a90f22add145a9c954e49d77da06263d58cf44d5fb91" dependencies = [ "base64", "bytes", diff --git a/Cargo.toml b/Cargo.toml index 56f1f42..27d4563 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -6,6 +6,7 @@ edition = "2021" # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [dependencies] +light-openid = "0.1.0-alpha" log = "0.4.17" env_logger = "0.10.0" clap = { version = "4.2.4", features = ["derive", "env"] } @@ -15,7 +16,6 @@ askama = "0.12.0" serde = { version = "1.0.160", features = ["derive"] } serde_json = "1.0.96" reqwest = { version = "0.11.16", features = ["json"] } -urlencoding = "2.1.2" futures-util = "0.3.28" aes-gcm = "0.10.1" base64 = "0.21.0" diff --git a/src/lib.rs b/src/lib.rs index 1def6df..664e496 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -4,7 +4,6 @@ pub type Res = Result>; pub mod app_config; pub mod crypto_wrapper; -pub mod openid_primitives; pub mod remote_ip; pub mod state_manager; pub mod time_utils; diff --git a/src/main.rs b/src/main.rs index 7faee55..150b4f6 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,9 +1,9 @@ use actix_web::middleware::Logger; use actix_web::{get, web, App, HttpResponse, HttpServer}; use askama::Template; +use light_openid::primitives::OpenIDConfig; use oidc_test_client::app_config::AppConfig; -use oidc_test_client::openid_primitives::OpenIDConfig; use oidc_test_client::remote_ip::RemoteIP; use oidc_test_client::state_manager::StateManager; @@ -61,7 +61,7 @@ async fn home() -> HttpResponse { #[get("/start")] async fn start(remote_ip: RemoteIP) -> HttpResponse { - let config = match OpenIDConfig::load_from(&AppConfig::get().configuration_url).await { + let config = match OpenIDConfig::load_from_url(&AppConfig::get().configuration_url).await { Ok(c) => c, Err(e) => { log::error!("Failed to load OpenID configuration! {e}"); @@ -77,7 +77,7 @@ async fn start(remote_ip: RemoteIP) -> HttpResponse { } }; - let authorization_url = config.authorization_url( + let authorization_url = config.gen_authorization_url( &AppConfig::get().client_id, &state, &AppConfig::get().redirect_url(), @@ -103,7 +103,7 @@ async fn redirect(remote_ip: RemoteIP, query: web::Query) -> Http } // Then, load OpenID configuration - let config = match OpenIDConfig::load_from(&AppConfig::get().configuration_url).await { + let config = match OpenIDConfig::load_from_url(&AppConfig::get().configuration_url).await { Ok(c) => c, Err(e) => { log::error!("Failed to load OpenID configuration! {e}"); diff --git a/src/openid_primitives.rs b/src/openid_primitives.rs deleted file mode 100644 index 7518012..0000000 --- a/src/openid_primitives.rs +++ /dev/null @@ -1,97 +0,0 @@ -use base64::engine::general_purpose::STANDARD as BASE64_STANDARD; -use base64::Engine; -use std::collections::HashMap; - -use crate::Res; - -#[derive(Debug, Clone, serde::Deserialize)] -pub struct OpenIDConfig { - pub authorization_endpoint: String, - pub token_endpoint: String, - pub userinfo_endpoint: String, -} - -#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)] -pub struct TokenResponse { - pub access_token: String, - pub token_type: String, - #[serde(skip_serializing_if = "Option::is_none")] - pub refresh_token: Option, - #[serde(skip_serializing_if = "Option::is_none")] - pub expires_in: Option, - #[serde(skip_serializing_if = "Option::is_none")] - pub id_token: Option, -} - -#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)] -pub struct UserInfo { - pub sub: String, - #[serde(skip_serializing_if = "Option::is_none")] - pub name: Option, - #[serde(skip_serializing_if = "Option::is_none")] - pub given_name: Option, - #[serde(skip_serializing_if = "Option::is_none")] - pub family_name: Option, - #[serde(skip_serializing_if = "Option::is_none")] - pub preferred_username: Option, - #[serde(skip_serializing_if = "Option::is_none")] - pub email: Option, - #[serde(skip_serializing_if = "Option::is_none")] - pub email_verified: Option, -} - -impl OpenIDConfig { - /// Load OpenID configuration from a given URL - pub async fn load_from(url: &str) -> Res { - Ok(reqwest::get(url).await?.json().await?) - } - - /// Get the authorization URL where a user should be redirect - pub fn authorization_url(&self, client_id: &str, state: &str, redirect_uri: &str) -> String { - let client_id = urlencoding::encode(client_id); - let state = urlencoding::encode(state); - let redirect_uri = urlencoding::encode(redirect_uri); - - format!("{}?response_type=code&scope=openid%20profile%20email&client_id={client_id}&state={state}&redirect_uri={redirect_uri}", self.authorization_endpoint) - } - - /// Query the token endpoint - pub async fn request_token( - &self, - client_id: &str, - client_secret: &str, - code: &str, - redirect_uri: &str, - ) -> Res<(TokenResponse, String)> { - let authorization = BASE64_STANDARD.encode(format!("{}:{}", client_id, client_secret)); - - let mut params = HashMap::new(); - params.insert("grant_type", "authorization_code"); - params.insert("code", code); - params.insert("redirect_uri", redirect_uri); - - let response = reqwest::Client::new() - .post(&self.token_endpoint) - .header("Authorization", format!("Basic {authorization}")) - .form(¶ms) - .send() - .await? - .text() - .await?; - - Ok((serde_json::from_str(&response)?, response)) - } - - /// Query the UserInfo endpoint - pub async fn request_user_info(&self, token: &TokenResponse) -> Res<(UserInfo, String)> { - let response = reqwest::Client::new() - .get(&self.userinfo_endpoint) - .header("Authorization", format!("Bearer {}", token.access_token)) - .send() - .await? - .text() - .await?; - - Ok((serde_json::from_str(&response)?, response)) - } -}