pub mod graphql;
use crate::auth::login::graphql::cli_access_tokens::{cli_access_tokens, CliAccessTokens};
use crate::auth::login::graphql::user_by_pk::{user_by_pk, UserByPk};
use crate::common::{
authorization_headers::authorization_headers, env_var::env_var,
execute_graphql_request::execute_graphql_request, fetch_user_id::fetch_user_id,
keyring::keyring, print_formatted_error::print_formatted_error,
};
use clap::Args;
use dialoguer::{theme::ColorfulTheme, Input};
use graphql_client::GraphQLQuery;
use reqwest::{header, Client};
use std::io;
use termimad::crossterm::style::Stylize;
#[derive(Args, Debug)]
pub struct AuthLoginArgs {
#[clap(
short,
long,
help = "If true - returns the access token to the console, if false or not specified - saves the access token to the keychain"
)]
console_output: Option<bool>,
}
pub fn login(args: &AuthLoginArgs) {
let webapp_cli_auth_url = env_var("WEBAPP_CLI_AUTH_URL");
let client = Client::new();
let is_console_output = match &args.console_output {
Some(token) => token.clone(),
_ => false,
};
println!(
"Press `Enter` to open tryzero.com in your browser or type `q` to quit.\n{}",
&webapp_cli_auth_url
);
loop {
let mut input = String::new();
io::stdin()
.read_line(&mut input)
.expect("Failed to read line");
input = input.trim().to_string();
match input.as_str() {
"" => {
match open::that(&webapp_cli_auth_url) {
Ok(_) => (),
Err(_) => print_formatted_error("Failed to open browser"),
};
let code = match Input::with_theme(&ColorfulTheme::default())
.with_prompt("Type the code shown in the browser:")
.validate_with(|input: &String| -> Result<(), &str> {
if input.trim().chars().count() == 32 {
Ok(())
} else {
Err("The code must be 32 characters long.")
}
})
.interact()
{
Ok(value) => value.trim().to_string(),
Err(_) => {
print_formatted_error("Failed to read the code.");
std::process::exit(1);
}
};
let access_token = execute_graphql_request::<cli_access_tokens::Variables, cli_access_tokens::ResponseData>(
header::HeaderMap::new(),
CliAccessTokens::build_query,
&client,
"Failed to retrieve access token, please check if the code is correct and try again.",
cli_access_tokens::Variables::new(code),
).cli_access_tokens.access_token;
let user_info_error_message = "Failed to retrieve user info";
let user_info_response =
execute_graphql_request::<user_by_pk::Variables, user_by_pk::ResponseData>(
authorization_headers(&access_token),
UserByPk::build_query,
&client,
user_info_error_message,
user_by_pk::Variables {
id: fetch_user_id(&access_token),
},
)
.user_by_pk;
let user_info = match user_info_response {
Some(user_info) => user_info,
None => {
print_formatted_error(user_info_error_message);
std::process::exit(1);
}
};
match is_console_output {
true => {
println!("{} You are authorized as {}", "✓".green(), user_info.name);
println!(
"{}",
"Be careful! Do not share your access token with anyone.".yellow()
);
println!("{} {}", "Your access token is:".bold(), access_token);
std::process::exit(0);
}
false => {
keyring::set("access_token", &access_token);
println!("{} You are authorized as {}", "✓".green(), user_info.name);
std::process::exit(0);
}
}
}
"q" | "Q" => {
std::process::exit(0);
}
_ => {
println!("You've entered: {}. Press only ENTER to continue.", input);
}
}
}
}