This commit is contained in:
Wim Velzeboer
2026-09-21 14:17:43 +01:00
parent e92bec2e70
commit c1c69168d7
2 changed files with 78 additions and 51 deletions
+60 -47
View File
@@ -16,9 +16,7 @@ use rsa::RsaPrivateKey;
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
use sha2::Sha256; use sha2::Sha256;
use std::time::Duration; use std::time::Duration;
use thiserror::Error;
use time::OffsetDateTime; use time::OffsetDateTime;
use url::Url;
/// The default Salesforce production login URL. /// The default Salesforce production login URL.
@@ -392,6 +390,13 @@ pub struct SalesforceAuthSession {
/// Salesforce identity service URL, when returned by Salesforce. /// Salesforce identity service URL, when returned by Salesforce.
pub id: Option<String>, pub id: Option<String>,
/// OAuth refresh token, when available.
/// Salesforce organization ID parsed from the identity service URL.
pub org_id: Option<String>,
/// Salesforce user ID parsed from the identity service URL.
pub user_id: Option<String>,
/// OAuth refresh token, when available. /// OAuth refresh token, when available.
pub refresh_token: Option<String>, pub refresh_token: Option<String>,
@@ -659,54 +664,29 @@ pub async fn authenticate_client_credentials(
client_id: &str, client_id: &str,
client_secret: &str, client_secret: &str,
) -> Result<SalesforceAuthSession, SalesforceAuthError> { ) -> Result<SalesforceAuthSession, SalesforceAuthError> {
println!("authenticate_client_credentials"); let response = Client::builder()
println!("login url {}", login_url);
println!("client Id {}", client_id);
println!("client secret {}", client_secret);
let http_client = Client::builder()
.redirect(reqwest::redirect::Policy::none()) .redirect(reqwest::redirect::Policy::none())
.timeout(Duration::from_secs(30)) .timeout(Duration::from_secs(30))
.build()?; .build()?
let token_url = TokenUrl::new(format!( .post(token_url(login_url)?.url().clone())
"{}/services/oauth2/token", .form(&[
login_url.trim_end_matches('/') ("grant_type", "client_credentials"),
// "http://localhost:3000" ("client_id", client_id),
)) ("client_secret", client_secret),
.map_err(|error| SalesforceAuthError::OAuth2(error.to_string()))?; ])
.send()
.await?
.error_for_status()?
.json::<SalesforceTokenResponse>()
.await?;
to_session(
let oauth_client = BasicClient::new(ClientId::new(client_id.to_string())) response,
.set_client_secret(ClientSecret::new(client_secret.to_string())) None,
.set_token_uri(token_url); Some(client_id.to_string()),
Some(client_secret.to_string()),
let token_response: StandardTokenResponse<oauth2::EmptyExtraTokenFields, BasicTokenType> = Some(login_url.to_string()),
oauth_client )
.exchange_client_credentials()
// .add_scope(Scope::new("api".to_string()))
.request_async(&http_client)
.await
.map_err(|error| SalesforceAuthError::OAuth2(error.to_string()))?;
let access_token = token_response.access_token().secret().to_string();
println!("asfasf {}", client_id);
Ok(SalesforceAuthSession {
token: RwLock::new(SalesforceAuthToken {
access_token: "asdf".to_string(),
token_type: Some("Bearer".to_string()),
issued_at: None,
signature: None,
}),
instance_url: login_url.trim_end_matches('/').to_string(),
id: None,
refresh_token: None,
client_id: Some(client_id.to_string()),
client_secret: Some(client_secret.to_string()),
login_url: Some(login_url.to_string()),
})
} }
/// Authenticates to Salesforce using the JWT bearer flow. /// Authenticates to Salesforce using the JWT bearer flow.
@@ -772,6 +752,31 @@ fn salesforce_token_endpoint(login_url: &str) -> String {
format!("{}/services/oauth2/token", login_url.trim_end_matches('/')) format!("{}/services/oauth2/token", login_url.trim_end_matches('/'))
} }
fn parse_salesforce_identity_ids(id_url: Option<&str>) -> (Option<String>, Option<String>) {
let Some(id_url) = id_url else {
return (None, None);
};
let segments = id_url
.trim_end_matches('/')
.split('/')
.filter(|segment| !segment.is_empty())
.collect::<Vec<_>>();
if segments.len() < 2 {
return (None, None);
}
let org_id = segments
.get(segments.len() - 2)
.map(|value| (*value).to_string());
let user_id = segments
.last()
.map(|value| (*value).to_string());
(org_id, user_id)
}
fn to_session( fn to_session(
response: SalesforceTokenResponse, response: SalesforceTokenResponse,
@@ -784,6 +789,8 @@ fn to_session(
.instance_url .instance_url
.ok_or(SalesforceAuthError::MissingRequiredField("instance_url"))?; .ok_or(SalesforceAuthError::MissingRequiredField("instance_url"))?;
let (org_id, user_id) = parse_salesforce_identity_ids(response.id.as_deref());
Ok(SalesforceAuthSession { Ok(SalesforceAuthSession {
token: RwLock::new(SalesforceAuthToken { token: RwLock::new(SalesforceAuthToken {
access_token: response.access_token, access_token: response.access_token,
@@ -793,6 +800,8 @@ fn to_session(
}), }),
instance_url, instance_url,
id: response.id, id: response.id,
org_id,
user_id,
refresh_token: response.refresh_token.or(refresh_token), refresh_token: response.refresh_token.or(refresh_token),
client_id, client_id,
client_secret, client_secret,
@@ -839,6 +848,8 @@ pub fn authenticate_access_token(
}), }),
instance_url: instance_url.trim_end_matches('/').to_string(), instance_url: instance_url.trim_end_matches('/').to_string(),
id: None, id: None,
org_id: None,
user_id: None,
refresh_token, refresh_token,
client_id, client_id,
client_secret, client_secret,
@@ -1134,6 +1145,8 @@ async fn fetch_salesforce_token_response_from_access_token(
}), }),
instance_url: login_url.trim_end_matches('/').to_string(), instance_url: login_url.trim_end_matches('/').to_string(),
id: None, id: None,
org_id: None,
user_id: None,
refresh_token: None, refresh_token: None,
client_id: None, client_id: None,
client_secret: None, client_secret: None,
+18 -4
View File
@@ -58,18 +58,32 @@ async fn main() {
//Default header //Default header
headers.insert("Accept", HeaderValue::from_static("application/json")); headers.insert("Accept", HeaderValue::from_static("application/json"));
// Get API Versions
let response = get_http_client().unwrap() let response = get_http_client().unwrap()
.get(format!("{}/services/data", session.instance_url)) .get(format!("{}/services/data", session.instance_url))
.headers(headers) .headers(headers.clone())
.send() .send()
.await .await
.unwrap(); .unwrap();
if response.status().is_success() { if response.status().is_success() {
println!("SUCCESS Response: {:?}", response.text().await.unwrap()); println!("SUCCESS Response: {:?}", response.text().await.unwrap());
} else { } else {
println!("ERROR Response: {:?}", response.text().await.unwrap()); println!("ERROR Response: {:?}", response.text().await.unwrap());
} }
session.refresh_access_token().await.unwrap();
// Get LIMITs Versions
let response = get_http_client().unwrap()
.get(format!("{}/services/data/v67.0/limits", session.instance_url))
.headers(headers)
.send()
.await
.unwrap();
if response.status().is_success() {
println!("SUCCESS Response 2: {:?}", response.text().await.unwrap());
} else {
println!("ERROR Response 2: {:?}", response.text().await.unwrap());
}
} }