From cb1e6a0876a20927b6d16ca29ad93f86b07c2596 Mon Sep 17 00:00:00 2001 From: Willem-Jan Date: Tue, 12 Dec 2023 16:54:10 +0100 Subject: [PATCH 1/2] Add oauth crate --- Cargo.lock | 244 ++++++++++++++++++++++++++++++++++- crates/mollie_cli/Cargo.toml | 1 + 2 files changed, 244 insertions(+), 1 deletion(-) diff --git a/Cargo.lock b/Cargo.lock index b175db7..c74878a 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -128,6 +128,12 @@ dependencies = [ "backtrace", ] +[[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.5" @@ -146,6 +152,15 @@ version = "2.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "327762f6e5a765692301e5bb513e0d9fef63be86bbc14528052b1cd3e6f03e07" +[[package]] +name = "block-buffer" +version = "0.10.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3078c7629b62d3f0439517fa394996acacc5cbc91c5a20d8c658e77abd503a71" +dependencies = [ + "generic-array", +] + [[package]] name = "bumpalo" version = "3.14.0" @@ -283,6 +298,15 @@ version = "0.8.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "06ea2b9bc92be3c2baa9334a323ebca2d6f074ff852cd1d7b11064035cd3868f" +[[package]] +name = "cpufeatures" +version = "0.2.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ce420fe07aecd3e67c5f910618fe65e94158f6dcc0adf44e00d69ce2bdfe0fd0" +dependencies = [ + "libc", +] + [[package]] name = "crossterm" version = "0.25.0" @@ -308,6 +332,26 @@ dependencies = [ "winapi", ] +[[package]] +name = "crypto-common" +version = "0.1.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1bfb12502f3fc46cca1bb51ac28df9d618d813cdc3d2f25b9fe775a34af26bb3" +dependencies = [ + "generic-array", + "typenum", +] + +[[package]] +name = "digest" +version = "0.10.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9ed9a281f7bc9b7576e61468ba615a66a5c8cfdff42420a70aa82701a3b1e292" +dependencies = [ + "block-buffer", + "crypto-common", +] + [[package]] name = "dirs" version = "5.0.1" @@ -467,6 +511,16 @@ dependencies = [ "slab", ] +[[package]] +name = "generic-array" +version = "0.14.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "85649ca51fd72272d7821adaf274ad91c288277713d9c18820d8499a7ff69e9a" +dependencies = [ + "typenum", + "version_check", +] + [[package]] name = "getopts" version = "0.2.21" @@ -483,8 +537,10 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "fe9006bed769170c11f845cf00c7c1e9092aeb3f268e007c3e760ac68008070f" dependencies = [ "cfg-if", + "js-sys", "libc", "wasi", + "wasm-bindgen", ] [[package]] @@ -594,6 +650,20 @@ dependencies = [ "want", ] +[[package]] +name = "hyper-rustls" +version = "0.24.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ec3efd23720e2049821a693cbc7e65ea87c72f1c58ff2f9522ff332b1491e590" +dependencies = [ + "futures-util", + "http", + "hyper", + "rustls", + "tokio", + "tokio-rustls", +] + [[package]] name = "hyper-tls" version = "0.5.0" @@ -844,6 +914,7 @@ dependencies = [ "log", "miette", "mollie_api", + "oauth2", "openssl", "pad", "qr2term", @@ -896,6 +967,26 @@ dependencies = [ "libc", ] +[[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", + "http", + "rand", + "reqwest", + "serde", + "serde_json", + "serde_path_to_error", + "sha2", + "thiserror", + "url", +] + [[package]] name = "object" version = "0.32.1" @@ -1056,6 +1147,12 @@ version = "0.3.27" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "26072860ba924cbfa98ea39c8c19b4dd6a4a25423dbdf219c1eca91aa0cf6964" +[[package]] +name = "ppv-lite86" +version = "0.2.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5b40af805b3121feab8a3c29f04d8ad262fa8e0561883e7653e024ae4479e6de" + [[package]] name = "proc-macro2" version = "1.0.70" @@ -1106,6 +1203,36 @@ dependencies = [ "proc-macro2", ] +[[package]] +name = "rand" +version = "0.8.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "34af8d1a0e25924bc5b7c43c079c942339d8f0a8b57c39049bef581b46327404" +dependencies = [ + "libc", + "rand_chacha", + "rand_core", +] + +[[package]] +name = "rand_chacha" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e6c10a63a0fa32252be49d21e7709d4d4baf8d231c2dbce1eaa8141b9b127d88" +dependencies = [ + "ppv-lite86", + "rand_core", +] + +[[package]] +name = "rand_core" +version = "0.6.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c" +dependencies = [ + "getrandom", +] + [[package]] name = "redox_syscall" version = "0.4.1" @@ -1187,7 +1314,7 @@ version = "0.11.22" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "046cd98826c46c2ac8ddecae268eb5c2e58628688a5fc7a2643704a73faba95b" dependencies = [ - "base64", + "base64 0.21.5", "bytes", "encoding_rs", "futures-core", @@ -1196,6 +1323,7 @@ dependencies = [ "http", "http-body", "hyper", + "hyper-rustls", "hyper-tls", "ipnet", "js-sys", @@ -1205,20 +1333,38 @@ dependencies = [ "once_cell", "percent-encoding", "pin-project-lite", + "rustls", + "rustls-pemfile", "serde", "serde_json", "serde_urlencoded", "system-configuration", "tokio", "tokio-native-tls", + "tokio-rustls", "tower-service", "url", "wasm-bindgen", "wasm-bindgen-futures", "web-sys", + "webpki-roots", "winreg", ] +[[package]] +name = "ring" +version = "0.17.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "688c63d65483050968b2a8937f7995f443e27041a0f7700aa59b0822aedebb74" +dependencies = [ + "cc", + "getrandom", + "libc", + "spin", + "untrusted", + "windows-sys 0.48.0", +] + [[package]] name = "rustc-demangle" version = "0.1.23" @@ -1238,6 +1384,37 @@ dependencies = [ "windows-sys 0.52.0", ] +[[package]] +name = "rustls" +version = "0.21.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f9d5a6813c0759e4609cd494e8e725babae6a2ca7b62a5536a13daaec6fcb7ba" +dependencies = [ + "log", + "ring", + "rustls-webpki", + "sct", +] + +[[package]] +name = "rustls-pemfile" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1c74cae0a4cf6ccbbf5f359f08efdf8ee7e1dc532573bf0db71968cb56b1448c" +dependencies = [ + "base64 0.21.5", +] + +[[package]] +name = "rustls-webpki" +version = "0.101.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8b6275d1ee7a1cd780b64aca7726599a1dbc893b1e64144529e55c3c2f745765" +dependencies = [ + "ring", + "untrusted", +] + [[package]] name = "rustversion" version = "1.0.14" @@ -1265,6 +1442,16 @@ version = "1.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" 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]] name = "security-framework" version = "2.9.2" @@ -1319,6 +1506,16 @@ dependencies = [ "serde", ] +[[package]] +name = "serde_path_to_error" +version = "0.1.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4beec8bce849d58d06238cb50db2e1c417cfeafa4c63f692b15c82b7c80f8335" +dependencies = [ + "itoa", + "serde", +] + [[package]] name = "serde_spanned" version = "0.6.4" @@ -1340,6 +1537,17 @@ dependencies = [ "serde", ] +[[package]] +name = "sha2" +version = "0.10.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "793db75ad2bcafc3ffa7c68b215fee268f537982cd901d132f89c6343f3a3dc8" +dependencies = [ + "cfg-if", + "cpufeatures", + "digest", +] + [[package]] name = "shell-words" version = "1.1.0" @@ -1417,6 +1625,12 @@ dependencies = [ "windows-sys 0.48.0", ] +[[package]] +name = "spin" +version = "0.9.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6980e8d7511241f8acf4aebddbb1ff938df5eebe98691418c4468d0b72a96a67" + [[package]] name = "strsim" version = "0.10.0" @@ -1632,6 +1846,16 @@ dependencies = [ "tokio", ] +[[package]] +name = "tokio-rustls" +version = "0.24.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c28327cf380ac148141087fbfb9de9d7bd4e84ab5d2c28fbc911d753de8a7081" +dependencies = [ + "rustls", + "tokio", +] + [[package]] name = "tokio-util" version = "0.7.10" @@ -1711,6 +1935,12 @@ version = "0.2.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e421abadd41a4225275504ea4d6566923418b7f05506fbc9c0fe86ba7396114b" +[[package]] +name = "typenum" +version = "1.17.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "42ff0bf0c66b8238c6f3b578df37d0b7848e55df8577b3f74f92a69acceeb825" + [[package]] name = "uncased" version = "0.9.9" @@ -1759,6 +1989,12 @@ version = "0.1.11" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e51733f11c9c4f72aa0c160008246859e340b00807569a0da0e7a1079b27ba85" +[[package]] +name = "untrusted" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8ecb6da28b8a351d773b68d5825ac39017e680750f980f3a1a85cd8dd28a47c1" + [[package]] name = "url" version = "2.5.0" @@ -1880,6 +2116,12 @@ dependencies = [ "wasm-bindgen", ] +[[package]] +name = "webpki-roots" +version = "0.25.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1778a42e8b3b90bff8d0f5032bf22250792889a5cdc752aa0020c84abe3aaf10" + [[package]] name = "winapi" version = "0.3.9" diff --git a/crates/mollie_cli/Cargo.toml b/crates/mollie_cli/Cargo.toml index 2b7b4d8..97c3b8b 100644 --- a/crates/mollie_cli/Cargo.toml +++ b/crates/mollie_cli/Cargo.toml @@ -35,6 +35,7 @@ pad = { version = "0.1"} # TODO: remove reqwest and use only mollie_api reqwest = { version = "0.11", features = ["json", "blocking"] } +oauth2 = "4.4.2" [dev-dependencies] From ccd2a0a15329a62a29e3c612569d5191d5511657 Mon Sep 17 00:00:00 2001 From: Willem-Jan Date: Tue, 12 Dec 2023 17:14:05 +0100 Subject: [PATCH 2/2] Basic oauth flow and store in config --- crates/mollie_cli/src/auth/mod.rs | 68 +++++++++++++++++++++++++- crates/mollie_cli/src/config/config.rs | 2 + 2 files changed, 69 insertions(+), 1 deletion(-) diff --git a/crates/mollie_cli/src/auth/mod.rs b/crates/mollie_cli/src/auth/mod.rs index 508985a..81dc405 100644 --- a/crates/mollie_cli/src/auth/mod.rs +++ b/crates/mollie_cli/src/auth/mod.rs @@ -1,8 +1,12 @@ -use crate::config::ConfigurationService; +use crate::config::{ConfigurationService, ConnectConfig}; +use chrono::{DateTime, Utc}; use clap::{Parser, Subcommand}; use log::info; use mollie_api::auth::{AccessCode, ApiKey}; +use oauth2::{basic::BasicClient, ClientId, AuthUrl, TokenUrl, ClientSecret, AuthorizationCode, reqwest::async_http_client, CsrfToken, Scope, TokenResponse}; +use url::Url; +mod oauth; mod store; #[derive(Parser)] @@ -31,6 +35,17 @@ pub enum AuthCommands { }, /// Get Auth information Get {}, + #[clap(arg_required_else_help(true))] + Connect { + #[clap(long)] + client_id: String, + + #[clap(long)] + client_secret: Option, + + #[clap(long)] + finish: Option, + } } pub async fn command( @@ -67,6 +82,57 @@ pub async fn command( info!("Test API Key: {:?}", config.test_api_key()); info!("Access Token: {:?}", config.access_code()); } + Some(AuthCommands::Connect { client_id, client_secret, finish }) => { + let client = + BasicClient::new( + ClientId::new(client_id.into()), + client_secret.clone().map(ClientSecret::new), + AuthUrl::new("https://my.mollie.com/oauth2/authorize".into()).unwrap(), + Some(TokenUrl::new("https://api.mollie.com/oauth2/tokens".into()).unwrap()) + ); + + if let Some(finish) = finish { + let url = Url::parse(&finish).expect("Invalid finish url"); + let code = url.query_pairs().find(|(key, _)| key == "code").unwrap().1; + + let request = client + .exchange_code(AuthorizationCode::new(code.into())); + info!("{:#?}", request); + + let result = request + .request_async(async_http_client) + .await; + info!("{:#?}", result); + if let Ok(new_config) = config_service.update(&|config| { + let res = result.as_ref(); + let old_connect = config.auth.connect.clone().unwrap(); + let expires_at: Option> = match res.unwrap().expires_in() { + Some(dur) => Some(Utc::now() + dur), + None => None + }; + config.auth.connect = Some(ConnectConfig{ + client_id: old_connect.client_id, + client_secret: old_connect.client_secret, + access_token: Some(res.unwrap().access_token().secret().to_string()), + refresh_token: Some(res.unwrap().refresh_token().unwrap().secret().to_string()), + expires_at, + }); + }) { + if let Some(connect) = &new_config.auth.connect { + info!("{:#?}", connect); + } + } + + } else { + let (auth_url, _csrf_token) = client + .authorize_url(CsrfToken::new_random) + .add_extra_param("approval_prompt", "force") + .add_scope(Scope::new("organizations.read".to_string())) + .url(); + + info!("Browse to: {}", auth_url); + } + } None => {} } Ok(()) diff --git a/crates/mollie_cli/src/config/config.rs b/crates/mollie_cli/src/config/config.rs index b52dbc0..97eb883 100644 --- a/crates/mollie_cli/src/config/config.rs +++ b/crates/mollie_cli/src/config/config.rs @@ -1,3 +1,4 @@ +use chrono::DateTime; use log::debug; use mollie_api::auth::{AccessCode, ApiBearerToken, ApiKey}; use serde::{Deserialize, Serialize}; @@ -76,6 +77,7 @@ pub struct ConnectConfig { pub client_secret: String, pub refresh_token: Option, pub access_token: Option, + pub expires_at: Option>, } fn default_api_config() -> ApiConfig {