Skip to main content

authplane_sdk/
client_builder.rs

1//! Builder for [`crate::AuthplaneClient`] that exposes the runtime knobs
2//! [`crate::AuthplaneClient::create`] configures: cache refresh
3//! intervals, circuit-breaker thresholds, token-cache buffer, optional
4//! outbound DPoP provider, and a metadata `on_change` hook.
5//!
6//! Use [`AuthplaneClient::builder`] to obtain a builder pre-seeded with
7//! the SDK defaults.
8
9use std::sync::Arc;
10
11use crate::auth_provider::AuthProvider;
12use crate::cache::MetadataChangeCallback;
13use crate::circuit_breaker::CircuitBreaker;
14use crate::client::{AuthplaneClient, ClientRuntimeConfig};
15use crate::dpop_provider::DpopProvider;
16use crate::{AuthplaneError, FetchSettings};
17
18/// Builder for [`AuthplaneClient`].
19///
20/// Every knob defaults to the value [`AuthplaneClient::create`] uses, so a
21/// builder built without further configuration produces a client identical to
22/// one obtained from that constructor.
23#[derive(Clone)]
24pub struct AuthplaneClientBuilder {
25    issuer: String,
26    fetch_settings: FetchSettings,
27    jwks_refresh_seconds: u64,
28    metadata_refresh_seconds: u64,
29    cache_ttl_buffer_seconds: f64,
30    default_token_ttl_seconds: f64,
31    circuit_breaker_threshold: u32,
32    circuit_breaker_cooldown_seconds: f64,
33    dpop_provider: Option<Arc<DpopProvider>>,
34    auth_provider: Option<Arc<dyn AuthProvider>>,
35    on_metadata_change: Option<MetadataChangeCallback>,
36}
37
38impl AuthplaneClientBuilder {
39    /// Default JWKS refresh interval.
40    pub const DEFAULT_JWKS_REFRESH_SECONDS: u64 = 300;
41    /// Default AS-metadata refresh interval.
42    pub const DEFAULT_METADATA_REFRESH_SECONDS: u64 = 3600;
43
44    /// Build a builder seeded with SDK defaults.
45    pub fn new(issuer: impl Into<String>) -> Self {
46        Self {
47            issuer: issuer.into(),
48            fetch_settings: FetchSettings::default(),
49            jwks_refresh_seconds: Self::DEFAULT_JWKS_REFRESH_SECONDS,
50            metadata_refresh_seconds: Self::DEFAULT_METADATA_REFRESH_SECONDS,
51            cache_ttl_buffer_seconds: crate::cache::TokenCache::DEFAULT_TTL_BUFFER_SECONDS,
52            default_token_ttl_seconds: crate::cache::TokenCache::DEFAULT_TTL_SECONDS,
53            circuit_breaker_threshold: CircuitBreaker::DEFAULT_THRESHOLD,
54            circuit_breaker_cooldown_seconds: CircuitBreaker::DEFAULT_COOLDOWN_SECONDS,
55            dpop_provider: None,
56            auth_provider: None,
57            on_metadata_change: None,
58        }
59    }
60
61    /// Outbound HTTP / SSRF policy. Applied to both AS metadata and JWKS
62    /// document fetches — RFC 8414 / RFC 7517 share the same threat profile,
63    /// so a single setting governs both.
64    pub fn with_fetch_settings(mut self, fetch_settings: FetchSettings) -> Self {
65        self.fetch_settings = fetch_settings;
66        self
67    }
68
69    /// Override the JWKS refresh interval (seconds).
70    pub fn with_jwks_refresh_seconds(mut self, seconds: u64) -> Self {
71        self.jwks_refresh_seconds = seconds.max(1);
72        self
73    }
74
75    /// Override the AS-metadata refresh interval (seconds).
76    pub fn with_metadata_refresh_seconds(mut self, seconds: u64) -> Self {
77        self.metadata_refresh_seconds = seconds.max(1);
78        self
79    }
80
81    /// Override the token-cache TTL buffer.
82    pub fn with_token_cache_ttl_buffer_seconds(mut self, buffer_seconds: f64) -> Self {
83        self.cache_ttl_buffer_seconds = buffer_seconds.max(0.0);
84        self
85    }
86
87    /// Override the fallback token TTL used when the AS does not return one.
88    pub fn with_default_token_ttl_seconds(mut self, seconds: f64) -> Self {
89        self.default_token_ttl_seconds = seconds.max(0.0);
90        self
91    }
92
93    /// Override the circuit-breaker threshold.
94    pub fn with_circuit_breaker_threshold(mut self, threshold: u32) -> Self {
95        self.circuit_breaker_threshold = threshold.max(1);
96        self
97    }
98
99    /// Override the circuit-breaker cooldown (seconds).
100    pub fn with_circuit_breaker_cooldown_seconds(mut self, cooldown_seconds: f64) -> Self {
101        self.circuit_breaker_cooldown_seconds = cooldown_seconds.max(0.0);
102        self
103    }
104
105    /// Store a default auth provider (e.g., [`ClientCredentialsProvider`])
106    /// so callers can use methods without passing credentials each time.
107    ///
108    /// [`ClientCredentialsProvider`]: crate::auth_provider::ClientCredentialsProvider
109    pub fn with_auth(mut self, provider: Arc<dyn AuthProvider>) -> Self {
110        self.auth_provider = Some(provider);
111        self
112    }
113
114    /// Wire an outbound DPoP provider so `client_credentials` /
115    /// `exchange_token` / `introspect` / `revoke` use a shared signing
116    /// key + nonce store when callers pass `Some(&proof_options)`.
117    pub fn with_dpop_provider(mut self, provider: Arc<DpopProvider>) -> Self {
118        self.dpop_provider = Some(provider);
119        self
120    }
121
122    /// Register an async callback fired when AS metadata changes (e.g.
123    /// `jwks_uri` rotation).
124    pub fn with_metadata_change_callback(mut self, callback: MetadataChangeCallback) -> Self {
125        self.on_metadata_change = Some(callback);
126        self
127    }
128
129    /// Build and initialize the [`AuthplaneClient`].
130    ///
131    /// Performs AS metadata discovery, validates the issuer, primes the
132    /// JWKS cache, and returns a fully wired client ready to use.
133    pub async fn build(self) -> Result<AuthplaneClient, AuthplaneError> {
134        let runtime = ClientRuntimeConfig {
135            jwks_refresh_seconds: self.jwks_refresh_seconds,
136            metadata_refresh_seconds: self.metadata_refresh_seconds,
137            cache_ttl_buffer_seconds: self.cache_ttl_buffer_seconds,
138            default_token_ttl_seconds: self.default_token_ttl_seconds,
139            circuit_breaker_threshold: self.circuit_breaker_threshold,
140            circuit_breaker_cooldown_seconds: self.circuit_breaker_cooldown_seconds,
141            dpop_provider: self.dpop_provider,
142            auth_provider: self.auth_provider,
143            on_metadata_change: self.on_metadata_change,
144        };
145        AuthplaneClient::build(self.issuer, self.fetch_settings, runtime).await
146    }
147}