use anyhow::Result;
use serde::{Deserialize, Serialize};
use crate::{client::post_json, url::URLBuilder};
#[derive(Serialize, Clone)]
pub struct CreateSessionRequest {
pub identifier: String,
pub password: String,
#[serde(skip_serializing_if = "Option::is_none", rename = "authFactorToken")]
pub auth_factor_token: Option<String>,
}
impl std::fmt::Debug for CreateSessionRequest {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.debug_struct("CreateSessionRequest")
.field("identifier", &self.identifier)
.field("password", &"[REDACTED]")
.field(
"auth_factor_token",
&self.auth_factor_token.as_ref().map(|_| "[REDACTED]"),
)
.finish()
}
}
#[derive(Deserialize, Clone)]
pub struct AppPasswordSession {
pub did: String,
pub handle: String,
pub email: String,
#[serde(rename = "accessJwt")]
pub access_jwt: String,
#[serde(rename = "refreshJwt")]
pub refresh_jwt: String,
}
impl std::fmt::Debug for AppPasswordSession {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.debug_struct("AppPasswordSession")
.field("did", &self.did)
.field("handle", &self.handle)
.field("email", &self.email)
.field("access_jwt", &"[REDACTED]")
.field("refresh_jwt", &"[REDACTED]")
.finish()
}
}
#[derive(Deserialize, Clone)]
pub struct RefreshSessionResponse {
pub did: String,
pub handle: String,
#[serde(rename = "accessJwt")]
pub access_jwt: String,
#[serde(rename = "refreshJwt")]
pub refresh_jwt: String,
#[serde(skip_serializing_if = "Option::is_none")]
pub active: Option<bool>,
#[serde(skip_serializing_if = "Option::is_none")]
pub status: Option<String>,
}
impl std::fmt::Debug for RefreshSessionResponse {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.debug_struct("RefreshSessionResponse")
.field("did", &self.did)
.field("handle", &self.handle)
.field("access_jwt", &"[REDACTED]")
.field("refresh_jwt", &"[REDACTED]")
.field("active", &self.active)
.field("status", &self.status)
.finish()
}
}
#[derive(Deserialize, Clone)]
pub struct AppPasswordResponse {
pub name: String,
pub password: String,
#[serde(rename = "createdAt")]
pub created_at: String,
}
impl std::fmt::Debug for AppPasswordResponse {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.debug_struct("AppPasswordResponse")
.field("name", &self.name)
.field("password", &"[REDACTED]")
.field("created_at", &self.created_at)
.finish()
}
}
pub async fn create_session(
http_client: &reqwest::Client,
base_url: &str,
identifier: &str,
password: &str,
auth_factor_token: Option<&str>,
) -> Result<AppPasswordSession> {
let mut url_builder = URLBuilder::new(base_url);
url_builder.path("/xrpc/com.atproto.server.createSession");
let url = url_builder.build();
let request = CreateSessionRequest {
identifier: identifier.to_string(),
password: password.to_string(),
auth_factor_token: auth_factor_token.map(|s| s.to_string()),
};
let value = serde_json::to_value(request)?;
post_json(http_client, &url, value)
.await
.and_then(|value| serde_json::from_value(value).map_err(|err| err.into()))
}
pub async fn refresh_session(
http_client: &reqwest::Client,
base_url: &str,
refresh_token: &str,
) -> Result<RefreshSessionResponse> {
let mut url_builder = URLBuilder::new(base_url);
url_builder.path("/xrpc/com.atproto.server.refreshSession");
let url = url_builder.build();
let mut headers = reqwest::header::HeaderMap::new();
headers.insert(
reqwest::header::AUTHORIZATION,
reqwest::header::HeaderValue::from_str(&format!("Bearer {}", refresh_token))?,
);
let response = http_client.post(&url).headers(headers).send().await?;
let value = response.json::<serde_json::Value>().await?;
serde_json::from_value(value).map_err(|err| err.into())
}
pub async fn create_app_password(
http_client: &reqwest::Client,
base_url: &str,
access_token: &str,
name: &str,
) -> Result<AppPasswordResponse> {
let mut url_builder = URLBuilder::new(base_url);
url_builder.path("/xrpc/com.atproto.server.createAppPassword");
let url = url_builder.build();
let request_body = serde_json::json!({
"name": name
});
let mut headers = reqwest::header::HeaderMap::new();
headers.insert(
reqwest::header::AUTHORIZATION,
reqwest::header::HeaderValue::from_str(&format!("Bearer {}", access_token))?,
);
let response = http_client
.post(&url)
.headers(headers)
.json(&request_body)
.send()
.await?;
let value = response.json::<serde_json::Value>().await?;
serde_json::from_value(value).map_err(|err| err.into())
}