Skip to content

@W-21933885: [MSDK Android] App Attestation Implementation#2868

Merged
JohnsonEricAtSalesforce merged 66 commits intoforcedotcom:devfrom
JohnsonEricAtSalesforce:feature/w-21933885_msdk-android-app-attestation-implementation
May 7, 2026
Merged

@W-21933885: [MSDK Android] App Attestation Implementation#2868
JohnsonEricAtSalesforce merged 66 commits intoforcedotcom:devfrom
JohnsonEricAtSalesforce:feature/w-21933885_msdk-android-app-attestation-implementation

Conversation

@JohnsonEricAtSalesforce
Copy link
Copy Markdown
Contributor

@JohnsonEricAtSalesforce JohnsonEricAtSalesforce commented Apr 15, 2026

🎸 Ready For Final Review - Test Failures Appear Unrelated 🥁

This adds Mobile SDK Android support for:

  1. The new "Challenge" API offered by the Salesforce App Attestation External Client App (ECA) Plug-In
    1.1. The "Challenge" API creates challenges for use as the "hash" when creating Google Play App Integrity API "tokens"
  2. Google Play App Integrity API
    2.1. Tokens can be created using the "Challenge" and then passed to authorization and token refresh requests for validation by the App Attestation ECA Plug-In

The app's entry point is calling a new "updateAppAttestationClient" method on the SalesforceSDKManager with the relevant Google Cloud Project ID. Once that's provided, the manager and the new AppAttestationClient manage the interaction with the Challenge API and Google Play Integrity API. The AppAttestationClient provides "Attestation" values to the existing authorization and token refresh logic when applicable.

Most of the new code follows our existing patterns. I'll include a source code walk-though in the differences as well. None of this is set in stone and we can incorporate feedback here or in follow-up work items.

I've tested this by creating a new test app from the REST Explorer sample. That app is configured for deployment to a Google Play Developer account that is configured for App Integrity with a matching Google Cloud project. I've been able to deploy the app to my internal release track. When installed from there, I'm able to successfully authenticate to the internal MSDK App Attestation Test org I've been given access to.

Test coverage is nearing 100% which is great given authentication's critical priority and high availability threshold.

The Google Play App Integrity API has public documentation here https://developer.android.com/google/play/integrity/overview

There's no user interface component for this change in either mobile or web for screenshots or a demo, alas!

Here's an example of how the client app could (re-)initialize app attestation each time the selected login server changes.

        // Prepare for Salesforce App Attestation.
        getInstance().loginServerManager.selectedServer.observeForever { selectedLoginServer ->
            selectedLoginServer.url.toUri().host.let { selectedLoginServerHost ->
                if (selectedLoginServerHost == "msdkappattestationtestorg.test1.my.pc-rnd.salesforce.com") {
                    Pair<String?, Long?>(selectedLoginServerHost, 762473690072L)
                } else {
                    Pair<String?, Long?>(selectedLoginServerHost, null)
                }
            }.let { appAttestationConfiguration ->
                getInstance().updateAppAttestationClient(appAttestationConfiguration.first ?: "_Ignored_Value_", appAttestationConfiguration.second)
            }
        }

@github-actions
Copy link
Copy Markdown

github-actions Bot commented Apr 15, 2026

