Skip to content
Merged
17 changes: 0 additions & 17 deletions libs/SalesforceSDK/AndroidManifest.xml
Original file line number Diff line number Diff line change
Expand Up @@ -69,23 +69,6 @@
android:theme="@style/SalesforceSDK"
android:exported="true" />

<!-- Dev info activity -->
<activity android:name="com.salesforce.androidsdk.ui.DevInfoActivity"
android:theme="@style/SalesforceSDK"
android:exported="false" />

<!-- Login Options activity -->
<activity android:name="com.salesforce.androidsdk.ui.LoginOptionsActivity"
android:theme="@style/SalesforceSDK"
android:exported="false" />

<!-- Test Authentication Activity For Automated Testing. This activity is only functional for debug builds of the app using Salesforce Mobile SDK -->
<activity
android:name="com.salesforce.androidsdk.util.test.TestAuthenticationActivity"
android:excludeFromRecents="true"
android:exported="true"
android:theme="@style/SalesforceSDK" />

<!-- Receiver in SP app for IDP-SP login flows -->
<receiver android:name="com.salesforce.androidsdk.auth.idp.SPReceiver"
android:exported="true"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -92,7 +92,6 @@ import com.salesforce.androidsdk.app.SalesforceSDKManager.Theme.SYSTEM_DEFAULT
import com.salesforce.androidsdk.auth.AuthenticatorService.KEY_INSTANCE_URL
import com.salesforce.androidsdk.auth.HttpAccess
import com.salesforce.androidsdk.auth.HttpAccess.DEFAULT
import com.salesforce.androidsdk.auth.JwtAccessToken
import com.salesforce.androidsdk.auth.NativeLoginManager
import com.salesforce.androidsdk.auth.OAuth2.LogoutReason
import com.salesforce.androidsdk.auth.OAuth2.LogoutReason.UNKNOWN
Expand All @@ -107,6 +106,7 @@ import com.salesforce.androidsdk.config.LoginServerManager
import com.salesforce.androidsdk.config.LoginServerManager.PRODUCTION_LOGIN_URL
import com.salesforce.androidsdk.config.LoginServerManager.SANDBOX_LOGIN_URL
import com.salesforce.androidsdk.config.LoginServerManager.WELCOME_LOGIN_URL
import com.salesforce.androidsdk.config.OAuthConfig
import com.salesforce.androidsdk.config.RuntimeConfig.ConfigKey.IDPAppPackageName
import com.salesforce.androidsdk.config.RuntimeConfig.getRuntimeConfig
import com.salesforce.androidsdk.developer.support.DevSupportInfo
Expand Down Expand Up @@ -149,11 +149,9 @@ import kotlinx.coroutines.Dispatchers.Main
import kotlinx.coroutines.launch
import kotlinx.coroutines.withTimeoutOrNull
import okhttp3.HttpUrl.Companion.toHttpUrlOrNull
import org.json.JSONObject
import org.jetbrains.annotations.Debug
import java.lang.String.CASE_INSENSITIVE_ORDER
import java.net.URI
import java.text.SimpleDateFormat
import java.util.Locale
import java.util.Locale.US
import java.util.SortedSet
import java.util.UUID.randomUUID
Expand Down Expand Up @@ -200,7 +198,7 @@ open class SalesforceSDKManager protected constructor(
*
* @return The class for the main activity.
*/
val mainActivityClass: Class<out Activity>
val mainActivityClass: Class<out Activity> = mainActivity

/**
* Null or an authenticated Activity for private use when developer support
Expand Down Expand Up @@ -234,6 +232,16 @@ open class SalesforceSDKManager protected constructor(
*/
var loginViewModelFactory = LoginViewModel.Factory

/**
* Asynchronously retrieves the app config for the specified login host. If not set or null is
* returned the values found in the BootConfig file will be used for all servers.
*/
var appConfigForLoginHost: suspend (server: String) -> OAuthConfig? = {
OAuthConfig(getBootConfig(appContext))
}

internal var debugOverrideAppConfig: OAuthConfig? = null

/** The class for the account switcher activity */
var accountSwitcherActivityClass = AccountSwitcherActivity::class.java

Expand Down Expand Up @@ -374,7 +382,6 @@ open class SalesforceSDKManager protected constructor(
@set:Synchronized
var useWebServerAuthentication = true


/**
* Whether or not the app supports welcome discovery. This should only be
* enabled if the connected app is supported.
Expand Down Expand Up @@ -432,6 +439,7 @@ open class SalesforceSDKManager protected constructor(
return _lightColorScheme ?: sfLightColors().also { _lightColorScheme = it }
}

@Suppress("unused")
fun setLightColorScheme(value: ColorScheme) {
_lightColorScheme = value
}
Expand All @@ -447,6 +455,7 @@ open class SalesforceSDKManager protected constructor(
return _darkColorScheme ?: sfDarkColors().also { _darkColorScheme = it }
}

@Suppress("unused")
fun setDarkColorScheme(value: ColorScheme) {
_darkColorScheme = value
}
Expand Down Expand Up @@ -531,7 +540,6 @@ open class SalesforceSDKManager protected constructor(

/** Initializer */
init {
mainActivityClass = mainActivity
features = ConcurrentSkipListSet(CASE_INSENSITIVE_ORDER)

/*
Expand Down Expand Up @@ -615,7 +623,7 @@ open class SalesforceSDKManager protected constructor(
communityUrl: String,
reCaptchaSiteKeyId: String? = null,
googleCloudProjectId: String? = null,
isReCaptchaEnterprise: Boolean = false
isReCaptchaEnterprise: Boolean = false,
): NativeLoginManagerInterface {
registerUsedAppFeature(FEATURE_NATIVE_LOGIN)
nativeLoginManager = NativeLoginManager(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -111,7 +111,9 @@ internal suspend fun onAuthFlowComplete(
handleScreenLockPolicy: (userIdentity: OAuth2.IdServiceResponse?, account: UserAccount) -> Unit = ::handleScreenLockPolicy,
handleBiometricAuthPolicy: (userIdentity: OAuth2.IdServiceResponse?, account: UserAccount) -> Unit = ::handleBiometricAuthPolicy,
handleDuplicateUserAccount: (userAccountManager: UserAccountManager, account: UserAccount, userIdentity: OAuth2.IdServiceResponse?) -> Unit = ::handleDuplicateUserAccount,
) {
) {
// Reset Dev Support LoginOptionsActivity override
SalesforceSDKManager.getInstance().debugOverrideAppConfig = null

// Note: Can't use default parameter value for suspended function parameter fetchUserIdentity
val actualFetchUserIdentity = fetchUserIdentity ?: ::fetchUserIdentity
Expand Down Expand Up @@ -140,14 +142,7 @@ internal suspend fun onAuthFlowComplete(
w(TAG, "Missing refresh token scope.")
}

// Check that the tokenResponse.scope contains the identity scope before calling the identity service
val userIdentity = if (scopeParser.hasIdentityScope()) {
actualFetchUserIdentity(tokenResponse)
} else {
w(TAG, "Missing identity scope, skipping identity service call.")
null
}

val userIdentity = actualFetchUserIdentity(tokenResponse)
val mustBeManagedApp = userIdentity?.customPermissions?.optBoolean(MUST_BE_MANAGED_APP_PERM) ?: false
if (mustBeManagedApp && !runtimeConfig.isManagedApp) {
onAuthFlowError(
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
/*
* Copyright (c) 2025-present, salesforce.com, inc.
* All rights reserved.
* Redistribution and use of this software in source and binary forms, with or
* without modification, are permitted provided that the following conditions
* are met:
* - Redistributions of source code must retain the above copyright notice, this
* list of conditions and the following disclaimer.
* - Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
* - Neither the name of salesforce.com, inc. nor the names of its contributors
* may be used to endorse or promote products derived from this software without
* specific prior written permission of salesforce.com, inc.
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*/
package com.salesforce.androidsdk.config

data class OAuthConfig(
val consumerKey: String,
val redirectUri: String,
val scopes: List<String>? = null,
) {

internal constructor(bootConfig: BootConfig): this(
bootConfig.remoteAccessConsumerKey,
bootConfig.oauthRedirectURI,
scopes = bootConfig.oauthScopes?.ifEmpty { null }?.toList(),
)

// Used by LoginOptionsActivity
internal constructor(consumerKey: String, redirectUri: String, scopes: String): this(
consumerKey.trim(),
redirectUri.trim(),
scopes = with(scopes) {
if (isNullOrBlank()) return@with null

return@with if (contains(",")) {
split(",")
} else {
split(" ")
}.map { it.trim() }
}
)

// Used by LoginOptionsActivity
internal val scopesString
get() = scopes?.joinToString(separator = " ") ?: ""
}
Original file line number Diff line number Diff line change
Expand Up @@ -157,9 +157,9 @@ data class DevSupportInfo(
"Consumer Key" to currentUser.clientId,
"Scopes" to currentUser.scope,
"Instance URL" to currentUser.instanceServer,
"Token Format" to currentUser.tokenFormat,
"Token Format" to (currentUser.tokenFormat?.ifBlank { "Opaque" } ?: "Opaque"),
"Access Token Expiration" to accessTokenExpiration,
"Beacon Child Consumer Key" to currentUser.beaconChildConsumerKey,
"Beacon Child Consumer Key" to (currentUser.beaconChildConsumerKey ?: "None"),
)
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -983,7 +983,7 @@ open class LoginActivity : FragmentActivity() {
) = salesforceWelcomeDiscoveryHostAndPathUrl.buildUpon()
.appendQueryParameter(
SALESFORCE_WELCOME_DISCOVERY_MOBILE_URL_QUERY_PARAMETER_KEY_CLIENT_ID,
viewModel.bootConfig.remoteAccessConsumerKey
viewModel.oAuthConfig.redirectUri,
)
.appendQueryParameter(
SALESFORCE_WELCOME_DISCOVERY_MOBILE_URL_QUERY_PARAMETER_KEY_CLIENT_VERSION,
Expand Down Expand Up @@ -1138,7 +1138,7 @@ open class LoginActivity : FragmentActivity() {
}

val formattedUrl = request.url.toString().replace("///", "/").lowercase()
val callbackUrl = viewModel.bootConfig.oauthRedirectURI.replace("///", "/").lowercase()
val callbackUrl = viewModel.oAuthConfig.redirectUri.replace("///", "/").lowercase()
val authFlowFinished = formattedUrl.startsWith(callbackUrl)

if (authFlowFinished) {
Expand Down
Loading