use std::sync::Arc;
use serde::{Deserialize, Serialize};
use tracing::{debug, info};
use crate::{Result, prelude::*};
use snafu::prelude::*;
#[derive(Debug, Serialize)]
struct CreateChallengeRequest {
pub app_name: String,
}
#[derive(Debug, Deserialize)]
struct CreateChallengeResponse {
pub challenge_id: String,
}
#[derive(Debug, Serialize)]
struct CreateApiKeyRequest {
pub challenge_id: String,
pub code: String,
}
#[derive(Debug, Deserialize)]
struct CreateApiKeyResponse {
pub api_key: String,
}
impl AnytypeClient {
pub async fn create_auth_challenge(&self) -> Result<String> {
let request = CreateChallengeRequest {
app_name: self.config.app_name.clone(),
};
debug!("creating auth challenge ...");
let response: CreateChallengeResponse = self
.client
.post_unauthenticated("/v1/auth/challenges", &request)
.await?;
debug!("challenge received: {}", &response.challenge_id);
Ok(response.challenge_id)
}
pub async fn create_api_key(
&self,
challenge_id: &str,
code: impl Into<String>,
) -> Result<SecretApiKey> {
let request = CreateApiKeyRequest {
challenge_id: challenge_id.to_string(),
code: code.into(),
};
let response: CreateApiKeyResponse = self
.client
.post_unauthenticated("/v1/auth/api_keys", &request)
.await?;
Ok(SecretApiKey::new(response.api_key))
}
pub async fn authenticate_interactive<F>(&self, get_code: F, force_reauth: bool) -> Result<()>
where
F: FnOnce(&str) -> Result<String>,
{
if !force_reauth {
if self.client.has_key() {
debug!("client already has key - no need to re-authenticate");
return Ok(());
}
if self.get_key_store().is_configured()
&& let Ok(true) = self.load_key(force_reauth)
{
return Ok(());
}
}
debug!("beginning interactive authentication");
let challenge_id: String = self.create_auth_challenge().await?;
let code = get_code(&challenge_id)?;
let api_key = self.create_api_key(&challenge_id, code).await?;
self.set_api_key(&api_key);
if self.keystore.is_configured() {
self.keystore.save_key(&api_key)?;
} else {
debug!(
"authentication completed, but key not persisted because no keystore is configured."
);
}
Ok(())
}
pub fn set_key_store<K: KeyStore + 'static>(mut self, keystore: K) -> Self {
self.keystore = Arc::new(Box::new(keystore));
self
}
pub fn env_key_store(self) -> Result<Self> {
let var = crate::config::ANYTYPE_KEY_FILE_ENV;
let path = std::env::var(var).context(FileEnvSnafu { var })?;
let client = self.set_key_store(KeyStoreFile::from_path(path)?);
client.load_key(false)?;
Ok(client)
}
pub fn get_key_store(&self) -> Arc<Box<dyn KeyStore>> {
self.keystore.clone()
}
pub fn clear_api_key(&self) {
self.client.clear_api_key();
}
pub fn set_api_key(&self, key: &SecretApiKey) {
self.client.set_api_key(key);
}
pub fn logout(&self) -> Result<()> {
self.clear_api_key();
if self.keystore.is_configured() {
self.keystore.remove_key()?;
}
Ok(())
}
pub fn is_authenticated(&self) -> bool {
self.client.has_key()
}
pub fn load_key(&self, force_reload: bool) -> Result<bool> {
if !force_reload && self.is_authenticated() {
return Ok(true);
}
if !self.keystore.is_configured() {
return Err(AnytypeError::NoKeyStore);
}
let key = self.keystore.load_key()?;
if let Some(ref api_key) = key {
self.set_api_key(api_key);
} else {
info!("key store: key not found");
}
Ok(key.is_some())
}
pub fn save_key(&self) -> Result<()> {
if !self.keystore.is_configured() {
return Err(AnytypeError::NoKeyStore);
}
match self.client.get_api_key() {
Some(key) => self.keystore.save_key(&key).map_err(AnytypeError::from),
None => Err(AnytypeError::Auth {
message: "No API key set on client".to_string(),
}),
}
}
}