116 lines
3.8 KiB
Rust
116 lines
3.8 KiB
Rust
use log::trace;
|
|
use crate::credentials::{http_client, required, SalesforceAuthFlow, SalesforceCredentials};
|
|
use crate::salesforce_token_response::SalesforceTokenResponse;
|
|
use crate::SalesforceAuthError;
|
|
|
|
impl SalesforceCredentials {
|
|
/// Creates a configuration for the OAuth 2.0 client mod flow.
|
|
///
|
|
/// # Examples
|
|
///
|
|
/// ```rust
|
|
/// use rustsf_auth::{SalesforceCredentials, SalesforceAuthFlow};
|
|
///
|
|
/// let config = SalesforceCredentials::client_credentials(
|
|
/// "https://login.salesforce.com",
|
|
/// "client-id",
|
|
/// "client-secret",
|
|
/// );
|
|
///
|
|
/// assert_eq!(config.flow, SalesforceAuthFlow::ClientCredentials);
|
|
/// assert_eq!(config.client_id.as_deref(), Some("client-id"));
|
|
/// ```
|
|
pub fn client_credentials(
|
|
login_url: impl Into<String>,
|
|
client_id: impl Into<String>,
|
|
client_secret: impl Into<String>,
|
|
) -> Self {
|
|
Self {
|
|
flow: SalesforceAuthFlow::ClientCredentials,
|
|
login_url: Some(login_url.into()),
|
|
client_id: Some(client_id.into()),
|
|
client_secret: Some(client_secret.into()),
|
|
username: None,
|
|
private_key_pem: None,
|
|
access_token: None,
|
|
refresh_token: None,
|
|
instance_url: None,
|
|
}
|
|
}
|
|
|
|
/// Authenticates to Salesforce using the OAuth 2.0 client mod flow.
|
|
///
|
|
/// This method sends a client mod token request to:
|
|
///
|
|
/// `{login_url}/services/oauth2/token`
|
|
///
|
|
/// # Errors
|
|
///
|
|
/// Returns [`SalesforceAuthError`] if the OAuth client cannot be built, the HTTP
|
|
/// request fails, or Salesforce rejects the token request.
|
|
///
|
|
/// # Examples
|
|
///
|
|
/// ```rust,no_run
|
|
///
|
|
/// use rustsf_auth::authenticate_client_credentials;
|
|
/// # async fn example() -> Result<(), rustsf_auth::SalesforceAuthError> {
|
|
/// let session = authenticate_client_credentials(
|
|
/// "https://login.salesforce.com",
|
|
/// "client-id",
|
|
/// "client-secret",
|
|
/// ).await?;
|
|
///
|
|
/// println!("{}", session.access_token);
|
|
/// # Ok(())
|
|
/// # }
|
|
/// ```
|
|
///
|
|
/// Configure Salesforce
|
|
///
|
|
/// 1. Create the External Client App
|
|
/// - Navigate to Setup > External Client App Manager.
|
|
/// - Click New External Client App and enter the app name and contact email.
|
|
/// - Expand the API (Enable OAuth Settings) section:
|
|
/// - Check Enable OAuth.
|
|
/// - Check Enable Client Credentials Flow.
|
|
/// - Add the Manage user data via APIs (api) scope. Do not add refresh_token or offline_access as these are invalid for this flow.
|
|
/// - Click Create and note the Consumer Key (Client ID) and Consumer Secret.
|
|
///
|
|
/// 2. Configure Policies and Run As User
|
|
/// - In the External Client App Manager, find your new app and click Edit.
|
|
/// - Go to the Policies tab.
|
|
/// - Under OAuth Flows and External Client App Enhancements:
|
|
/// - Ensure Enable Client Credentials Flow is checked.
|
|
/// - In the Run As field, select the integration user (a dedicated service account with necessary API permissions).
|
|
/// - Save the changes.
|
|
///
|
|
pub(crate) async fn connect_client_credentials(&self) -> Result<SalesforceTokenResponse, SalesforceAuthError> {
|
|
|
|
let client_id = required(self.client_id.as_deref(), "client_id")?;
|
|
let client_secret = required(self.client_secret.as_deref(), "client_secret")?;
|
|
|
|
let url = self.token_url()?.url().clone();
|
|
let data = &[
|
|
("grant_type", "client_credentials"),
|
|
("client_id", client_id),
|
|
("client_secret", client_secret),
|
|
];
|
|
trace!("Client Credentials auth request: POST {}", url);
|
|
|
|
let response = http_client()?
|
|
.post(url)
|
|
.form(data)
|
|
.send()
|
|
.await?;
|
|
|
|
let status = response.status();
|
|
let body = response.text().await.map_err(|e| SalesforceAuthError::Http(e))?;
|
|
|
|
trace!("Client Credentials auth response: {}", body);
|
|
if !status.is_success() { return Err(SalesforceAuthError::OAuth2(body)) }
|
|
|
|
Ok(serde_json::from_str::<SalesforceTokenResponse>(&body)
|
|
.map_err(|e| SalesforceAuthError::TokenExchange(e.to_string()))?)
|
|
}
|
|
} |