works now
This commit is contained in:
1
.gitignore
vendored
1
.gitignore
vendored
@@ -1,3 +1,4 @@
|
||||
config.json
|
||||
.idea/
|
||||
GIT_IGNORE.txt
|
||||
# ---> macOS
|
||||
|
||||
101
src/main.rs
101
src/main.rs
@@ -2,11 +2,15 @@ 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};
|
||||
use rust_util::{debugging, failure, information, opt_result, opt_value_result, success, XResult};
|
||||
use rust_util::util_file::read_file_content;
|
||||
use rust_util::util_msg::{clear_lastline, MessageType, print_lastline};
|
||||
use tokio::time::sleep;
|
||||
|
||||
use crate::oidc::{DeviceAuthorizationResponse, ErrorResponse, OpenIdClientConfiguration, OpenIdConfiguration, TokenResponse};
|
||||
|
||||
mod oidc;
|
||||
|
||||
@@ -24,9 +28,10 @@ async fn main() -> XResult<()> {
|
||||
.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("hide-qr").long("hide-qr").help("Hide QR"))
|
||||
.arg(Arg::new("skip-certificate-check").short('k').long("skip-certificate-check").help("Skip certificate check"))
|
||||
.arg(Arg::new("config").short('c').long("config").takes_value(true).help("Config file"))
|
||||
.get_matches();
|
||||
|
||||
if matches.is_present("verbose") {
|
||||
@@ -36,7 +41,14 @@ async fn main() -> XResult<()> {
|
||||
information!("{} v{}", NAME, VERSION);
|
||||
exit(1);
|
||||
}
|
||||
let issuer = opt_value_result!(matches.value_of("issuer"), "Issuer is required");
|
||||
|
||||
let config = opt_value_result!(matches.value_of("config"), "Config file is required");
|
||||
let config_content = read_file_content(config)?;
|
||||
|
||||
let openid_client_configuration: OpenIdClientConfiguration =
|
||||
opt_result!( serde_json::from_str(&config_content), "Parse config failed: {}");
|
||||
|
||||
let issuer = &openid_client_configuration.issuer;
|
||||
let openid_configuration_url = format!("{}/.well-known/openid-configuration", issuer);
|
||||
debugging!("Open id configuration url: {}", openid_configuration_url);
|
||||
|
||||
@@ -47,7 +59,7 @@ async fn main() -> XResult<()> {
|
||||
let proxy = opt_result!( Proxy::all(proxy), "Parse proxy: {} failed: {}", proxy);
|
||||
client_builder = client_builder.proxy(proxy);
|
||||
}
|
||||
if (matches.is_present("skip-certificate-check")) {
|
||||
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: {}");
|
||||
@@ -71,13 +83,6 @@ async fn main() -> XResult<()> {
|
||||
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
|
||||
//{
|
||||
@@ -87,12 +92,30 @@ async fn main() -> XResult<()> {
|
||||
// "interval": 5,
|
||||
// "verification_url": "https://www.google.com/device"
|
||||
// }
|
||||
debugging!("Get {}", device_authorization_endpoint);
|
||||
let mut params = HashMap::new();
|
||||
params.insert("client_id", &openid_client_configuration.issuer);
|
||||
params.insert("client_id", &openid_client_configuration.client_id);
|
||||
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.
|
||||
let device_authorization_result = opt_result!(client.post(&device_authorization_endpoint)
|
||||
.form(¶ms).send().await, "Get device authorization: {}, failed: {}", device_authorization_endpoint);
|
||||
let device_authorization = opt_result!(
|
||||
device_authorization_result.json::<DeviceAuthorizationResponse>().await, "Parse device authorization failed: {}");
|
||||
|
||||
let hide_qr = matches.is_present("hide-qr");
|
||||
let input = &device_authorization.get_complete_url();
|
||||
if !hide_qr {
|
||||
let qr_url = format!("{}{}", "https://hatter.ink/qr/qr_terminal.action?qr=", urlencoding::encode(input));
|
||||
let qr_text = ClientBuilder::new().timeout(Duration::from_secs(10))
|
||||
.connect_timeout(Duration::from_secs(6))
|
||||
.danger_accept_invalid_certs(true)
|
||||
.build()?
|
||||
.get(qr_url)
|
||||
.send().await?
|
||||
.text().await?;
|
||||
print!("{}", qr_text);
|
||||
}
|
||||
success!("User code url: {}", input);
|
||||
success!("Get device authorization success: {}", serde_json::to_string_pretty(&device_authorization).unwrap());
|
||||
|
||||
// xh POST https://oauth2.googleapis.com/token
|
||||
// client_id==364023986785-o5mqsqd1ej2d1vmvcqai108b7m7v7vc9.apps.googleusercontent.com
|
||||
@@ -107,6 +130,50 @@ async fn main() -> XResult<()> {
|
||||
// "token_type": "Bearer",
|
||||
// "id_token": "***"
|
||||
// }
|
||||
let grant_type_device_code = "urn:ietf:params:oauth:grant-type:device_code".to_string();
|
||||
debugging!("Get {}", token_endpoint);
|
||||
for i in 0..1000 {
|
||||
print_lastline(&format!("@{}", i));
|
||||
sleep(Duration::from_secs(device_authorization.interval as u64)).await;
|
||||
let mut params = HashMap::new();
|
||||
params.insert("grant_type", &grant_type_device_code);
|
||||
params.insert("client_id", &openid_client_configuration.client_id);
|
||||
params.insert("client_secret", &openid_client_configuration.client_secret);
|
||||
params.insert("device_code", &device_authorization.device_code);
|
||||
|
||||
let token_response_result = opt_result!(
|
||||
client.post(&token_endpoint).form(¶ms).send().await, "Post url: {}, failed: {}", token_endpoint);
|
||||
|
||||
let token_response = opt_result!(token_response_result.text().await, "Get token failed: {}");
|
||||
debugging!("Get token response: {}", token_response);
|
||||
|
||||
let try_parse_error: serde_json::error::Result<ErrorResponse> = serde_json::from_str(&token_response);
|
||||
if let Ok(error) = try_parse_error {
|
||||
let err = &error.error;
|
||||
if err == "authorization_pending" {
|
||||
// JUST FINE
|
||||
} else if err == "slow_down" {
|
||||
sleep(Duration::from_secs(5)).await;
|
||||
} else {
|
||||
clear_lastline();
|
||||
failure!("Unknown error: {}, description: {}",
|
||||
err, error.error_description.unwrap_or_else(|| "<none>".to_string()));
|
||||
break;
|
||||
}
|
||||
continue;
|
||||
}
|
||||
|
||||
let try_parse_token: serde_json::error::Result<TokenResponse> = serde_json::from_str(&token_response);
|
||||
if let Ok(token) = try_parse_token {
|
||||
clear_lastline();
|
||||
success!("Get token: {}", serde_json::to_string_pretty(&token).unwrap());
|
||||
break;
|
||||
}
|
||||
|
||||
clear_lastline();
|
||||
failure!("Get token failed, raw output: {}", token_response);
|
||||
break;
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
18
src/oidc.rs
18
src/oidc.rs
@@ -30,7 +30,23 @@ pub struct DeviceAuthorizationResponse {
|
||||
pub user_code: String,
|
||||
pub expires_in: i64,
|
||||
pub interval: i64,
|
||||
pub verification_url: String,
|
||||
pub verification_url: Option<String>,
|
||||
pub verification_uri: Option<String>,
|
||||
pub verification_uri_complete: Option<String>,
|
||||
}
|
||||
|
||||
impl DeviceAuthorizationResponse {
|
||||
pub fn get_complete_url(&self) -> String {
|
||||
let mut s = String::new();
|
||||
if let Some(u) = &self.verification_uri_complete {
|
||||
s.push_str(u);
|
||||
} else if let Some(u) = self.verification_uri.as_ref().or(self.verification_url.as_ref()) {
|
||||
s.push_str(u);
|
||||
s.push_str("?user_code=");
|
||||
s.push_str(&self.user_code);
|
||||
}
|
||||
return s;
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, Serialize, Deserialize)]
|
||||
|
||||
Reference in New Issue
Block a user