Skip to main content

auths_infra_http/
github_gist.rs

1//! GitHub Gist proof publisher HTTP implementation.
2
3use std::future::Future;
4
5use serde::Deserialize;
6
7use auths_core::ports::platform::{PlatformError, PlatformProofPublisher};
8
9use crate::default_http_client;
10use crate::error::map_reqwest_error;
11
12#[derive(Deserialize)]
13struct GistResponse {
14    html_url: String,
15}
16
17/// HTTP implementation that publishes platform claim proofs as public GitHub Gists.
18///
19/// The Gist persists as a permanent, publicly-verifiable anchor. Anyone can
20/// verify the Ed25519 signature inside the claim using only the DID's public key.
21///
22/// Usage:
23/// ```ignore
24/// let publisher = HttpGistPublisher::new();
25/// let proof_url = publisher.publish_proof(&access_token, &claim_json).await?;
26/// ```
27pub struct HttpGistPublisher {
28    client: reqwest::Client,
29}
30
31impl HttpGistPublisher {
32    /// Create a new publisher with a default HTTP client.
33    pub fn new() -> Self {
34        Self {
35            client: default_http_client(),
36        }
37    }
38}
39
40impl Default for HttpGistPublisher {
41    fn default() -> Self {
42        Self::new()
43    }
44}
45
46impl PlatformProofPublisher for HttpGistPublisher {
47    fn publish_proof(
48        &self,
49        access_token: &str,
50        claim_json: &str,
51    ) -> impl Future<Output = Result<String, PlatformError>> + Send {
52        let client = self.client.clone();
53        let access_token = access_token.to_string();
54        let claim_json = claim_json.to_string();
55        async move {
56            let payload = serde_json::json!({
57                "description": "Auths Identity Proof — cryptographic link between DID and GitHub account",
58                "public": true,
59                "files": {
60                    "auths-proof.json": {
61                        "content": claim_json
62                    }
63                }
64            });
65
66            let resp = client
67                .post("https://api.github.com/gists")
68                .header("Authorization", format!("Bearer {access_token}"))
69                .header("User-Agent", "auths-cli")
70                .header("Accept", "application/vnd.github+json")
71                .json(&payload)
72                .send()
73                .await
74                .map_err(|e| PlatformError::Network(map_reqwest_error(e, "api.github.com")))?;
75
76            if !resp.status().is_success() {
77                let status = resp.status().as_u16();
78                let body = resp.text().await.unwrap_or_default();
79                return Err(PlatformError::Platform {
80                    message: format!("GitHub Gist creation failed (HTTP {status}): {body}"),
81                });
82            }
83
84            let gist: GistResponse = resp.json().await.map_err(|e| PlatformError::Platform {
85                message: format!("failed to parse Gist response: {e}"),
86            })?;
87
88            Ok(gist.html_url)
89        }
90    }
91}