use std::path::Path;
use std::time::Duration;
use reqwest::Client;
use serde::Deserialize;
use super::error::CliError;
use super::pds::PdsClient;
use super::session::SessionFile;
#[derive(Debug, Deserialize)]
struct DidJsonMin {
id: String,
}
pub async fn login(
cairn_server: &str,
pds_url: &str,
handle: &str,
app_password: &str,
cairn_did_override: Option<&str>,
session_path: &Path,
) -> Result<SessionFile, CliError> {
let cairn_service_did = match cairn_did_override {
Some(d) => d.to_string(),
None => fetch_cairn_service_did(cairn_server).await?,
};
let pds = PdsClient::new(pds_url)?;
let resp = pds.create_session(handle, app_password).await?;
let session = SessionFile {
version: crate::cli::session::SESSION_VERSION,
cairn_server_url: cairn_server.to_string(),
cairn_service_did,
pds_url: pds_url.to_string(),
moderator_did: resp.did,
moderator_handle: resp.handle,
access_jwt: resp.access_jwt,
refresh_jwt: resp.refresh_jwt,
};
session.save(session_path)?;
Ok(session)
}
async fn fetch_cairn_service_did(cairn_server: &str) -> Result<String, CliError> {
let url = format!(
"{}/.well-known/did.json",
cairn_server.trim_end_matches('/')
);
let client = Client::builder()
.timeout(Duration::from_secs(15))
.build()
.expect("reqwest build");
let resp = client
.get(&url)
.send()
.await
.map_err(|source| CliError::Http {
url: url.clone(),
source,
})?;
if !resp.status().is_success() {
return Err(CliError::CairnStatus {
url,
status: resp.status().as_u16(),
body: resp.text().await.unwrap_or_default(),
});
}
let bytes = resp.bytes().await.map_err(|source| CliError::Http {
url: url.clone(),
source,
})?;
let doc: DidJsonMin =
serde_json::from_slice(&bytes).map_err(|source| CliError::MalformedResponse {
url: url.clone(),
source,
})?;
Ok(doc.id)
}
pub fn post_login_warning(session: &SessionFile, session_path: &Path) -> String {
format!(
"Logged in as {did}.\n\
Session cached at {path}.\n\
\n\
WARNING: this session file is a moderator credential equivalent\n\
to your PDS app password. Protect it accordingly. Anyone with\n\
read access to this file can act as you at {server} until you\n\
run `cairn logout` or the session expires at the PDS.",
did = session.moderator_did,
path = session_path.display(),
server = session.cairn_server_url,
)
}