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 {