Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
20 commits
Select commit Hold shift + click to select a range
5cecb66
@W-22699714: [Android] Improve error handling at code exchange
JohnsonEricAtSalesforce Jun 17, 2026
0c9a8b8
fix(test): correct MockK static mock pattern for OAuth2.exchangeCode
JohnsonEricAtSalesforce Jun 17, 2026
2da22da
Remove LoginActivityAuthErrorTest — fragile Activity-based tests
JohnsonEricAtSalesforce Jun 18, 2026
d5adffb
Rename toastMessage to message in onAuthFlowError
JohnsonEricAtSalesforce Jun 18, 2026
519d32c
Remove redundant semicolon in LoginActivity
JohnsonEricAtSalesforce Jun 18, 2026
96bff10
Remove redundant semicolon in LoginActivity
JohnsonEricAtSalesforce Jun 18, 2026
96148a2
Remove redundant semicolon in LoginActivity
JohnsonEricAtSalesforce Jun 18, 2026
f1b65ef
Remove redundant return in LoginActivity
JohnsonEricAtSalesforce Jun 18, 2026
c9e9693
Fix KDoc @param name mismatch in LoginActivity
JohnsonEricAtSalesforce Jun 18, 2026
cd50873
Fix KDoc @param name mismatch in LoginActivity
JohnsonEricAtSalesforce Jun 18, 2026
54f68fa
Fix KDoc @param name mismatch in LoginActivity
JohnsonEricAtSalesforce Jun 18, 2026
9824a0e
Remove redundant inner modifier in LoginActivity
JohnsonEricAtSalesforce Jun 18, 2026
d472656
Prefer static imports in doCodeExchange tests
JohnsonEricAtSalesforce Jun 18, 2026
0b8b79d
Optimize imports
JohnsonEricAtSalesforce Jun 18, 2026
16fa5e6
Use static import for TIMESTAMP_FORMAT in tests
JohnsonEricAtSalesforce Jun 18, 2026
c9f33df
Remove unnecessary Thread.sleep from doCodeExchange error tests
JohnsonEricAtSalesforce Jun 18, 2026
05da06f
Use static import for exchangeCode in tests
JohnsonEricAtSalesforce Jun 18, 2026
fc07157
Strengthen doCodeExchange error path tests
JohnsonEricAtSalesforce Jun 18, 2026
47436fb
Update app blocked error message to reference verification
JohnsonEricAtSalesforce Jun 18, 2026
7c29e77
Also recognize CLIENT_BLOCKED_RETRY_ERROR at code exchange
JohnsonEricAtSalesforce Jun 18, 2026
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
1 change: 1 addition & 0 deletions libs/SalesforceSDK/res/values/sf__strings.xml
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
<string name="sf__managed_app_error">Authentication only allowed from managed device.</string>
<string name="sf__jwt_authentication_error">JWT authentication error. Please try again.</string>
<string name="sf__lightning_url_code_exchange_error">Lightning URLs are not supported for OAuth code exchange. Use your My Domain URL instead.</string>
<string name="sf__app_blocked_error">This app could not be verified. Contact support.</string>

<!-- SSL errors -->
<string name="sf__ssl_error">SSL error: %s.</string>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -106,6 +106,7 @@ import com.salesforce.androidsdk.R.color.sf__background
import com.salesforce.androidsdk.R.color.sf__background_dark
import com.salesforce.androidsdk.R.drawable.sf__action_back
import com.salesforce.androidsdk.R.string.cannot_use_another_apps_login_qr_code
import com.salesforce.androidsdk.R.string.sf__app_blocked_error
import com.salesforce.androidsdk.R.string.sf__biometric_opt_in_title
import com.salesforce.androidsdk.R.string.sf__generic_authentication_error_title
import com.salesforce.androidsdk.R.string.sf__jwt_authentication_error
Expand All @@ -125,6 +126,8 @@ import com.salesforce.androidsdk.app.Features.FEATURE_WELCOME_DISCOVERY_LOGIN
import com.salesforce.androidsdk.app.SalesforceSDKManager
import com.salesforce.androidsdk.app.SalesforceSDKManager.Theme.DARK
import com.salesforce.androidsdk.auth.HttpAccess
import com.salesforce.androidsdk.auth.OAuth2.CLIENT_BLOCKED_ERROR
import com.salesforce.androidsdk.auth.OAuth2.CLIENT_BLOCKED_RETRY_ERROR
import com.salesforce.androidsdk.auth.OAuth2.OAuthFailedException
import com.salesforce.androidsdk.auth.OAuth2.TokenEndpointResponse
import com.salesforce.androidsdk.auth.OAuth2.swapJWTForTokens
Expand Down Expand Up @@ -228,7 +231,7 @@ open class LoginActivity : FragmentActivity() {
}