24 Warnings
⚠️ libs/SalesforceSDK/build.gradle.kts#L20 - A newer version of com.squareup.okhttp3:okhttp than 4.12.0 is available: 5.3.2
⚠️ libs/SalesforceSDK/build.gradle.kts#L21 - A newer version of com.google.firebase:firebase-messaging than 25.0.0 is available: 25.0.1
⚠️ libs/SalesforceSDK/build.gradle.kts#L22 - A newer version of androidx.core:core than 1.16.0 is available: 1.18.0
⚠️ libs/SalesforceSDK/build.gradle.kts#L32 - A newer version of androidx.core:core-ktx than 1.16.0 is available: 1.18.0
⚠️ libs/SalesforceSDK/build.gradle.kts#L33 - A newer version of androidx.activity:activity-ktx than 1.10.1 is available: 1.13.0
⚠️ libs/SalesforceSDK/build.gradle.kts#L34 - A newer version of androidx.activity:activity-compose than 1.10.1 is available: 1.13.0
⚠️ libs/SalesforceSDK/build.gradle.kts#L35 - A newer version of androidx.lifecycle:lifecycle-viewmodel-ktx than 2.8.7 is available: 2.10.0
⚠️ libs/SalesforceSDK/build.gradle.kts#L36 - A newer version of androidx.lifecycle:lifecycle-viewmodel-compose than 2.8.7 is available: 2.10.0
⚠️ libs/SalesforceSDK/build.gradle.kts#L37 - A newer version of androidx.lifecycle:lifecycle-viewmodel-savedstate than 2.8.7 is available: 2.10.0
⚠️ libs/SalesforceSDK/build.gradle.kts#L38 - A newer version of androidx.lifecycle:lifecycle-service than 2.8.7 is available: 2.10.0
⚠️ libs/SalesforceSDK/build.gradle.kts#L39 - A newer version of org.jetbrains.kotlinx:kotlinx-serialization-json than 1.6.3 is available: 1.11.0
⚠️ libs/SalesforceSDK/build.gradle.kts#L40 - A newer version of androidx.window:window than 1.4.0 is available: 1.5.1
⚠️ libs/SalesforceSDK/build.gradle.kts#L41 - A newer version of androidx.window:window-core than 1.4.0 is available: 1.5.1
⚠️ libs/SalesforceSDK/build.gradle.kts#L42 - A newer version of androidx.compose.material3:material3-android than 1.3.2 is available: 1.4.0
⚠️ libs/SalesforceSDK/build.gradle.kts#L43 - A newer version of androidx.compose:compose-bom than 2025.07.00 is available: 2026.04.01
⚠️ libs/SalesforceSDK/build.gradle.kts#L44 - A newer version of androidx.compose.foundation:foundation-android than 1.8.2 is available: 1.11.0
⚠️ libs/SalesforceSDK/build.gradle.kts#L45 - A newer version of androidx.compose.runtime:runtime-livedata than 1.8.2 is available: 1.11.0
⚠️ libs/SalesforceSDK/build.gradle.kts#L46 - A newer version of androidx.compose.ui:ui-tooling-preview-android than 1.8.2 is available: 1.11.0
⚠️ libs/SalesforceSDK/build.gradle.kts#L47 - A newer version of androidx.compose.material:material than 1.8.2 is available: 1.11.0
⚠️ libs/SalesforceSDK/build.gradle.kts#L49 - A newer version of androidx.compose.ui:ui-tooling than 1.8.2 is available: 1.11.0
⚠️ libs/SalesforceSDK/build.gradle.kts#L50 - A newer version of androidx.compose.ui:ui-test-manifest than 1.8.2 is available: 1.11.0
⚠️ libs/SalesforceSDK/build.gradle.kts#L57 - A newer version of androidx.compose.ui:ui-test-junit4 than 1.8.2 is available: 1.11.0
⚠️ libs/SalesforceSDK/build.gradle.kts#L58 - A newer version of io.mockk:mockk-android than 1.14.0 is available: 1.14.9
⚠️ libs/SalesforceSDK/src/com/salesforce/androidsdk/auth/NativeLoginManager.kt#L249 - This method should only be accessed from tests or within private scope

Generated by 🚫 Danger

@codecov
Copy link
Copy Markdown

codecov Bot commented Apr 15, 2026

Codecov Report

❌ Patch coverage is 5.88235% with 128 lines in your changes missing coverage. Please review.
✅ Project coverage is 40.45%. Comparing base (50b4c43) to head (480c38a).
⚠️ Report is 2 commits behind head on dev.

