Skip to content

Commit c5c0c6a

Browse files
authored
Hotfix 24.2.1: cherry-pick onboarding telemetry (#3088, #3111, #3117), Fixes AB#3462876 (#3119)
Cherry-picks the onboarding telemetry feature into the 24.2.1 hotfix release branch. Cherry-picked from `dev` (in order): | PR | Commit | Description | |---|---|---| | #3088 | `543578aa4` | Add onboarding telemetry recorder, field keys, and session correlation store | | #3111 | `0965c25ca` | Add onboarding telemetry blob fields to BrokerRequest/BrokerResult and command parameters for client↔broker IPC transport | | #3117 | `1fe87aaa5` | Add additional step ID and blocking error constants for full onboarding telemetry coverage | All three are part of the same feature (mobile onboarding telemetry, [AB#3462876](https://identitydivision.visualstudio.com/fac9d424-53d2-45c0-91b5-ef6ba7a6bf26/_workitems/edit/3462876) / [AB#3568357](https://identitydivision.visualstudio.com/fac9d424-53d2-45c0-91b5-ef6ba7a6bf26/_workitems/edit/3568357)) and are bundled here so OneAuth can take a single hotfix dependency. **Conflicts resolved:** `changelog.txt` only — entries moved from `vNext` to `Version 24.2.1-RC1`. No code conflicts. Fixes [AB#3462876](https://identitydivision.visualstudio.com/fac9d424-53d2-45c0-91b5-ef6ba7a6bf26/_workitems/edit/3462876)
1 parent a09d5c6 commit c5c0c6a

16 files changed

Lines changed: 1094 additions & 8 deletions

File tree

changelog.txt

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,12 @@
11
vNext
22
----------
33

4-
Version 24.2.1-RC1
5-
----------
6-
4+
Version 24.2.1-RC1
5+
----------
6+
- [MINOR] Add additional step ID and blocking error constants for full onboarding telemetry coverage (#3117)
7+
- [MINOR] Add onboarding telemetry blob fields to BrokerRequest/BrokerResult and command parameters for client↔broker IPC transport (#3111)
8+
- [MINOR] Add onboarding telemetry recorder, field keys, and session persistence for mobile onboarding flow (#3088)
9+
710
Version 24.2.0
811
----------
912
- [PATCH] Add support for Authenticator app activation links in WebView, enabling account pairing/MFA flows to launch Microsoft Authenticator directly instead of redirecting to the Play Store (#3090)

common/src/main/java/com/microsoft/identity/common/internal/broker/BrokerRequest.java

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -87,6 +87,7 @@ private static final class SerializedNames {
8787
final static String TENANT_ID = "tenant_id";
8888
final static String REQUEST_TYPE = "request_type";
8989
final static String WEB_APPS_STATE = "web_apps_state";
90+
final static String ONBOARDING_SEED_JSON = "onboarding_seed_json";
9091
}
9192

9293
/**
@@ -295,4 +296,17 @@ private static final class SerializedNames {
295296
@Nullable
296297
@SerializedName(SerializedNames.REQUEST_TYPE)
297298
private String mRequestType;
299+
300+
/**
301+
* Onboarding telemetry seed JSON blob.
302+
* Direction: client → broker (input only). Contains sessionCorrelationId,
303+
* onboarding_mode, and schema_version, supplied by the client (OneAuth/MSAL)
304+
* so the broker can construct an OnboardingTelemetryRecorder using the same
305+
* correlation id. The broker returns the populated blob (with steps and
306+
* blocking errors) via {@link BrokerResult#getOnboardingBlob()}, not via
307+
* this field.
308+
*/
309+
@Nullable
310+
@SerializedName(SerializedNames.ONBOARDING_SEED_JSON)
311+
private String mOnboardingSeedJson;
298312
}

common/src/main/java/com/microsoft/identity/common/internal/broker/BrokerResult.java

Lines changed: 20 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -97,6 +97,7 @@ private static class SerializedNames {
9797
static final String SPE_RING = "spe_ring";
9898
static final String CLI_TELEM_ERRORCODE = "cli_telem_error_code";
9999
static final String CLI_TELEM_SUB_ERROR_CODE = "cli_telem_suberror_code";
100+
static final String ONBOARDING_BLOB = "onboarding_blob";
100101
}
101102

102103
private static final long serialVersionUID = 8606631820514878489L;
@@ -327,6 +328,13 @@ private static class SerializedNames {
327328
@SerializedName(SerializedNames.BROKER_AAD_DEVICE_ID_RECORD)
328329
private final AadDeviceIdRecord mAadDeviceIdRecord;
329330

331+
/**
332+
* Populated onboarding telemetry blob JSON returned by the broker.
333+
*/
334+
@Nullable
335+
@SerializedName(SerializedNames.ONBOARDING_BLOB)
336+
private final String mOnboardingBlob;
337+
330338
private BrokerResult(@NonNull final Builder builder) {
331339
mAccessToken = builder.mAccessToken;
332340
mIdToken = builder.mIdToken;
@@ -362,6 +370,7 @@ private BrokerResult(@NonNull final Builder builder) {
362370
mCliTelemSubErrorCode = builder.mCliTelemSubErrorCode;
363371
mExceptionType = builder.mExceptionType;
364372
mAadDeviceIdRecord = builder.mAadDeviceIdRecord;
373+
mOnboardingBlob = builder.mOnboardingBlob;
365374
}
366375

367376
public String getExceptionType() {
@@ -498,6 +507,11 @@ public AadDeviceIdRecord getAadDeviceIdRecord() {
498507
return mAadDeviceIdRecord;
499508
}
500509

510+
@Nullable
511+
public String getOnboardingBlob() {
512+
return mOnboardingBlob;
513+
}
514+
501515
public static class Builder {
502516
private String mAccessToken;
503517
private String mIdToken;
@@ -523,6 +537,7 @@ public static class Builder {
523537
private List<ICacheRecord> mTenantProfileData;
524538
private boolean mServicedFromCache;
525539
private AadDeviceIdRecord mAadDeviceIdRecord;
540+
private String mOnboardingBlob;
526541

527542
// Exception parameters
528543
private String mErrorCode;
@@ -535,7 +550,6 @@ public static class Builder {
535550
private String mCliTelemErrorCode;
536551
private String mCliTelemSubErrorCode;
537552
private String mExceptionType;
538-
539553
public Builder accessToken(@Nullable final String accessToken) {
540554
this.mAccessToken = accessToken;
541555
return this;
@@ -712,6 +726,11 @@ public Builder exceptionType(String exceptionType) {
712726
this.mExceptionType = exceptionType;
713727
return this;
714728
}
729+
730+
public Builder onboardingBlob(@Nullable final String onboardingBlob) {
731+
this.mOnboardingBlob = onboardingBlob;
732+
return this;
733+
}
715734
}
716735

717736
}

common/src/main/java/com/microsoft/identity/common/internal/request/MsalBrokerRequestAdapter.java

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -139,7 +139,8 @@ public BrokerRequest brokerRequestFromAcquireTokenParameters(@NonNull final Inte
139139
.preferredBrowser(parameters.getPreferredBrowser())
140140
.preferredAuthMethod(parameters.getPreferredAuthMethod())
141141
.accountTransferToken(parameters.getAccountTransferToken())
142-
.suppressAccountPicker(parameters.isSuppressBrokerAccountPicker());
142+
.suppressAccountPicker(parameters.isSuppressBrokerAccountPicker())
143+
.onboardingSeedJson(parameters.getOnboardingSeedJson());
143144

144145
if (parameters instanceof AndroidInteractiveTokenCommandParameters) {
145146
final AndroidInteractiveTokenCommandParameters androidInteractiveTokenCommandParameters = (AndroidInteractiveTokenCommandParameters) parameters;

common/src/main/java/com/microsoft/identity/common/internal/result/MsalBrokerResultAdapter.java

Lines changed: 55 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -465,9 +465,16 @@ public Bundle bundleFromBaseExceptionForWebApps(@NonNull final BaseException exc
465465
@NonNull
466466
@Override
467467
public ILocalAuthenticationResult authenticationResultFromBundle(@NonNull final Bundle resultBundle) throws ClientException {
468-
final String methodTag = TAG + ":authenticationResultFromBundle";
469-
final BrokerResult brokerResult = brokerResultFromBundle(resultBundle);
468+
return authenticationResultFromBrokerResult(brokerResultFromBundle(resultBundle));
469+
}
470470

471+
/**
472+
* Overload that builds the authentication result from an already-deserialized
473+
* {@link BrokerResult}. Use this when the caller has the [BrokerResult] in hand
474+
* to avoid a redundant deserialization of the result bundle.
475+
*/
476+
public ILocalAuthenticationResult authenticationResultFromBrokerResult(@NonNull final BrokerResult brokerResult) throws ClientException {
477+
final String methodTag = TAG + ":authenticationResultFromBrokerResult";
471478
Logger.info(methodTag, "Broker Result returned from Bundle, constructing authentication result");
472479

473480
final List<ICacheRecord> tenantProfileCacheRecords = brokerResult.getTenantProfileData();
@@ -565,6 +572,39 @@ public BrokerPerformanceMetrics getBrokerPerformanceMetricsFromBundle(@NonNull f
565572
}
566573
}
567574

575+
/**
576+
* Extracts the onboarding telemetry blob (JSON string) from the result bundle.
577+
* Best-effort: returns null if the bundle cannot be deserialized into a BrokerResult
578+
* or if no blob is present. Telemetry failures must never fail an otherwise-successful
579+
* auth result. Blob contents are not logged (may carry sessionCorrelationId).
580+
*
581+
* If the caller has already deserialized the [BrokerResult], prefer the overload
582+
* [getOnboardingBlobFromBundle(BrokerResult)] to avoid a second deserialization.
583+
*/
584+
@Nullable
585+
@VisibleForTesting(otherwise = VisibleForTesting.PRIVATE)
586+
public String getOnboardingBlobFromBundle(@NonNull final Bundle resultBundle) {
587+
final String methodTag = TAG + ":getOnboardingBlobFromBundle";
588+
try {
589+
final BrokerResult brokerResult = brokerResultFromBundle(resultBundle);
590+
return brokerResult.getOnboardingBlob();
591+
} catch (final ClientException e) {
592+
Logger.warn(methodTag, "Failed to extract onboarding blob from broker result: " + e.getErrorCode());
593+
return null;
594+
}
595+
}
596+
597+
/**
598+
* Overload that reads the onboarding telemetry blob from a {@link BrokerResult}
599+
* that has already been deserialized by the caller. Use this when you already
600+
* have a [BrokerResult] in hand to avoid a second deserialization of the bundle.
601+
*/
602+
@Nullable
603+
@VisibleForTesting(otherwise = VisibleForTesting.PRIVATE)
604+
public String getOnboardingBlobFromBundle(@NonNull final BrokerResult brokerResult) {
605+
return brokerResult.getOnboardingBlob();
606+
}
607+
568608
@NonNull
569609
@Override
570610
public AcquirePrtSsoTokenResult getAcquirePrtSsoTokenResultFromBundle(Bundle resultBundle) {
@@ -1017,9 +1057,14 @@ public AcquireTokenResult getDeviceCodeFlowTokenResultFromResultBundle(@NonNull
10171057
AcquireTokenResult getAcquireTokenResultFromResultBundle(@NonNull final Bundle resultBundle) throws BaseException {
10181058
final MsalBrokerResultAdapter resultAdapter = new MsalBrokerResultAdapter();
10191059
if (resultBundle.getBoolean(AuthenticationConstants.Broker.BROKER_REQUEST_V2_SUCCESS)) {
1060+
// Deserialize BrokerResult once and reuse for both the local authentication result
1061+
// and the onboarding telemetry blob, instead of letting authenticationResultFromBundle
1062+
// and getOnboardingBlobFromBundle each pay the deserialization cost.
1063+
final BrokerResult brokerResult = resultAdapter.brokerResultFromBundle(resultBundle);
1064+
10201065
final AcquireTokenResult acquireTokenResult = new AcquireTokenResult();
10211066
acquireTokenResult.setLocalAuthenticationResult(
1022-
resultAdapter.authenticationResultFromBundle(resultBundle)
1067+
resultAdapter.authenticationResultFromBrokerResult(brokerResult)
10231068
);
10241069
// Set broker performance metrics if available
10251070
final BrokerPerformanceMetrics metrics = resultAdapter.getBrokerPerformanceMetricsFromBundle(resultBundle);
@@ -1038,6 +1083,13 @@ AcquireTokenResult getAcquireTokenResultFromResultBundle(@NonNull final Bundle r
10381083
resultBundle.getString(AuthenticationConstants.Broker.BROKER_PACKAGE_NAME)
10391084
);
10401085
}
1086+
1087+
// Set onboarding telemetry blob if present (best-effort; never fails the result).
1088+
final String onboardingBlob = resultAdapter.getOnboardingBlobFromBundle(brokerResult);
1089+
if (onboardingBlob != null) {
1090+
acquireTokenResult.setOnboardingBlob(onboardingBlob);
1091+
}
1092+
10411093
return acquireTokenResult;
10421094
}
10431095

Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,59 @@
1+
// Copyright (c) Microsoft Corporation.
2+
// All rights reserved.
3+
//
4+
// This code is licensed under the MIT License.
5+
//
6+
// Permission is hereby granted, free of charge, to any person obtaining a copy
7+
// of this software and associated documentation files(the "Software"), to deal
8+
// in the Software without restriction, including without limitation the rights
9+
// to use, copy, modify, merge, publish, distribute, sublicense, and / or sell
10+
// copies of the Software, and to permit persons to whom the Software is
11+
// furnished to do so, subject to the following conditions :
12+
//
13+
// The above copyright notice and this permission notice shall be included in
14+
// all copies or substantial portions of the Software.
15+
//
16+
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17+
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18+
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19+
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20+
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
21+
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
22+
// THE SOFTWARE.
23+
package com.microsoft.identity.common.internal.telemetry
24+
25+
import android.content.Context
26+
27+
/**
28+
* SharedPreferences-backed persistence for session correlation IDs.
29+
* Used by OneAuth (via JNI/Djinni SessionCachePersistence adapter).
30+
* Each app (OneAuth host, broker) has its own sandboxed SharedPreferences file;
31+
* the same schema and file name are used across apps for consistency.
32+
* OnboardingTelemetryRecorder also writes to this file on block detection.
33+
*/
34+
class OnboardingSessionCorrelationStore(context: Context) {
35+
36+
private val appContext: Context = context.applicationContext
37+
38+
/**
39+
* Load the persisted session correlation cache JSON string.
40+
* @return JSON string, or empty string if nothing is persisted
41+
*/
42+
fun load(): String {
43+
val prefs = appContext.getSharedPreferences(PREFS_FILE, Context.MODE_PRIVATE)
44+
return prefs.getString(PREFS_FILE, "") ?: ""
45+
}
46+
47+
/**
48+
* Save the session correlation cache JSON string to SharedPreferences.
49+
* @param json The JSON string to persist
50+
*/
51+
fun save(json: String) {
52+
val prefs = appContext.getSharedPreferences(PREFS_FILE, Context.MODE_PRIVATE)
53+
prefs.edit().putString(PREFS_FILE, json).apply()
54+
}
55+
56+
companion object {
57+
private const val PREFS_FILE = "com.microsoft.oneauth.session_correlation_cache"
58+
}
59+
}

0 commit comments

Comments
 (0)