From ea811aea56088e3d1ce1dcfe04917f98e037bdea Mon Sep 17 00:00:00 2001 From: Hatter Jiang Date: Mon, 10 Jan 2022 23:59:03 +0800 Subject: [PATCH] init commit --- .gitignore | 2 + Cargo.toml | 17 ++++++++ README.md | 2 + src/main.rs | 112 ++++++++++++++++++++++++++++++++++++++++++++++++++++ src/oidc.rs | 44 +++++++++++++++++++++ 5 files changed, 177 insertions(+) create mode 100644 Cargo.toml create mode 100644 src/main.rs create mode 100644 src/oidc.rs diff --git a/.gitignore b/.gitignore index 7f4dba8..b30a24e 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,5 @@ +.idea/ +GIT_IGNORE.txt # ---> macOS # General .DS_Store diff --git a/Cargo.toml b/Cargo.toml new file mode 100644 index 0000000..5c24b52 --- /dev/null +++ b/Cargo.toml @@ -0,0 +1,17 @@ +[package] +name = "oidc_client_flow_login" +version = "0.1.0" +edition = "2021" + +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html + +[dependencies] +rust_util = "0.6.41" +clap = "3.0.5" +base64 = "0.13.0" +urlencoding = "2.1.0" +reqwest = { version= "0.11.8", features = ["json", "socks"] } +serde = { version = "1.0", features = ["derive"] } +serde_json = "1.0" +tokio = { version = "1.15.0", features = ["full"] } + diff --git a/README.md b/README.md index ea5438b..95e8aab 100644 --- a/README.md +++ b/README.md @@ -1,2 +1,4 @@ # oidc_client_flow_login +Google device code: +https://developers.google.com/identity/sign-in/devices diff --git a/src/main.rs b/src/main.rs new file mode 100644 index 0000000..fa0ad3b --- /dev/null +++ b/src/main.rs @@ -0,0 +1,112 @@ +use std::collections::HashMap; +use std::env; +use std::process::exit; +use std::time::Duration; +use clap::{App, Arg}; +use reqwest::{ClientBuilder, Proxy}; +use rust_util::{debugging, information, opt_result, opt_value_result, XResult}; +use rust_util::util_msg::MessageType; +use crate::oidc::{OpenIdClientConfiguration, OpenIdConfiguration}; + +mod oidc; + + +const NAME: &str = env!("CARGO_PKG_NAME"); +const VERSION: &str = env!("CARGO_PKG_VERSION"); +const AUTHORS: &str = env!("CARGO_PKG_AUTHORS"); +const DESCRIPTION: &str = env!("CARGO_PKG_DESCRIPTION"); + +#[tokio::main] +async fn main() -> XResult<()> { + let matches = App::new(NAME) + .version(VERSION) + .about(DESCRIPTION) + .author(AUTHORS) + .arg(Arg::new("version").short('V').long("version").help("Print version")) + .arg(Arg::new("verbose").short('v').long("verbose").help("Verbose")) + .arg(Arg::new("issuer").short('i').long("issuer").takes_value(true).help("Issuer")) + .arg(Arg::new("proxy").short('p').long("proxy").takes_value(true).help("Proxy")) + .arg(Arg::new("skip-certificate-check").short('k').long("skip-certificate-check").help("Skip certificate check")) + .get_matches(); + + if matches.is_present("verbose") { + env::set_var("LOGGER_LEVEL", "*"); + } + if matches.is_present("version") { + information!("{} v{}", NAME, VERSION); + exit(1); + } + let issuer = opt_value_result!(matches.value_of("issuer"), "Issuer is required"); + let openid_configuration_url = format!("{}/.well-known/openid-configuration", issuer); + debugging!("Open id configuration url: {}", openid_configuration_url); + + let mut client_builder = ClientBuilder::new() + .timeout(Duration::from_secs(10)) + .connect_timeout(Duration::from_secs(3)); + if let Some(proxy) = matches.value_of("proxy") { + let proxy = opt_result!( Proxy::all(proxy), "Parse proxy: {} failed: {}", proxy); + client_builder = client_builder.proxy(proxy); + } + if (matches.is_present("skip-certificate-check")) { + client_builder = client_builder.danger_accept_invalid_certs(true); + } + let client = opt_result!( client_builder.build(), "Build reqwest client failed: {}"); + + debugging!("Sending request..."); + let get_openid_configuration_result = opt_result!( + client.get(&openid_configuration_url).send().await, "Get url: {}, failed: {}", openid_configuration_url); + let openid_configuration = opt_result!( + get_openid_configuration_result.json::().await, "Parse open id configuration failed: {}"); + + rust_util::util_msg::when(MessageType::DEBUG, || { + debugging!("Return open id configuration: {}", serde_json::to_string_pretty(&openid_configuration).unwrap()); + }); + + let device_authorization_endpoint = opt_value_result!( + openid_configuration.device_authorization_endpoint, "Open id configuration has not configured device authorization endpoint"); + let token_endpoint = openid_configuration.token_endpoint; + let userinfo_endpoint = openid_configuration.userinfo_endpoint; + + information!("device_authorization_endpoint: {}", &device_authorization_endpoint); + information!("token_endpoint: {}", &token_endpoint); + information!("userinfo_endpoint: {}", &userinfo_endpoint); + + let openid_client_configuration = OpenIdClientConfiguration { + issuer: "".into(), + client_id: "".into(), + client_secret: "".into(), + scope: "".into(), + }; + + // xh POST https://oauth2.googleapis.com/device/code + // client_id==364023986785-o5mqsqd1ej2d1vmvcqai108b7m7v7vc9.apps.googleusercontent.com scope=openid + //{ + // "device_code": "***", + // "user_code": "STLB-RNKS", + // "expires_in": 1800, + // "interval": 5, + // "verification_url": "https://www.google.com/device" + // } + let mut params = HashMap::new(); + params.insert("client_id", &openid_client_configuration.issuer); + params.insert("scope", &openid_client_configuration.scope); + let token_endpoint_builder = opt_result!(client.post(&device_authorization_endpoint) + .form(params).send().await, "Get token: {}, failed: {}", device_authorization_endpoint); + // token_endpoint_builder. + + // xh POST https://oauth2.googleapis.com/token + // client_id==364023986785-o5mqsqd1ej2d1vmvcqai108b7m7v7vc9.apps.googleusercontent.com + // client_secret==*** + // device_code==*** + // grant_type==urn:ietf:params:oauth:grant-type:device_code + // { + // "access_token": "***", + // "expires_in": 3599, + // "refresh_token": "***", + // "scope": "openid", + // "token_type": "Bearer", + // "id_token": "***" + // } + + Ok(()) +} diff --git a/src/oidc.rs b/src/oidc.rs new file mode 100644 index 0000000..9a0b339 --- /dev/null +++ b/src/oidc.rs @@ -0,0 +1,44 @@ +use serde::{Serialize, Deserialize}; + +#[derive(Clone, Debug, Serialize, Deserialize)] +pub struct OpenIdClientConfiguration { + pub issuer: String, + pub client_id: String, + pub client_secret: String, + pub scope: String, +} + +#[derive(Clone, Debug, Serialize, Deserialize)] +pub struct OpenIdConfiguration { + pub issuer: String, + pub authorization_endpoint: String, + pub device_authorization_endpoint: Option, + pub token_endpoint: String, + pub userinfo_endpoint: String, + pub revocation_endpoint: String, +} + +#[derive(Clone, Debug, Serialize, Deserialize)] +pub struct ErrorResponse { + pub error: String, + pub error_description: Option, +} + +#[derive(Clone, Debug, Serialize, Deserialize)] +pub struct DeviceAuthorizationResponse { + pub device_code: String, + pub user_code: String, + pub expires_in: i64, + pub interval: i64, + pub verification_url: String, +} + +#[derive(Clone, Debug, Serialize, Deserialize)] +pub struct TokenResponse { + pub access_token: String, + pub refresh_token: String, + pub scope: Option, + pub token_type: String, + pub id_token: Option, + pub expires_in: i64, +}