use pubky_common::{
auth::{grant::GrantClaims, grant_session_responses::GrantSessionResponse},
crypto::PublicKey,
};
use reqwest::Method;
use super::{
credential::{GrantCredential, sign_pop_for_grant},
pop_signer::GrantPopSigner,
};
use crate::actors::storage::resource::resolve_pubky;
use crate::errors::{RequestError, Result};
use crate::util::check_http_status;
use crate::{PubkyHttpClient, cross_log};
pub(crate) async fn credential_from_grant_exchange(
client: &PubkyHttpClient,
grant_jws: String,
grant_claims: GrantClaims,
client_signer: GrantPopSigner,
homeserver_pubkey: PublicKey,
) -> Result<GrantCredential> {
cross_log!(
info,
"Exchanging grant for grant credential (user={}, hs={})",
grant_claims.iss.z32(),
homeserver_pubkey.z32()
);
let response = post_grant_session(
client,
&grant_jws,
&grant_claims,
&client_signer,
&homeserver_pubkey,
)
.await?;
Ok(GrantCredential::from_response(
response,
grant_jws,
grant_claims,
client_signer,
homeserver_pubkey,
))
}
pub(crate) async fn signup_account_from_grant(
client: &PubkyHttpClient,
grant_jws: &str,
grant_claims: &GrantClaims,
client_signer: &GrantPopSigner,
homeserver_pk: &PublicKey,
signup_token: Option<&str>,
) -> Result<()> {
let pop_jws = sign_pop_for_grant(client_signer, homeserver_pk, &grant_claims.jti).await?;
let body = serde_json::json!({ "grant": grant_jws, "pop": pop_jws });
let mut url = url::Url::parse(&format!(
"https://{}/auth/grant/signup",
homeserver_pk.z32()
))
.map_err(|e| RequestError::Validation {
message: format!("invalid signup url: {e}"),
})?;
if let Some(token) = signup_token {
url.query_pairs_mut().append_pair("signup_token", token);
}
let resp = client
.cross_request(Method::POST, url)
.await?
.json(&body)
.send()
.await?;
check_http_status(resp).await?;
Ok(())
}
async fn post_grant_session(
client: &PubkyHttpClient,
grant_jws: &str,
grant_claims: &GrantClaims,
client_signer: &GrantPopSigner,
homeserver_pk: &PublicKey,
) -> Result<GrantSessionResponse> {
let pop_jws = sign_pop_for_grant(client_signer, homeserver_pk, &grant_claims.jti).await?;
let body = serde_json::json!({ "grant": grant_jws, "pop": pop_jws });
let url = format!("pubky://{}/auth/grant/session", grant_claims.iss.z32());
let resolved = resolve_pubky(&url)?;
let resp = client
.cross_request(Method::POST, resolved)
.await?
.json(&body)
.send()
.await?;
let resp = check_http_status(resp).await?;
resp.json().await.map_err(|e| {
RequestError::DecodeJson {
message: format!("decoding grant session response: {e}"),
}
.into()
})
}