// Private variables
private var baseUserAgentString = "";
private var baseUserAgentString = ""
private var wasBackgrounded = false
private var accountAuthenticatorResponse: AccountAuthenticatorResponse? = null
private var accountAuthenticatorResult: Bundle? = null
Expand Down Expand Up @@ -584,6 +587,9 @@ open class LoginActivity : FragmentActivity() {
)

viewModel.clearCookies()
val isClientBlocked = e is OAuthFailedException
Comment thread
JohnsonEricAtSalesforce marked this conversation as resolved.
&& (e.tokenErrorResponse.error == CLIENT_BLOCKED_ERROR
|| e.tokenErrorResponse.error == CLIENT_BLOCKED_RETRY_ERROR)
val isLightningTokenEndpointFailure = e is OAuthFailedException
&& e.tokenErrorResponse.error == "unsupported_grant_type"
&& viewModel.selectedServer.value?.contains(".lightning.") == true
Expand All @@ -592,11 +598,12 @@ open class LoginActivity : FragmentActivity() {
}
// Displays the error in a toast, clears cookies and reloads the login page
runOnUiThread {
if (isLightningTokenEndpointFailure) {
makeText(this, getString(sf__lightning_url_code_exchange_error), LENGTH_LONG).show()
} else {
makeText(this, "$error : $errorDesc", LENGTH_LONG).show()
val message = when {
isClientBlocked -> getString(sf__app_blocked_error)
isLightningTokenEndpointFailure -> getString(sf__lightning_url_code_exchange_error)
else -> "$error : $errorDesc"
}
makeText(this, message, LENGTH_LONG).show()
viewModel.reloadWebView()
}
}
Expand Down Expand Up @@ -936,12 +943,12 @@ open class LoginActivity : FragmentActivity() {
// Set welcome discovery feature flag if applicable
if (isLoginWithWelcomeDiscovery(intent)) {
SalesforceSDKManager.getInstance()
.registerUsedAppFeature(FEATURE_WELCOME_DISCOVERY_LOGIN);
.registerUsedAppFeature(FEATURE_WELCOME_DISCOVERY_LOGIN)
}
else {
SalesforceSDKManager.getInstance().unregisterUsedAppFeature(
FEATURE_WELCOME_DISCOVERY_LOGIN
);
)
}

// Re-apply user agent to WebView
Expand Down Expand Up @@ -1125,7 +1132,7 @@ open class LoginActivity : FragmentActivity() {
loginHint = uri.getQueryParameter(SALESFORCE_WELCOME_DISCOVERY_MOBILE_CALLBACK_URL_QUERY_PARAMETER_KEY_LOGIN_HINT) ?: return false,
loginHost = uri.getQueryParameter(SALESFORCE_WELCOME_DISCOVERY_MOBILE_CALLBACK_URL_QUERY_PARAMETER_KEY_MY_DOMAIN)?.toUri()?.host ?: return false
)
return true
true
} else false
}

Expand Down Expand Up @@ -1534,7 +1541,7 @@ open class LoginActivity : FragmentActivity() {
/**
* Determines if the provided URL has the Salesforce Welcome Discovery
* path.
* @param url The URL to examine for the Salesforce Welcome Discovery
* @param uri The URL to examine for the Salesforce Welcome Discovery
* path
* @return Boolean true if the URL has the Salesforce Welcome Discovery
* path or false otherwise
Expand All @@ -1549,7 +1556,7 @@ open class LoginActivity : FragmentActivity() {
* Determines if the provided URL has the Salesforce Welcome Discovery
* path and parameters for mobile callback. The client id (consumer
* key) of the URL must match the boot config's consumer key.
* @param url The URL to examine for the Salesforce Welcome Discovery
* @param uri The URL to examine for the Salesforce Welcome Discovery
* path and parameters for mobile callback
* @return Boolean true if the URL has the Salesforce Welcome Discovery
* path and parameters for mobile callback and matches the boot config's
Expand All @@ -1575,7 +1582,7 @@ open class LoginActivity : FragmentActivity() {
* Determines if the provided URL has the Salesforce Welcome Discovery
* path and parameters for mobile callback. The client id (consumer
* key) of the URL must match the boot config's consumer key.
* @param url The URL to examine for the Salesforce Welcome Discovery
* @param uri The URL to examine for the Salesforce Welcome Discovery
* path and parameters for mobile callback
* @return Boolean true if the URL has the Salesforce Welcome Discovery
* path and parameters for mobile callback and matches the boot config's
Expand Down Expand Up @@ -1654,7 +1661,7 @@ open class LoginActivity : FragmentActivity() {
* Activity result callback for the "Login for Admin" custom tab.
*/
@VisibleForTesting
internal inner class AdminCustomTabActivityResult : ActivityResultCallback<ActivityResult> {
internal class AdminCustomTabActivityResult : ActivityResultCallback<ActivityResult> {
override fun onActivityResult(result: ActivityResult) {
// Intentional no-op: keep the existing WebView visible on cancel.
}
Expand Down
Loading
Loading