Skip to main content

polyester/services/
api_keys.rs

1//! API keys service (Go `services/api_keys.go` parity).
2
3use super::ServiceContext;
4use super::scope;
5use super::unary;
6use crate::auth;
7use crate::codecs::decode::{api_key_from_get_proto, api_keys_list_from_proto};
8use crate::connect::auth::v1::ApiKeyServiceClient;
9use crate::errors::Result;
10use crate::models::{ApiKeySummary, ApiKeysList, Ed25519Keypair};
11use crate::proto::auth::v1::{GetApiKeyRequest, ListApiKeysRequest};
12
13#[derive(Clone)]
14pub struct ApiKeysService {
15    ctx: ServiceContext,
16}
17
18impl ApiKeysService {
19    pub fn new(ctx: ServiceContext) -> Self {
20        Self { ctx }
21    }
22
23    pub(crate) fn connect_client(&self) -> ApiKeyServiceClient<crate::transport::SharedTransport> {
24        ApiKeyServiceClient::new(
25            self.ctx.factory.transport(),
26            self.ctx.factory.connect_config(),
27        )
28    }
29
30    pub async fn list(&self, subaccount_id: Option<u64>) -> Result<ApiKeysList> {
31        let req = ListApiKeysRequest {
32            subaccount_id: scope::optional_subaccount(&self.ctx, subaccount_id)?,
33            ..Default::default()
34        };
35        let client = self.connect_client();
36        let resp = unary::await_auth(
37            &self.ctx.factory,
38            "/auth.v1.ApiKeyService/ListApiKeys",
39            req,
40            |req, opts| client.list_api_keys_with_options(req, opts),
41        )
42        .await?
43        .into_owned();
44        Ok(api_keys_list_from_proto(&resp))
45    }
46
47    pub async fn get(&self, key_id: &str) -> Result<Option<ApiKeySummary>> {
48        let client = self.connect_client();
49        let resp = unary::await_auth(
50            &self.ctx.factory,
51            "/auth.v1.ApiKeyService/GetApiKey",
52            GetApiKeyRequest {
53                key_id: key_id.to_owned(),
54                ..Default::default()
55            },
56            |req, opts| client.get_api_key_with_options(req, opts),
57        )
58        .await?
59        .into_owned();
60        Ok(api_key_from_get_proto(&resp))
61    }
62
63    /// Generate a local Ed25519 keypair for API key creation (secret never sent to API).
64    pub fn generate_keypair(&self) -> Ed25519Keypair {
65        let (secret_key_hex, public_key_hex) = auth::generate_ed25519_keypair();
66        let public_key = hex::decode(&public_key_hex).unwrap_or_default();
67        let secret_key = hex::decode(&secret_key_hex).unwrap_or_default();
68        Ed25519Keypair {
69            public_key_hex,
70            secret_key_hex,
71            public_key,
72            secret_key,
73        }
74    }
75
76    /// Subscribe to private API key updates (requires `realtime` feature).
77    pub async fn subscribe(
78        &self,
79        account_id: Option<&str>,
80    ) -> Result<crate::realtime::TypedSubscription<ApiKeySummary>> {
81        let account = scope::resolve_account_id(&self.ctx, account_id)?;
82        let channel = format!("private:auth:api-keys:{account}:proto");
83        self.ctx
84            .realtime
85            .subscribe_proto(&channel, crate::codecs::decode::api_key_from_bytes)
86            .await
87    }
88}