Files with missing lines Patch % Lines
...salesforce/androidsdk/auth/AppAttestationClient.kt 0.00% 64 Missing ⚠️
...ndroidsdk/rest/AppAttestationChallengeApiClient.kt 0.00% 13 Missing ⚠️
...src/com/salesforce/androidsdk/ui/LoginViewModel.kt 13.33% 10 Missing and 3 partials ⚠️
.../salesforce/androidsdk/app/SalesforceSDKManager.kt 14.28% 12 Missing ⚠️
...m/salesforce/androidsdk/auth/NativeLoginManager.kt 0.00% 8 Missing ⚠️
...alesforce/androidsdk/auth/idp/IDPAuthCodeHelper.kt 0.00% 8 Missing ⚠️
...SDK/src/com/salesforce/androidsdk/auth/OAuth2.java 36.36% 4 Missing and 3 partials ⚠️
...oidsdk/rest/AppAttestationChallengeApiException.kt 0.00% 3 Missing ⚠️

❌ Your patch check has failed because the patch coverage (5.88%) is below the target coverage (80.00%). You can increase the patch coverage or adjust the target coverage.

❗ There is a different number of reports uploaded between BASE (50b4c43) and HEAD (480c38a). Click for more details.

HEAD has 1 upload less than BASE
Flag BASE (50b4c43) HEAD (480c38a)
SalesforceSDK 2 1
Additional details and impacted files
@@              Coverage Diff              @@
##                dev    #2868       +/-   ##
=============================================
- Coverage     64.87%   40.45%   -24.43%     
+ Complexity     2979      858     -2121     
=============================================
  Files           223      126       -97     
  Lines         17507     9542     -7965     
  Branches       2497     1438     -1059     
=============================================
- Hits          11358     3860     -7498     
- Misses         4937     5080      +143     
+ Partials       1212      602      -610     
Components Coverage Δ
Analytics ∅ <ø> (∅)
SalesforceSDK 40.45% <5.88%> (-19.32%) ⬇️
Hybrid ∅ <ø> (∅)
SmartStore ∅ <ø> (∅)
MobileSync ∅ <ø> (∅)
React ∅ <ø> (∅)
Files with missing lines Coverage Δ
...oidsdk/rest/AppAttestationChallengeApiException.kt 0.00% <0.00%> (ø)
...SDK/src/com/salesforce/androidsdk/auth/OAuth2.java 61.14% <36.36%> (-16.13%) ⬇️
...m/salesforce/androidsdk/auth/NativeLoginManager.kt 0.00% <0.00%> (-10.49%) ⬇️
...alesforce/androidsdk/auth/idp/IDPAuthCodeHelper.kt 0.00% <0.00%> (-6.85%) ⬇️
.../salesforce/androidsdk/app/SalesforceSDKManager.kt 48.93% <14.28%> (-10.52%) ⬇️
...ndroidsdk/rest/AppAttestationChallengeApiClient.kt 0.00% <0.00%> (ø)
...src/com/salesforce/androidsdk/ui/LoginViewModel.kt 45.01% <13.33%> (-42.90%) ⬇️
...salesforce/androidsdk/auth/AppAttestationClient.kt 0.00% <0.00%> (ø)

... and 153 files with indirect coverage changes

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

@JohnsonEricAtSalesforce JohnsonEricAtSalesforce force-pushed the feature/w-21933885_msdk-android-app-attestation-implementation branch 2 times, most recently from 145a8a9 to 6990e6f Compare April 16, 2026 15:25
@github-actions
Copy link
Copy Markdown

1 Warning
⚠️ Big PR, try to keep changes smaller if you can.

Generated by 🚫 Danger

@JohnsonEricAtSalesforce JohnsonEricAtSalesforce force-pushed the feature/w-21933885_msdk-android-app-attestation-implementation branch 9 times, most recently from fa97a67 to 4076ab2 Compare April 17, 2026 21:37
@JohnsonEricAtSalesforce
Copy link
Copy Markdown
Contributor Author

