Skip to main content

cirrus_auth/
lib.rs

1//! Salesforce OAuth 2.0 authentication flows for the Cirrus SDK.
2//!
3//! Salesforce supports several OAuth 2.0 flows, each producing the same two
4//! pieces of information: a bearer access token and an `instance_url` that
5//! becomes the base URL for subsequent REST calls. The rest of the SDK is
6//! flow-agnostic — it only needs an [`AuthSession`] that can hand over those
7//! two values on demand.
8//!
9//! Implementations live in submodules (one per flow). They are responsible
10//! for their own token acquisition, refresh, and any caching. The trait
11//! method is `async` so an implementation may transparently refresh an
12//! expired token when called.
13//!
14//! ## Crate boundary
15//!
16//! This crate is the canonical home of every auth flow used by the
17//! Cirrus SDK. It is re-exported as `cirrus::auth` so end users never
18//! depend on `cirrus-auth` directly — `cirrus = "..."` is enough. Other
19//! Cirrus subcrates (e.g. `cirrus-metadata`) that need an authenticated
20//! session depend on `cirrus-auth` so they don't pull in the full REST
21//! client.
22//!
23//! ## Implementations
24//!
25//! - [`static_token`] — preset access token + instance URL. Useful for
26//!   tests, scripts, or callers that have already obtained credentials by
27//!   other means.
28//! - [`jwt`] — OAuth 2.0 JWT Bearer flow for server-to-server auth.
29//! - [`refresh`] — OAuth 2.0 Refresh Token grant. Long-lived sessions for
30//!   any flow that produces a refresh token.
31//! - [`client_credentials`] — OAuth 2.0 Client Credentials grant for
32//!   server-to-server integrations that run as a pre-configured user.
33//! - [`web_server`] — OAuth 2.0 Web Server flow with PKCE for
34//!   user-interactive authorization. Yields a [`RefreshTokenAuth`] once the
35//!   user comes back through the redirect URL.
36//! - [`token_exchange`] — OAuth 2.0 Token Exchange (RFC 8693), including
37//!   Salesforce's hybrid grant.
38//!
39//! Flows Salesforce lists as legacy or deprecated are intentionally not
40//! supported.
41
42use async_trait::async_trait;
43use std::borrow::Cow;
44use std::sync::Arc;
45
46pub mod client_credentials;
47mod error;
48pub mod jwt;
49pub mod refresh;
50pub mod static_token;
51mod token_endpoint;
52pub mod token_exchange;
53pub mod web_server;
54
55pub use client_credentials::{ClientCredentialsAuth, ClientCredentialsAuthBuilder};
56pub use error::{AuthError, AuthResult};
57pub use jwt::{JwtAuth, JwtAuthBuilder};
58pub use refresh::{RefreshTokenAuth, RefreshTokenAuthBuilder};
59pub use static_token::StaticTokenAuth;
60pub use token_exchange::{
61    SubjectTokenType, TokenExchangeFlow, TokenExchangeFlowBuilder, TokenExchangeGrantType,
62    TokenExchangeSession,
63};
64pub use web_server::{CompletedSession, PendingExchange, WebServerFlow, WebServerFlowBuilder};
65
66/// Abstraction over a Salesforce authentication session.
67///
68/// An implementation holds whatever credentials the chosen flow needs,
69/// produces a bearer access token on demand (refreshing if necessary), and
70/// reports the instance URL that REST requests should target.
71///
72/// The trait is `Send + Sync` and uses [`async_trait`](mod@async_trait) to remain
73/// `dyn`-compatible — the client stores `Arc<dyn AuthSession>` so handlers
74/// don't care which flow was configured.
75#[async_trait]
76pub trait AuthSession: Send + Sync {
77    /// Returns a valid bearer access token. Implementations may refresh
78    /// expired tokens transparently here; that is why the method is `async`.
79    async fn access_token(&self) -> AuthResult<Cow<'_, str>>;
80
81    /// Returns the instance URL for REST requests, e.g.
82    /// `https://my-org.my.salesforce.com`. No trailing slash.
83    fn instance_url(&self) -> &str;
84
85    /// Invalidates a cached token that the SDK has determined is no
86    /// longer valid (typically because Salesforce returned 401
87    /// `INVALID_SESSION_ID` when it was used).
88    ///
89    /// The next [`access_token`](Self::access_token) call should mint
90    /// a fresh token. The `stale_token` parameter lets implementations
91    /// compare-and-swap — only clear the cache if the cached value
92    /// still matches what the caller actually used. This avoids
93    /// blowing away a *newer* token that another concurrent task may
94    /// have refreshed in the meantime.
95    ///
96    /// Default impl is a no-op for static or stateless sessions
97    /// ([`StaticTokenAuth`]) where there is nothing to invalidate.
98    /// Stateful flows ([`JwtAuth`], [`RefreshTokenAuth`],
99    /// [`ClientCredentialsAuth`]) override to clear their cached
100    /// token.
101    ///
102    /// `stale_token` is borrowed for the duration of the call only —
103    /// implementations should compare it against their cached value
104    /// and not retain it.
105    async fn invalidate(&self, stale_token: &str) {
106        // Default: nothing to invalidate. Suppress the unused-arg
107        // warning on the default impl without requiring overriders to
108        // bind a different name.
109        let _ = stale_token;
110    }
111}
112
113/// `Arc<dyn AuthSession>` — the shape stored inside the client.
114pub type SharedAuth = Arc<dyn AuthSession>;