1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
//! Pluggable per-request bearer credentials for the Hotdata Rust SDK.
//!
//! Most callers never touch this module: they hand [`ClientBuilder::api_token`]
//! a long-lived `hd_` API token, it lands on
//! [`Configuration::bearer_access_token`](crate::apis::configuration::Configuration),
//! and every request sends it verbatim.
//!
//! A consumer that owns its own credential lifecycle needs more than a value
//! baked in at construction. The Hotdata CLI, for example, authenticates a user
//! through a PKCE browser login whose access token lives about five minutes and
//! must be refreshed mid-command — a multi-gigabyte `upload_file` whose finalize
//! call lands after the TTL, a long-running query, or a large parallel batch all
//! outlive the credential they started with. For those hosts, install a
//! [`BearerTokenProvider`] on
//! [`Configuration::token_provider`](crate::apis::configuration::Configuration)
//! and the SDK asks it for a bearer once per request instead:
//!
//! ```no_run
//! use hotdata::auth::{async_trait, BearerTokenError, BearerTokenProvider};
//! use hotdata::prelude::*;
//!
//! #[derive(Debug)]
//! struct MySession { /* refresh token, expiry, mutex, ... */ }
//!
//! #[async_trait]
//! impl BearerTokenProvider for MySession {
//! async fn bearer_value(&self) -> Result<String, BearerTokenError> {
//! // Refresh if needed, then hand back a currently valid access token.
//! Ok("eyJ...".to_owned())
//! }
//! }
//!
//! # fn main() -> Result<(), Box<dyn std::error::Error>> {
//! let mut client = Client::builder().api_token("unused").build()?;
//! client.configuration_mut().token_provider = Some(std::sync::Arc::new(MySession {}));
//! # Ok(())
//! # }
//! ```
//!
//! This module is hand-written and regeneration-immune: OpenAPI Generator only
//! rewrites the files it emits, and `auth.rs` is additionally listed in
//! `.openapi-generator-ignore` as belt-and-suspenders.
//!
//! Nothing here exchanges one credential for another. The API-token -> JWT
//! exchange that earlier releases performed against `/v1/auth/jwt` is gone and
//! is not coming back; a provider is purely a hook for a host that already
//! knows how to produce a fresh bearer.
//!
//! [`ClientBuilder::api_token`]: crate::client::ClientBuilder::api_token
/// Re-exported so an implementor does not have to add its own `async-trait`
/// dependency (and cannot pick a version whose desugaring disagrees with ours).
/// Attach it to the `impl` block: `#[hotdata::auth::async_trait]`.
pub use async_trait;
/// Raised when a [`BearerTokenProvider`] cannot produce a bearer token.
///
/// The variants cover what a provider that does its own HTTP typically hits.
/// A provider is free to use whichever fits; [`Malformed`](Self::Malformed)
/// doubles as the catch-all for a failure that isn't transport or status
/// shaped (an expired refresh token, a missing keyring entry, ...).
///
/// Marked `#[non_exhaustive]`: new failure modes may be added in future releases
/// without a breaking change, so downstream `match`es should carry a wildcard arm.
/// A pluggable async source of bearer tokens.
///
/// Installed on the generated `Configuration` as
/// `Option<Arc<dyn BearerTokenProvider>>`; the generated `resolve_bearer_token`
/// method calls [`bearer_value`](BearerTokenProvider::bearer_value) exactly once
/// per request, so a provider can hand back a freshly refreshed credential for
/// every call rather than one captured at `Client` construction.
///
/// Implementors are shared across concurrent requests behind an `Arc`, so
/// `bearer_value` takes `&self` and must be safe to call from several tasks at
/// once (typically a `tokio::sync::Mutex` around the refresh so concurrent
/// callers single-flight instead of stampeding). It is on the hot path of every
/// request, so the common case should be a cheap cache read.