Skip to content

Commit

Permalink
Merge pull request #61 from DeterminateSystems/cole/ds-1331-fh-login-…
Browse files Browse the repository at this point in the history
…validate-provided-jwt

Validate JWT
  • Loading branch information
grahamc authored Oct 16, 2023
2 parents 2172476 + 6e56409 commit 37dbc50
Show file tree
Hide file tree
Showing 2 changed files with 50 additions and 16 deletions.
14 changes: 9 additions & 5 deletions src/cli/cmd/login/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -42,11 +42,15 @@ impl LoginSubcommand {
println!();

let token = crate::cli::cmd::init::prompt::Prompt::maybe_string("Paste your token here:");
let token = match token {
let (token, status) = match token {
Some(token) => {
// FIXME: validate that the token is valid?
// or at least validate that it's a... jwt at all lol
token
// This serves as validating that provided token is actually a JWT, and is valid.
let status = crate::cli::cmd::status::get_status_from_auth_token(
self.api_addr.clone(),
&token,
)
.await?;
(token, status)
}
None => {
tracing::error!("Missing token.");
Expand Down Expand Up @@ -111,7 +115,7 @@ impl LoginSubcommand {
}

if !self.skip_status {
crate::cli::cmd::status::get_status(self.api_addr.clone()).await?;
print!("{status}");
}

Ok(())
Expand Down
52 changes: 41 additions & 11 deletions src/cli/cmd/status/mod.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
use std::process::ExitCode;

use clap::Parser;
use color_eyre::eyre::WrapErr;

use super::CommandExecute;

Expand All @@ -16,12 +17,22 @@ pub(crate) struct StatusSubcommand {
}

#[derive(Debug, serde::Deserialize)]
struct TokenStatus {
pub(crate) struct TokenStatus {
gh_name: String,
#[serde(deserialize_with = "i64_to_local_datetime")]
expires_at: chrono::DateTime<chrono::Local>,
}

impl std::fmt::Display for TokenStatus {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
writeln!(f, "Logged in: true")?;
writeln!(f, "GitHub user name: {}", self.gh_name)?;
writeln!(f, "Token expires at: {}", self.expires_at)?;

Ok(())
}
}

fn i64_to_local_datetime<'de, D>(
deserializer: D,
) -> Result<chrono::DateTime<chrono::Local>, D::Error>
Expand All @@ -40,31 +51,50 @@ where
#[async_trait::async_trait]
impl CommandExecute for StatusSubcommand {
async fn execute(self) -> color_eyre::Result<ExitCode> {
get_status(self.api_addr).await?;
let status = get_status_from_auth_file(self.api_addr).await?;
print!("{status}");

Ok(ExitCode::SUCCESS)
}
}

pub(crate) async fn get_status(api_addr: url::Url) -> color_eyre::Result<()> {
pub(crate) async fn get_status_from_auth_file(
api_addr: url::Url,
) -> color_eyre::Result<TokenStatus> {
let auth_token_path = crate::cli::cmd::login::auth_token_path()?;
let token = tokio::fs::read_to_string(auth_token_path).await?;
let token = token.trim();

get_status_from_auth_token(api_addr, token).await
}

pub(crate) async fn get_status_from_auth_token(
api_addr: url::Url,
token: &str,
) -> color_eyre::Result<TokenStatus> {
let mut cli_status = api_addr;
cli_status.set_path("/cli/status");

let token_status: TokenStatus = reqwest::Client::new()
let res = reqwest::Client::new()
.get(cli_status)
.header("Authorization", &format!("Bearer {token}"))
.send()
.await?
.json()
.await?;
.await
.wrap_err("Failed to send request")?;

println!("Logged in: true");
println!("GitHub user name: {}", token_status.gh_name);
println!("Token expires at: {}", token_status.expires_at);
if res.status() == 401 {
return Err(color_eyre::eyre::eyre!(
"The provided token was invalid. Please try again, or contact [email protected] if the problem persists."
));
}

let res = res
.error_for_status()
.wrap_err("Request was unsuccessful")?;
let token_status: TokenStatus = res
.json()
.await
.wrap_err("Failed to get TokenStatus from response (wasn't JSON, or was invalid JSON?)")?;

Ok(())
Ok(token_status)
}

0 comments on commit 37dbc50

Please sign in to comment.