The last mile of code coverage is taking some time to wrap up today. While I'm waiting on the last couple of CodeCov reports, I'll add some walk-through commentary even though I'd still recommend holding reviews until I mark the pull request Ready For Review.

@JohnsonEricAtSalesforce JohnsonEricAtSalesforce force-pushed the feature/w-21933885_msdk-android-app-attestation-implementation branch 3 times, most recently from 973e1dd to e374d58 Compare April 20, 2026 18:56
Comment thread .github/workflows/reusable-lib-workflow.yaml Outdated

if $IS_PR ; then
LEVELS_TO_TEST=$PR_API_VERSION
RETRIES=1
Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@brandonpage, a little later you'll see a fix in the RestClientTest.java when I had generated by our tools to resolve unreliable behavior I saw in that test. I've been marking it @Ignore a lot since it's so unpredictable. The fix looks solid - It's so solid that our tools believe we don't need retry anymore. I'd love to see a "faster fail" on pull request runs since they can take a very long time. Thoughts?

* TODO: Make this Kotlin-internal once it is no longer referenced by Java. ECJ20260420
*/
@Volatile
var appAttestationClient: AppAttestationClient? = null
Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

A crux change is creating a new object to encapsulate all the things for the new Salesforce "Challenge" API, the Integrity Token Provider, the Token and providing that in the "Attestation" format the auth and token refresh endpoints now expect. That's here.

Our tools had some great suggestion around making this property thread safe, so I added @volatile, the private setter and a dedicated lock object based on tool feedback.

