Skip to main content

cratestack_client_rust/
auth.rs

1use crate::error::ClientError;
2
3#[derive(Debug, Clone, PartialEq, Eq)]
4pub struct AuthorizationRequest {
5    pub method: String,
6    pub path: String,
7    pub canonical_query: Option<String>,
8    pub content_type: Option<String>,
9    pub body: Vec<u8>,
10    pub canonical_request: String,
11}
12
13/// Async by design (issue #453): real credential providers — an OAuth2
14/// client-credentials token with a refresh-on-expiry cache, for instance —
15/// need to make an HTTP call on a cache miss, which a sync `authorize`
16/// can't do without `block_on` (panics/deadlocks depending on the runtime)
17/// or a pre-fetch-and-stash workaround that reintroduces the expiry race
18/// the cache existed to avoid. `#[async_trait]` (rather than a bare
19/// `async fn` in the trait) keeps `Arc<dyn RequestAuthorizer>` —
20/// `CratestackClient::with_request_authorizer` stores the authorizer as
21/// a trait object — dyn-compatible; native AFIT would drop that. Same
22/// convention other dyn-dispatched async hook traits already use elsewhere
23/// in the workspace (e.g. `cratestack_core::audit::AuditSink`).
24///
25/// Target-split (cratestack#1104 follow-up): natively the trait is
26/// `Send + Sync` with a `Send` future, exactly as before. On
27/// `wasm32-unknown-unknown` it is neither, because a browser `fetch` future
28/// is never `Send`, so an authorizer that makes a request of its own (the
29/// token refresh above) could not satisfy a `Send` bound there. An
30/// implementation that builds for both targets uses the same split:
31/// `#[cfg_attr(not(target_arch = "wasm32"), async_trait::async_trait)]` and
32/// `#[cfg_attr(target_arch = "wasm32", async_trait::async_trait(?Send))]`.
33#[cfg(not(target_arch = "wasm32"))]
34#[async_trait::async_trait]
35pub trait RequestAuthorizer: Send + Sync {
36    async fn authorize(
37        &self,
38        request: &AuthorizationRequest,
39    ) -> Result<Vec<(String, String)>, ClientError>;
40}
41
42/// The wasm32 half of [`RequestAuthorizer`]: no `Send`/`Sync`, and a
43/// future that need not be `Send`, so `authorize` can await a `fetch`.
44#[cfg(target_arch = "wasm32")]
45#[async_trait::async_trait(?Send)]
46pub trait RequestAuthorizer {
47    async fn authorize(
48        &self,
49        request: &AuthorizationRequest,
50    ) -> Result<Vec<(String, String)>, ClientError>;
51}