-
Notifications
You must be signed in to change notification settings - Fork 393
Token Migration #2837
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Token Migration #2837
Changes from all commits
5fa7cf7
9275eb3
cbdf7fa
4662ecd
71fb91f
33d95e1
ca7876b
a69b73d
b63665f
d6d948e
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change | ||
|---|---|---|---|---|
|
|
@@ -8,6 +8,7 @@ plugins { | |||
| `publish-module` | ||||
| jacoco | ||||
| kotlin("plugin.serialization") version "2.0.21" | ||||
| kotlin("plugin.parcelize") | ||||
| } | ||||
|
|
||||
| dependencies { | ||||
|
|
@@ -48,6 +49,7 @@ dependencies { | |||
|
|
||||
| androidTestImplementation("androidx.test:runner:1.7.0") | ||||
| androidTestImplementation("androidx.test:rules:1.7.0") | ||||
| androidTestImplementation("androidx.test.espresso:espresso-core:3.7.0") | ||||
| androidTestImplementation("androidx.test.ext:junit:1.3.0") | ||||
| androidTestImplementation("androidx.arch.core:core-testing:2.2.0") | ||||
| androidTestImplementation("androidx.compose.ui:ui-test-junit4:$composeVersion") | ||||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
|
||||
|
|
||||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,111 @@ | ||
| /* | ||
| * Copyright (c) 2026-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.accounts | ||
|
|
||
| import android.content.Intent | ||
| import com.salesforce.androidsdk.accounts.UserAccountManager.getInstance | ||
| import com.salesforce.androidsdk.app.SalesforceSDKManager | ||
| import com.salesforce.androidsdk.config.OAuthConfig | ||
| import com.salesforce.androidsdk.ui.TokenMigrationActivity | ||
| import com.salesforce.androidsdk.util.SalesforceSDKLogger | ||
| import java.util.UUID | ||
|
|
||
| const val TAG = "UserAccountManager" | ||
|
|
||
| /** | ||
| * Attempts to migrate the [userAccount] to the provided Connected App or | ||
| * External Client Application [appConfig]. | ||
| * | ||
| * This might cause the approve/deny screen to be presented to the user to authorize the | ||
| * new app. If successful a new set of credentials (refresh token, access token) are obtained | ||
| * and replace the existing credentials for the user. | ||
| */ | ||
| @Suppress("UnusedReceiverParameter") | ||
| fun UserAccountManager.migrateRefreshToken( | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Taking the success and error callbacks as up-front parameters works so no change required. I wondered if this could benefit from returning a
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I had the same thought. Originally I had a function in the AppFlowTester's I don't think it makes sense to change the signature to Result since the lambdas passed in flow all the way through |
||
| userAccount: UserAccount? = getInstance().currentUser, | ||
| appConfig: OAuthConfig, | ||
| onMigrationSuccess: (userAccount: UserAccount) -> Unit, | ||
| onMigrationError: (error: String, errorDesc: String?, e: Throwable?) -> Unit, | ||
| ) { | ||
| val loggedOnSuccess: (userAccount: UserAccount) -> Unit = { user -> | ||
| SalesforceSDKLogger.i(TAG, "Token Migration Successful \n\nUser ${user.username} " + | ||
| "(${user.instanceServer}) successfully migrated to: \n$appConfig.") | ||
| onMigrationSuccess.invoke(user) | ||
| } | ||
| val userId = userAccount?.userId | ||
| val orgId = userAccount?.orgId | ||
|
|
||
| if (userId == null || orgId == null) { | ||
| val message = "User account, userId or orgId is null." | ||
| SalesforceSDKLogger.e(TAG, message) | ||
| onMigrationError(message, null, null) | ||
| return | ||
| } | ||
|
|
||
| val callbackKey = MigrationCallbackRegistry.register( | ||
| callbacks = MigrationCallbackRegistry.MigrationCallbacks( | ||
| onMigrationSuccess = loggedOnSuccess, | ||
| onMigrationError = onMigrationError, | ||
| ) | ||
| ) | ||
|
|
||
| with(SalesforceSDKManager.getInstance().appContext) { | ||
| startActivity( | ||
| Intent(/* packageContext = */ this, TokenMigrationActivity::class.java).apply { | ||
| addFlags(Intent.FLAG_ACTIVITY_NEW_TASK) | ||
| putExtra(TokenMigrationActivity.EXTRA_ORG_ID, orgId) | ||
| putExtra(TokenMigrationActivity.EXTRA_USER_ID, userId) | ||
| putExtra(TokenMigrationActivity.EXTRA_OAUTH_CONFIG, appConfig) | ||
| putExtra(TokenMigrationActivity.EXTRA_CALLBACK_ID, callbackKey) | ||
| } | ||
| ) | ||
| } | ||
| } | ||
|
|
||
| /* | ||
| This mechanism is used to pass a _string_ id to the Activity to retrieve callback functions. | ||
|
|
||
| Lambda functions may appear Parcelable/Serializable but since we cannot guarantee the | ||
| content are they should not be passed. For instance, if the lambda function contains | ||
| compose state an exception will be thrown. | ||
| */ | ||
| internal object MigrationCallbackRegistry { | ||
| private val callbacks = mutableMapOf<String, MigrationCallbacks>() | ||
|
|
||
| data class MigrationCallbacks( | ||
| val onMigrationSuccess: (UserAccount) -> Unit, | ||
| val onMigrationError: (String, String?, Throwable?) -> Unit | ||
| ) | ||
|
|
||
| fun register(callbacks: MigrationCallbacks): String { | ||
| val key = UUID.randomUUID().toString() | ||
| this.callbacks[key] = callbacks | ||
| return key | ||
| } | ||
|
|
||
| fun consume(key: String): MigrationCallbacks? = callbacks.remove(key) | ||
| } | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -26,11 +26,15 @@ | |
| */ | ||
| package com.salesforce.androidsdk.config | ||
|
|
||
| import android.os.Parcelable | ||
| import kotlinx.parcelize.Parcelize | ||
|
|
||
| @Parcelize | ||
|
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This is Kotlin magic ✨. The compiler is able to implement the required functions to satisfy the We should do this for |
||
| data class OAuthConfig( | ||
| val consumerKey: String, | ||
| val redirectUri: String, | ||
| val scopes: List<String>? = null, | ||
| ) { | ||
| ): Parcelable { | ||
|
|
||
| internal constructor(bootConfig: BootConfig): this( | ||
| bootConfig.remoteAccessConsumerKey, | ||
|
|
||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Should I rename this style to something more generic?