Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
85 changes: 73 additions & 12 deletions src/main/java/com/salesforce/dataloader/client/LoginClient.java
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@
import com.salesforce.dataloader.config.Messages;
import com.salesforce.dataloader.controller.Controller;
import com.salesforce.dataloader.exception.PasswordExpiredException;
import com.salesforce.dataloader.util.OAuthServerFlow;
import com.sforce.soap.partner.Connector;
import com.sforce.soap.partner.GetUserInfoResult;
import com.sforce.soap.partner.LimitInfo;
Expand Down Expand Up @@ -180,6 +181,15 @@ && getAPIVersionForTheSession() != null
private boolean dologin() throws ConnectionException, ApiFault {
// Attempt the login giving the user feedback
logger.info(Messages.getString("Client.sforceLogin")); //$NON-NLS-1$
String refreshToken =
appConfig.getString(AppConfig.PROP_OAUTH_REFRESHTOKEN);

String secret =
appConfig.getEffectiveClientSecretForCurrentEnv();

if (refreshToken != null) {
logger.info("Refresh token length=" + refreshToken.length());
}
String oauthAccessToken = appConfig.getString(AppConfig.PROP_OAUTH_ACCESSTOKEN);
String serverUrl = appConfig.getAuthEndpointForCurrentEnv();
if (oauthAccessToken != null && oauthAccessToken.trim().length() > 0) {
Expand All @@ -194,19 +204,70 @@ private boolean dologin() throws ConnectionException, ApiFault {
final PartnerConnection conn = Connector.newConnection(cc);
// identify the client as dataloader
conn.setCallOptions(ClientBase.getClientName(this.appConfig), null);


try {

try {
if (oauthAccessToken != null && oauthAccessToken.trim().length() > 0) {
setConfiguredSessionId(conn, oauthAccessToken, null);
} else if (appConfig.getBoolean(AppConfig.PROP_SFDC_INTERNAL) && appConfig.getBoolean(AppConfig.PROP_SFDC_INTERNAL_IS_SESSION_ID_LOGIN)) {
setConfiguredSessionId(conn, appConfig.getString(AppConfig.PROP_SFDC_INTERNAL_SESSION_ID), null);
} else {
setSessionRenewer(conn);
loginInternal(conn);
}
} catch (Exception ex) {
throw ex;
} finally {
if (refreshToken != null
&& refreshToken.trim().length() > 0) {

try {

OAuthServerFlow flow =
new OAuthServerFlow(appConfig);

String newAccessToken =
flow.refreshAccessToken(
refreshToken);

logger.info("Refreshed token present="
+ (newAccessToken != null));

if (newAccessToken != null) {

setConfiguredSessionId(
conn,
newAccessToken,
null);

return true;
}

} catch (Exception ex) {

logger.error(
"Refresh token flow failed",
ex);
}

setSessionRenewer(conn);
loginInternal(conn);

} else if (oauthAccessToken != null
&& oauthAccessToken.trim().length() > 0) {

setConfiguredSessionId(
conn,
oauthAccessToken,
null);

} else if (appConfig.getBoolean(AppConfig.PROP_SFDC_INTERNAL)
&& appConfig.getBoolean(AppConfig.PROP_SFDC_INTERNAL_IS_SESSION_ID_LOGIN)) {

setConfiguredSessionId(
conn,
appConfig.getString(AppConfig.PROP_SFDC_INTERNAL_SESSION_ID),
null);

} else {

setSessionRenewer(conn);
loginInternal(conn);
}

} catch (Exception ex) {
throw ex;
} finally {
cc.setTraceMessage(savedIsTraceMessage);
}
return true;
Expand Down
29 changes: 21 additions & 8 deletions src/main/java/com/salesforce/dataloader/config/AppConfig.java
Original file line number Diff line number Diff line change
Expand Up @@ -538,7 +538,6 @@ public class AppConfig {
*/
PROP_DELETE_WITH_EXTERNALID,
PROP_OAUTH_ACCESSTOKEN,
PROP_OAUTH_REFRESHTOKEN,
PROP_OAUTH_INSTANCE_URL,
PROP_OAUTH_REDIRECT_URI_PROD,
PROP_OAUTH_REDIRECT_URI_SB,
Expand Down Expand Up @@ -1088,6 +1087,7 @@ public void load() throws IOException, ConfigInitializationException {
}
FileInputStream in = new FileInputStream(filename);
try {
logger.info("Config file=" + filename);
load(in);
} finally {
in.close();
Expand Down Expand Up @@ -1192,7 +1192,9 @@ private void postLoad(Map<?, ?> propMap, boolean isConfigFilePropsMap) throws Co
removeUnsupportedProperties();
}
}

public void markDirty() {
this.inMemoryPropValuesHaveChanged = true;
}
private void decryptAndCacheProperty(Map<?, ?> values, String propertyName) throws ConfigInitializationException {
@SuppressWarnings("unchecked")
Map<String, String> propMap = (Map<String, String>)values;
Expand All @@ -1207,12 +1209,23 @@ private void decryptAndCacheProperty(Map<?, ?> values, String propertyName) thro
return; // do not decrypt an already decrypted value
}
}
String propValue = decryptProperty(encrypter, propMap, propertyName, isBatchMode());
if (propValue == null) propValue = "";
propMap.put(propertyName, propValue);

// cache decrypted value
propMap.put(propertyName + DECRYPTED_SUFFIX, propValue);
String propValue =
decryptProperty(
encrypter,
propMap,
propertyName,
isBatchMode());

if (propValue == null) {
propValue = "";
}

logger.info(
"Decrypted property: " + propertyName
+ " length=" + propValue.length());

propMap.put(propertyName, propValue);
propMap.put(propertyName + DECRYPTED_SUFFIX, propValue);
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -63,16 +63,55 @@ private boolean shouldRunLoginButtonEnabler() {
*
* @return true if login was successful, false otherwise
*/
public boolean handleOAuthLogin() {
logger.info("Starting OAuth flow");
appConfig.setLastOAuthFlow("PKCE");
public boolean handleOAuthLogin() {
logger.info("Starting OAuth flow");
appConfig.setLastOAuthFlow("PKCE");

logger.info("Attempting OAuth WebServer with PKCE flow, launching browser for login");
String refreshToken =
appConfig.getString(
AppConfig.PROP_OAUTH_REFRESHTOKEN);

if (refreshToken != null
&& !refreshToken.trim().isEmpty()) {
logger.info("USING REFRESH TOKEN PATH");
logger.info(
"Refresh token found, skipping browser login");

try {

if (controller != null) {

if (controller.login()) {

controller.saveConfig();

if (!AppUtil.isRunningInBatchMode()) {

Display.getDefault().asyncExec(
() -> controller.updateLoaderWindowTitleAndCacheUserInfoForTheSession());

}

return true;
}
}

} catch (Exception ex) {

logger.error(
"Refresh token login failed",
ex);
}
}

logger.info(
"Attempting OAuth WebServer with PKCE flow, launching browser for login");
if (statusConsumer != null) {
statusConsumer.accept(Labels.getString("OAuthLoginControl.statusAttemptingPKCE"));
}
try {
OAuthServerFlow pkceFlow = new OAuthServerFlow(appConfig, true, statusConsumer);
logger.info("USING BROWSER PATH");
if (pkceFlow.performOAuthFlow()) {
logger.info("WebServer with PKCE flow completed successfully");
if (statusConsumer != null) {
Expand Down
109 changes: 106 additions & 3 deletions src/main/java/com/salesforce/dataloader/util/OAuthServerFlow.java
Original file line number Diff line number Diff line change
Expand Up @@ -269,7 +269,7 @@ private String buildAuthorizationUrl() throws Exception {
authUrl.append("?response_type=code");
authUrl.append("&client_id=").append(URLEncoder.encode(clientId, StandardCharsets.UTF_8.name()));
authUrl.append("&redirect_uri=").append(URLEncoder.encode(redirectUri, StandardCharsets.UTF_8.name()));
authUrl.append("&scope=").append(URLEncoder.encode("api", StandardCharsets.UTF_8.name()));
authUrl.append("&scope=").append(URLEncoder.encode("api refresh_token", StandardCharsets.UTF_8.name()));
authUrl.append("&state=").append(URLEncoder.encode(state, StandardCharsets.UTF_8.name()));
if (usePkce && codeChallenge != null) {
authUrl.append("&code_challenge=").append(URLEncoder.encode(codeChallenge, StandardCharsets.UTF_8.name()));
Expand Down Expand Up @@ -355,11 +355,28 @@ private boolean exchangeCodeForTokens() throws com.salesforce.dataloader.excepti
String refreshToken = (String) response.get("refresh_token");
String instanceUrl = (String) response.get("instance_url");

logger.info("### ACCESS TOKEN PRESENT: "
+ (accessToken != null));

logger.info("### TOKEN RESPONSE: " + response);

logger.info("### REFRESH TOKEN PRESENT: "
+ (refreshToken != null));

if (refreshToken != null) {
logger.info("### REFRESH TOKEN LENGTH: "
+ refreshToken.length());
}

// Configure AppConfig with OAuth tokens
appConfig.setValue(AppConfig.PROP_OAUTH_ACCESSTOKEN, accessToken);
if (refreshToken != null) {
appConfig.setValue(AppConfig.PROP_OAUTH_REFRESHTOKEN, refreshToken);
}
logger.info("OAuth refresh token received. Length="
+ refreshToken.length());

appConfig.setValue(AppConfig.PROP_OAUTH_REFRESHTOKEN, refreshToken);
appConfig.save();
}
if (instanceUrl != null) {
appConfig.setAuthEndpointForCurrentEnv(instanceUrl);
appConfig.setValue(AppConfig.PROP_OAUTH_INSTANCE_URL, instanceUrl);
Expand All @@ -382,6 +399,92 @@ private boolean exchangeCodeForTokens() throws com.salesforce.dataloader.excepti
}
}

public String refreshAccessToken(String refreshToken)
throws Exception {

logger.info("Refreshing OAuth access token");

String tokenUrl =
appConfig.getAuthEndpointForCurrentEnv()
+ "/services/oauth2/token";

String clientId =
appConfig.getEffectiveClientIdForCurrentEnv();

String clientSecret =
appConfig.getEffectiveClientSecretForCurrentEnv();

List<BasicNameValuePair> params =
new ArrayList<>();

params.add(new BasicNameValuePair(
"grant_type",
"refresh_token"));

params.add(new BasicNameValuePair(
"client_id",
clientId));

params.add(new BasicNameValuePair(
"refresh_token",
refreshToken));

if (appConfig.isExternalClientAppConfigured()
&& clientSecret != null
&& !clientSecret.trim().isEmpty()) {

params.add(new BasicNameValuePair(
"client_secret",
clientSecret));
}

SimplePostInterface client =
SimplePostFactory.getInstance(
appConfig,
tokenUrl,
params.toArray(new BasicNameValuePair[0]));

client.post();

if (!client.isSuccessful()) {
logger.error("Refresh token request failed with status: "+ client.getStatusCode());
logErrorResponse(client.getInput());
return null;
}

Map<?, ?> response =
parseJsonResponse(client.getInput());

String accessToken = (String) response.get("access_token");

String newRefreshToken =
(String) response.get("refresh_token");

if (accessToken != null) {
appConfig.setValue(
AppConfig.PROP_OAUTH_ACCESSTOKEN,
accessToken);
}

if (newRefreshToken != null) {

logger.info(
"Updated refresh token received. Length="
+ newRefreshToken.length());

appConfig.setValue(
AppConfig.PROP_OAUTH_REFRESHTOKEN,
newRefreshToken);
appConfig.markDirty();
appConfig.save();

logger.info(
"Updated refresh token saved to config");

}

return accessToken;
}
/**
* Handles the OAuth callback from Salesforce.
*/
Expand Down