* @param googleCloudProjectId The Google Cloud Project ID or null to
* disable Salesforce App Attestation
*/
fun updateAppAttestationClient(
Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If one reads the description of this pull request, this is the entry point for an app to actually enable App Attestation.

* @param restClient The REST client, usually provided by the Salesforce SDK
* Manager's unauthenticated REST client
*/
class AppAttestationClient(
Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This object is the heart 'n soul of App Attestation.

internal var integrityTokenProvider: StandardIntegrityTokenProvider? = null

init {
prepareIntegrityTokenProvider()
Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is the "warm up" for Google Play Integrity API, as we often call it in internal discussion and docs.

* @return The "attestation" value usable in Salesforce OAuth authorization
* and token refresh requests or null if the value cannot be created
*/
internal suspend fun createSalesforceOAuthAuthorizationAppAttestation(
Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The doc comment says it all and having detailed comments has really been adding context for our tools. This is where our auth and token refresh flows get the new attestation value.

integrityTokenProvider: StandardIntegrityTokenProvider? = this.integrityTokenProvider,
): String? {
// Guard to ensure the Google Play Integrity API Integrity Provider was asynchronously resolved or do so synchronously now.
val integrityTokenProviderResolved = integrityTokenProvider ?: prepareIntegrityTokenProvider().result
Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This guard helps resolve the case where LoginActivity generates the authorization URL faster than the integrity token provider can be warmed up. Our login flow gets to this point fast compared to what I believe Google Integrity API expected according to their documentation.

}.getOrElse { e ->
// If the Google Play Integrity API failed due to the Integrity Token Provider being expired, re-prepare it once for an inline retry.
if ((e as? IntegrityServiceException)?.errorCode == INTEGRITY_TOKEN_PROVIDER_INVALID) {
createSalesforceOAuthAuthorizationAppAttestation(
Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is a second way we guard by checking for Google Play Integrity API's documented "expired" code and issuing an inline retry.

* Client App (ECA) Plug-In.
* @return The Salesforce App Attestation ECA Plug-In's "Challenge"
*/
internal fun fetchSalesforceMobileAppAttestationChallenge(): String {
Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This fetches the new "Challenge" value from the org at the very beginning of the flow in our internal docs. If needed, I can send those internally.

)
val attestationValue = getInstance().appAttestationClient?.createSalesforceOAuthAuthorizationAppAttestation()
val authRequestBody = createRequestBody(
ATTESTATION to attestationValue,
Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Here's the attestation parameter in use in Native Login.

@JohnsonEricAtSalesforce JohnsonEricAtSalesforce force-pushed the feature/w-21933885_msdk-android-app-attestation-implementation branch from f86e876 to a8352aa Compare April 28, 2026 15:42
@JohnsonEricAtSalesforce
Copy link
Copy Markdown
Contributor Author

The is rebased on current dev which was a simple update with minimal conflicts.

@github-actions
Copy link
Copy Markdown

Job Summary for Gradle

Pull Request :: test-android
Gradle Root Project Requested Tasks Gradle Version Build Outcome Build Scan®
SalesforceMobileSDK-Android native:NativeSampleApps:AuthFlowTester:assembleDebug 8.14.3 Build Scan not published

@github-actions
Copy link
Copy Markdown

Job Summary for Gradle

Pull Request :: test-android
Gradle Root Project Requested Tasks Gradle Version Build Outcome Build Scan®
SalesforceMobileSDK-Android libs:SalesforceSDK:convertCodeCoverage 8.14.3 Build Scan not published
SalesforceMobileSDK-Android libs:SalesforceSDK:lint 8.14.3 Build Scan not published
SalesforceMobileSDK-Android libs:SalesforceSDK:assembleAndroidTest 8.14.3 Build Scan not published

…read-Unsafe Local Member References And Force-Unwrap From LoginViewModel.generateAuthorizationUrl)
@JohnsonEricAtSalesforce JohnsonEricAtSalesforce force-pushed the feature/w-21933885_msdk-android-app-attestation-implementation branch from a6a2c03 to b610e69 Compare April 30, 2026 17:18
val useHybridAuthentication = SalesforceSDKManager.getInstance().useHybridAuthentication

// Add Salesforce Mobile App Attestation parameter to authorization URL if applicable.
val additionalParams = appAttestationClient?.run {
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

For client side IDP-SP flow, how should app attestation work? Right now it looks like an attested IDP app could get a refresh token for a non-attested SP app. That could be problematic.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The part of the "attestation" that is specific to an org is the "challenge" that's generated before requesting the "token" from Google Play Integrity API. That said, the "attestation" parameter is ignored if it is not needed by the org. Would that make this a non-issue? What analysis do we have of the cross-matrix between IDP and App Attestation?

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I believe during a IDP / SP flow, the IDP gets the code and then the SP does the code to token exchange. As long as both use their own attestation values, we should be okay (both app would be attested).

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Any call to /authorize or /token will know include attestation so long as the host app has configured the Google Cloud Project Id and Challenge API Host. With that in mind, this should be solid. Do we have a test environment ready to try this out? If so, @wmathurin, send that to my direct message.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'll also update the internal test plan documentation with a task to cover this flow.

REDIRECT_URI to redirectUri,
CODE_CHALLENGE to codeChallenge,
)
val queryString = if (attestationValue != null) "?$ATTESTATION=$attestationValue" else ""
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why not have it in the request body with the other params?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The server didn't see the parameter in the request body when I tested a while back. It seems to be specifically coded to receive the parameter in the query parameters. Should we request a change to look in both locations?

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It feels weird to have it in the query string. IMHO we should request a change to look for it in the body.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Agreed. I have this updated in cc5f7db

* @param googleCloudProjectId The Google Cloud Project ID or null to
* disable Salesforce App Attestation
*/
fun updateAppAttestationClient(
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

How is it supposed to work? Is the app supposed to register the googleCloudProjectId before first login? Why is it apiHostName specific?
If it needs to be dynamic and decided at runtime, should we use the approach we did for dynamic consumer key where the app can register a block/lambda to provide a googleCloudProjectId during a login flow once the login server is known?

Copy link
Copy Markdown
Contributor Author

@JohnsonEricAtSalesforce JohnsonEricAtSalesforce May 5, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Based on our conversation last Friday, I created follow-up W-22355537 to cover this. That will separate the Google Cloud Project Id into a new and optional Salesforce SDK Manager initialization parameter. When set, that will allow the manager to immediately begin preparing the Google Play Integrity API Token Provider warm up. The Salesforce Challenge API Hostname will be resolved by a new callback function which the app registers as a separate Salesforce SDK Manager property. That callback will be called with the resolved My Domain host after the well-known authentication configuration is fetched. Only when both are present will the attestation parameter be generated.

Comment on lines +500 to +517
val jwtFlow = !jwt.isNullOrBlank() && !authCodeForJwtFlow.isNullOrBlank()
val currentJwt = jwt
val currentAuthCode = authCodeForJwtFlow
val jwtFlow = if (currentJwt != null && currentAuthCode != null) {
currentJwt.isNotBlank() && currentAuthCode.isNotBlank()
} else {
false
}
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@JohnsonEricAtSalesforce I don't understand. Does this not do the exact same thing in 7 lines of code that was originally 1 line?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You're exactly correct - with the exception that this generates to simpler byte code with fewer logic branches. This came as a suggestion from our tools and gives us a higher coverage score, at the least. I'm on the fence much of the time when it comes to cosmetic Kotlin changes that have logic/coverage impacts that improve our coverage. For this one, I was game to get the coverage win. If anyone's particularly adverse to it, I'm flexible.

There were two cases like this. The other, however, remained a one-liner.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@brandonpage, just checking if you do or don't want to roll this back. I thought it over a little more and still like the cosmetic code change in exchange for fewer logic branches in the generated byte code and higher coverage. It's just a preference though, so feel free to make the call.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@JohnsonEricAtSalesforce I am very much in favor of keeping the code as easy as possible for humans to read and digest. I think the 1 line removed is easier to quickly understand than the 7 that replaced it. And when have we ever cared about byte code?

As for code coverage, I am very happy we are pushing to increase it, but this feels artificial. Adding more code just to have more coverage doesn't accomplish much IMO.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That works. I'll just roll it back. It's just a neat experiment in balancing our myriad of expectations 😊

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That commit is pushed 👍🏻

@JohnsonEricAtSalesforce
Copy link
Copy Markdown
Contributor Author

Before making any updates which might intentionally reduce code coverage, here's a screenshot of the "peak" coverage possible on this pull request, some of which was possible largely due to our new tools. Also, future coverage reports may not generate properly once known unstable tests from upstream are restored to this change set prior to merge.

Screenshot 2026-05-04 at 18 45 18

…ew: Move `attestation` Parameter To Request Body In `NativeLoginManager.kt`)
@JohnsonEricAtSalesforce JohnsonEricAtSalesforce force-pushed the feature/w-21933885_msdk-android-app-attestation-implementation branch from c270bee to cc5f7db Compare May 5, 2026 01:18
…EMPORARY: Updated Ignored Test Inventory)"

This reverts commit 8387168.
Comment on lines -502 to +519
jwtFlow -> null
jwtFlow -> mutableMapOf()
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This probably doesn't matter, but I think we need to remove jwtFlow in 14.0 since getFrontdoorUrl is being removed. I believe it is the (very) old "Magic Links" flow that was simply carried over when we re-wrote the login flows in 13.

import java.net.URI

@RunWith(AndroidJUnit4::class)
class OAuth2MockTests {
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🎉

…de Coverage Updates For `jwtFlow` Logic In `LoginViewModel.kt`)
@JohnsonEricAtSalesforce JohnsonEricAtSalesforce merged commit 3b19ac6 into forcedotcom:dev May 7, 2026
16 of 20 checks passed
@JohnsonEricAtSalesforce JohnsonEricAtSalesforce deleted the feature/w-21933885_msdk-android-app-attestation-implementation branch May 7, 2026 17:29
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants