alibi_plugins/oauth/providers/
salesforce.rs1use super::remaining_profile::{ProfileKind, PublishedProfile};
3use super::{OAuthAuthorizationPolicy, OAuthProvider, OAuthTokenEndpointAuth, OAuthUserInfo};
4use serde_json::Value;
5#[derive(Debug, Clone, Copy, Default, PartialEq, Eq)]
6pub enum SalesforceEnvironment {
7 #[default]
8 Production,
9 Sandbox,
10}
11
12#[derive(Clone)]
13pub struct SalesforceOptions {
14 pub client_id: String,
15 pub client_secret: Option<String>,
16 pub client_key: Option<String>,
17 pub authorization_endpoint: Option<String>,
18 pub redirect_uri: Option<String>,
19 pub user_info_endpoint: Option<String>,
21 pub map_profile_to_user: Option<fn(Value) -> Result<OAuthUserInfo, String>>,
22 pub scope: Vec<String>,
23 pub disable_default_scope: bool,
24 pub environment: SalesforceEnvironment,
25 pub login_url: Option<String>,
26}
27impl SalesforceOptions {
28 #[must_use]
29 pub fn new(client_id: impl Into<String>, client_secret: Option<String>) -> Self {
30 Self {
31 client_id: client_id.into(),
32 client_secret,
33 client_key: None,
34 authorization_endpoint: None,
35 redirect_uri: None,
36 user_info_endpoint: None,
37 map_profile_to_user: None,
38 scope: Vec::new(),
39 disable_default_scope: false,
40 environment: SalesforceEnvironment::Production,
41 login_url: None,
42 }
43 }
44}
45impl std::fmt::Debug for SalesforceOptions {
46 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
47 f.debug_struct("SalesforceOptions")
48 .field("client_id", &self.client_id)
49 .field("authorization_endpoint", &self.authorization_endpoint)
50 .finish_non_exhaustive()
51 }
52}
53impl OAuthProvider {
54 #[must_use]
55 pub fn salesforce(client_id: &str, client_secret: Option<&str>) -> Self {
56 Self::salesforce_with_options(SalesforceOptions::new(
57 client_id,
58 client_secret.map(str::to_owned),
59 ))
60 }
61 #[must_use]
62 pub fn salesforce_with_options(options: SalesforceOptions) -> Self {
63 let mut policy = OAuthAuthorizationPolicy {
64 preserve_raw_profile_scalars: true,
65 source_profile_exceptions: true,
66 require_client_id: true,
67 login_hint: false,
68 pkce: true,
69 configured_scopes: options.scope,
70 disable_default_scopes: options.disable_default_scope,
71 authorization_code_client_key: options.client_key,
72 redirect_uri: options.redirect_uri,
73 token_endpoint_auth: Some(
74 if options
75 .client_secret
76 .as_ref()
77 .is_some_and(|v| !v.is_empty())
78 {
79 OAuthTokenEndpointAuth::ClientSecretPost
80 } else {
81 OAuthTokenEndpointAuth::None
82 },
83 ),
84 allow_missing_access_token: true,
85 preserve_raw_email_errors: true,
86 ..OAuthAuthorizationPolicy::default()
87 };
88 let mut provider = Self {
89 client_id: options.client_id,
90 client_secret: options.client_secret.unwrap_or_default(),
91 additional_client_ids: Vec::new(),
92 hosted_domain: None,
93 require_email_verification: false,
94 auth_url: "https://login.salesforce.com/services/oauth2/authorize".into(),
95 token_url: "https://login.salesforce.com/services/oauth2/token".into(),
96 user_info_url: Some("https://login.salesforce.com/services/oauth2/userinfo".into()),
97 scopes: vec!["openid".into(), "email".into(), "profile".into()],
98 authorization: None,
99 allowed_request_params: Vec::new(),
100 authorization_params: Vec::new(),
101 account_subject: Some(subject),
102 map_user_info: None,
103 get_user_info: None,
104 refresh_access_token: None,
105 verify_id_token: None,
106 id_token: None,
107 disable_id_token_sign_in: false,
108 disable_implicit_sign_up: false,
109 disable_sign_up: false,
110 allow_idp_initiated: false,
111 override_user_info_on_sign_in: false,
112 };
113 policy.require_client_secret = true;
114 let host = options
115 .login_url
116 .filter(|v| !v.is_empty())
117 .unwrap_or_else(|| match options.environment {
118 SalesforceEnvironment::Sandbox => "test.salesforce.com".into(),
119 SalesforceEnvironment::Production => "login.salesforce.com".into(),
120 });
121 provider.auth_url = options
122 .authorization_endpoint
123 .filter(|v| !v.is_empty())
124 .unwrap_or_else(|| format!("https://{host}/services/oauth2/authorize"));
125 provider.token_url = format!("https://{host}/services/oauth2/token");
126 provider.user_info_url = Some(
127 options
128 .user_info_endpoint
129 .unwrap_or_else(|| format!("https://{host}/services/oauth2/userinfo")),
130 );
131 provider.authorization = Some(policy);
132 provider.get_user_info = Some(std::sync::Arc::new(PublishedProfile {
133 kind: ProfileKind::Salesforce,
134 endpoint: provider.user_info_url.clone(),
135 email_endpoint: None,
136 client_id: provider.client_id.clone(),
137 mapper: options.map_profile_to_user,
138 application_mapper: None,
139 }));
140 provider
141 }
142}
143fn subject(profile: &Value) -> Result<String, String> {
144 super::remaining_profile::subject(ProfileKind::Salesforce, profile)
145}