2828class AuthType (str , Enum ):
2929 """Supported authentication methods for Salesforce Data Cloud."""
3030
31- USERNAME_PASSWORD = "username_password"
3231 OAUTH_TOKENS = "oauth_tokens"
32+ CLIENT_CREDENTIALS = "client_credentials"
3333
3434
3535# Environment variable mappings for each auth type
@@ -38,37 +38,31 @@ class AuthType(str, Enum):
3838 "client_id" : "SFDC_CLIENT_ID" ,
3939}
4040
41- ENV_CREDENTIALS_USERNAME_PASSWORD = {
42- "username" : "SFDC_USERNAME" ,
43- "password" : "SFDC_PASSWORD" ,
44- "client_secret" : "SFDC_CLIENT_SECRET" ,
45- }
46-
4741ENV_CREDENTIALS_OAUTH_TOKENS = {
4842 "client_secret" : "SFDC_CLIENT_SECRET" ,
4943 "refresh_token" : "SFDC_REFRESH_TOKEN" ,
5044 "core_token" : "SFDC_CORE_TOKEN" ,
5145}
5246
47+ ENV_CREDENTIALS_CLIENT_CREDENTIALS = {
48+ "client_secret" : "SFDC_CLIENT_SECRET" ,
49+ }
50+
5351
5452@dataclass
5553class Credentials :
5654 """Flexible credentials supporting multiple authentication methods.
5755
5856 Supports two authentication methods:
59- - OAUTH_TOKENS: OAuth tokens (core_token and refresh_token) authentication
60- - USERNAME_PASSWORD: Traditional username/password OAuth flow
57+ - OAUTH_TOKENS: OAuth tokens (refresh_token) authentication (default)
58+ - CLIENT_CREDENTIALS: Server-to-server integration using client_id/secret only
6159 """
6260
6361 # Required for all auth types
6462 login_url : str
6563 client_id : str
6664 auth_type : AuthType = field (default = AuthType .OAUTH_TOKENS )
6765
68- # Username/Password flow fields
69- username : Optional [str ] = None
70- password : Optional [str ] = None
71-
7266 # Common field
7367 client_secret : Optional [str ] = None
7468
@@ -82,20 +76,7 @@ def __post_init__(self):
8276
8377 def _validate (self ) -> None :
8478 """Validate that required fields are present for the auth type."""
85- if self .auth_type == AuthType .USERNAME_PASSWORD :
86- missing = []
87- if not self .username :
88- missing .append ("username" )
89- if not self .password :
90- missing .append ("password" )
91- if not self .client_secret :
92- missing .append ("client_secret" )
93- if missing :
94- raise ValueError (
95- f"Username/Password auth requires: { ', ' .join (missing )} "
96- )
97-
98- elif self .auth_type == AuthType .OAUTH_TOKENS :
79+ if self .auth_type == AuthType .OAUTH_TOKENS :
9980 missing = []
10081 if not self .refresh_token :
10182 missing .append ("refresh_token" )
@@ -104,6 +85,10 @@ def _validate(self) -> None:
10485 if missing :
10586 raise ValueError (f"OAuth Tokens auth requires: { ', ' .join (missing )} " )
10687
88+ elif self .auth_type == AuthType .CLIENT_CREDENTIALS :
89+ if not self .client_secret :
90+ raise ValueError ("Client Credentials auth requires: client_secret" )
91+
10792 @classmethod
10893 def from_ini (
10994 cls ,
@@ -150,9 +135,6 @@ def from_ini(
150135 login_url = section ["login_url" ],
151136 client_id = section ["client_id" ],
152137 auth_type = auth_type ,
153- # Username/Password fields
154- username = section .get ("username" ),
155- password = section .get ("password" ),
156138 client_secret = section .get ("client_secret" ),
157139 # OAuth Tokens fields
158140 core_token = section .get ("core_token" ),
@@ -166,19 +148,14 @@ def from_env(cls) -> Credentials:
166148 Environment variables:
167149 Common (required):
168150 SFDC_LOGIN_URL: Salesforce login URL
169- SFDC_CLIENT_ID: Connected App client ID
151+ SFDC_CLIENT_ID: External Client App client ID
170152 SFDC_AUTH_TYPE: Authentication type (optional, defaults to oauth_tokens)
171153
172154 For oauth_tokens (default):
173- SFDC_CLIENT_SECRET: Connected App client secret
155+ SFDC_CLIENT_SECRET: External Client App client secret
174156 SFDC_REFRESH_TOKEN: OAuth refresh token
175157 SFDC_CORE_TOKEN: OAuth core/access token (optional)
176158
177- For username_password:
178- SFDC_USERNAME: Salesforce username
179- SFDC_PASSWORD: Salesforce password
180- SFDC_CLIENT_SECRET: Connected App client secret
181-
182159 Returns:
183160 Credentials instance loaded from environment variables
184161
@@ -208,9 +185,6 @@ def from_env(cls) -> Credentials:
208185 login_url = login_url ,
209186 client_id = client_id ,
210187 auth_type = auth_type ,
211- # Username/Password fields
212- username = os .environ .get ("SFDC_USERNAME" ),
213- password = os .environ .get ("SFDC_PASSWORD" ),
214188 client_secret = os .environ .get ("SFDC_CLIENT_SECRET" ),
215189 # OAuth Tokens fields
216190 core_token = os .environ .get ("SFDC_CORE_TOKEN" ),
@@ -273,15 +247,7 @@ def update_ini(self, profile: str = "default", ini_file: str = INI_FILE) -> None
273247 config [profile ]["client_id" ] = self .client_id
274248
275249 # Save fields based on auth type
276- if self .auth_type == AuthType .USERNAME_PASSWORD :
277- config [profile ]["username" ] = self .username or ""
278- config [profile ]["password" ] = self .password or ""
279- config [profile ]["client_secret" ] = self .client_secret or ""
280- # Remove fields from other auth types
281- for key in ["refresh_token" , "core_token" ]:
282- config [profile ].pop (key , None )
283-
284- elif self .auth_type == AuthType .OAUTH_TOKENS :
250+ if self .auth_type == AuthType .OAUTH_TOKENS :
285251 config [profile ]["client_secret" ] = self .client_secret or ""
286252 config [profile ]["refresh_token" ] = self .refresh_token or ""
287253 if self .core_token :
@@ -290,6 +256,12 @@ def update_ini(self, profile: str = "default", ini_file: str = INI_FILE) -> None
290256 for key in ["username" , "password" ]:
291257 config [profile ].pop (key , None )
292258
259+ elif self .auth_type == AuthType .CLIENT_CREDENTIALS :
260+ config [profile ]["client_secret" ] = self .client_secret or ""
261+ # Remove fields from other auth types
262+ for key in ["username" , "password" , "refresh_token" , "core_token" ]:
263+ config [profile ].pop (key , None )
264+
293265 with open (expanded_ini_file , "w" ) as f :
294266 config .write (f )
295267
0 commit comments