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
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
//! Bearer-credential provider for [`SchwabClient`](crate::SchwabClient).
//!
//! A trivial implementation is provided:
//!
//! - [`StaticTokenProvider`] - returns the same [`AuthToken`] forever.
//! This is what [`SchwabClient::new`](crate::SchwabClient::new) wraps
//! internally; callers who never need to rotate a token need not
//! interact with the trait at all.
//!
//! A consumer that wants on-demand refresh, lazy fetch from a secret
//! store, or any other policy implements [`TokenProvider`] directly.
//!
//! # OAuth flow
//!
//! The SDK does not perform the authorization-code exchange. Callers
//! obtain the bearer out of band. If you stand up a local callback
//! server for the redirect, bind to `127.0.0.1` only, make the
//! listener one-shot, and validate the `state` parameter on every
//! callback to prevent CSRF.
use async_trait;
use crateError;
use crateAuthToken;
/// Source of the bearer token used on every Schwab REST request.
///
/// The SDK calls [`access_token`](Self::access_token) once per request,
/// just before sending. A provider that wants to cache should do so
/// internally; the SDK does not.
///
/// The trait itself carries no `Send`/`Sync` bound so `!Send`
/// implementations remain expressible (tests, future client variants).
/// The bound is enforced at the storage site: [`SchwabClient`] holds
/// `Arc<dyn TokenProvider + Send + Sync>`, so a provider handed to
/// [`SchwabClient::with_token_provider`] must satisfy both.
///
/// # Examples
///
/// A swappable provider using `arc-swap` for wait-free reads. A refresh
/// loop calls [`rotate`](#method.rotate) when a new access token arrives
/// and the next [`access_token`](Self::access_token) call hands it out.
/// Wire it in with [`SchwabClient::with_token_provider`]. The same provider
/// is reused across every clone of the client.
///
/// ```no_run
/// use std::sync::Arc;
/// use arc_swap::ArcSwap;
/// use async_trait::async_trait;
/// use schwab_sdk::{AuthToken, Error, SchwabClient, TokenProvider};
///
/// struct SwappableProvider(ArcSwap<AuthToken>);
///
/// impl SwappableProvider {
/// fn new(initial: AuthToken) -> Self {
/// Self(ArcSwap::from_pointee(initial))
/// }
///
/// /// Called by your refresh loop when a fresh access token arrives.
/// fn rotate(&self, fresh: AuthToken) {
/// self.0.store(Arc::new(fresh));
/// }
/// }
///
/// #[async_trait]
/// impl TokenProvider for SwappableProvider {
/// async fn access_token(&self) -> Result<AuthToken, Error> {
/// Ok((*self.0.load_full()).clone())
/// }
/// }
///
/// async fn run() -> schwab_sdk::Result<()> {
/// let provider = Arc::new(SwappableProvider::new(AuthToken::new("initial-token")));
/// let client = SchwabClient::with_token_provider(provider.clone());
///
/// // The first REST call sees the initial token.
/// let _ = client.accounts().numbers().await?;
///
/// // Your refresh task obtains a new access token out of band, then
/// // hands it to the provider.
/// provider.rotate(AuthToken::new("rotated-token"));
///
/// // The next REST call sees the rotated token.
/// let _ = client.accounts().numbers().await?;
///
/// Ok(())
/// }
/// ```
///
/// [`SchwabClient`]: crate::SchwabClient
/// [`SchwabClient::with_token_provider`]: crate::SchwabClient::with_token_provider
/// [`TokenProvider`] that returns the same [`AuthToken`] for every call.
///
/// This is the default impl wrapping the token passed to
/// [`SchwabClient::new`](crate::SchwabClient::new); callers who hold a
/// short-lived token and tear the client down when it expires need no
/// other provider.
;