oauth2_broker/
flows.rs

1//! High-level flow orchestrators powered by the broker facade.
2
3pub mod auth_code_pkce;
4pub mod common;
5pub mod refresh;
6
7mod client_credentials;
8
9pub use auth_code_pkce::*;
10pub use common::*;
11pub use refresh::*;
12
13// self
14use crate::{
15	_prelude::*,
16	http::TokenHttpClient,
17	oauth::TransportErrorMapper,
18	provider::{ProviderDescriptor, ProviderStrategy},
19	store::{BrokerStore, StoreKey},
20};
21#[cfg(feature = "reqwest")]
22use crate::{http::ReqwestHttpClient, oauth::ReqwestTransportErrorMapper};
23
24#[cfg(feature = "reqwest")]
25/// Broker specialized for the crate's default reqwest transport stack.
26pub type ReqwestBroker = Broker<ReqwestHttpClient, ReqwestTransportErrorMapper>;
27
28/// Coordinates OAuth 2.0 flows against a single provider descriptor.
29///
30/// The broker owns the HTTP client, token store, provider descriptor, and strategy
31/// references so individual flow implementations can focus on grant-specific logic
32/// (state + PKCE generation, code exchanges, refresh rotations, etc.). Client
33/// credentials are stored alongside the descriptor so client-auth methods can be
34/// applied consistently across token endpoints.
35#[derive(Clone)]
36pub struct Broker<C, M>
37where
38	C: ?Sized + TokenHttpClient,
39	M: ?Sized + TransportErrorMapper<C::TransportError>,
40{
41	/// HTTP client wrapper used for every outbound provider request.
42	pub http_client: Arc<C>,
43	/// Mapper applied to transport-layer errors before surfacing them to callers.
44	pub transport_mapper: Arc<M>,
45	/// Token store implementation that persists issued secrets.
46	pub store: Arc<dyn BrokerStore>,
47	/// Provider descriptor that defines OAuth endpoints and quirks.
48	pub descriptor: ProviderDescriptor,
49	/// Strategy responsible for provider-specific token request adjustments.
50	pub strategy: Arc<dyn ProviderStrategy>,
51	/// OAuth 2.0 client identifier used in every grant.
52	pub client_id: String,
53	/// Optional client secret for confidential authentication methods.
54	pub client_secret: Option<String>,
55	/// Shared metrics recorder for refresh flow outcomes.
56	pub refresh_metrics: Arc<RefreshMetrics>,
57	flow_guards: Arc<Mutex<HashMap<StoreKey, Arc<AsyncMutex<()>>>>>,
58}
59impl<C, M> Broker<C, M>
60where
61	C: ?Sized + TokenHttpClient,
62	M: ?Sized + TransportErrorMapper<C::TransportError>,
63{
64	/// Creates a broker that reuses the caller-provided transport + mapper pair.
65	pub fn with_http_client(
66		store: Arc<dyn BrokerStore>,
67		descriptor: ProviderDescriptor,
68		strategy: Arc<dyn ProviderStrategy>,
69		client_id: impl Into<String>,
70		http_client: impl Into<Arc<C>>,
71		mapper: impl Into<Arc<M>>,
72	) -> Self {
73		Self {
74			http_client: http_client.into(),
75			transport_mapper: mapper.into(),
76			store,
77			descriptor,
78			strategy,
79			client_id: client_id.into(),
80			client_secret: None,
81			flow_guards: Default::default(),
82			refresh_metrics: Default::default(),
83		}
84	}
85
86	/// Sets or replaces the client secret used for confidential client auth modes.
87	pub fn with_client_secret(mut self, secret: impl Into<String>) -> Self {
88		self.client_secret = Some(secret.into());
89
90		self
91	}
92}
93#[cfg(feature = "reqwest")]
94impl Broker<ReqwestHttpClient, ReqwestTransportErrorMapper> {
95	/// Creates a new broker for the provided descriptor and client identifier.
96	///
97	/// The broker provisions its own reqwest-backed transport so callers do not need
98	/// to pass HTTP handles explicitly. Use [`Broker::with_client_secret`] to attach a confidential
99	/// client secret when the descriptor prefers `client_secret_basic` or
100	/// `client_secret_post`.
101	pub fn new(
102		store: Arc<dyn BrokerStore>,
103		descriptor: ProviderDescriptor,
104		strategy: Arc<dyn ProviderStrategy>,
105		client_id: impl Into<String>,
106	) -> Self {
107		Self::with_http_client(
108			store,
109			descriptor,
110			strategy,
111			client_id,
112			ReqwestHttpClient::default(),
113			Arc::new(ReqwestTransportErrorMapper),
114		)
115	}
116}
117impl<C, M> Debug for Broker<C, M>
118where
119	C: ?Sized + TokenHttpClient,
120	M: ?Sized + TransportErrorMapper<C::TransportError>,
121{
122	fn fmt(&self, f: &mut Formatter) -> FmtResult {
123		f.debug_struct("Broker")
124			.field("descriptor", &self.descriptor)
125			.field("client_id", &self.client_id)
126			.field("client_secret_set", &self.client_secret.is_some())
127			.finish()
128	}
129}