Skip to content

Commit ea53d78

Browse files
committed
Merge branch 'dev' into sedemche/sign_out_corr_id
2 parents 9905d2d + 3a7c38e commit ea53d78

23 files changed

+1539
-6
lines changed

IdentityCore/IdentityCore.xcodeproj/project.pbxproj

Lines changed: 42 additions & 0 deletions
Large diffs are not rendered by default.

IdentityCore/src/MSIDOAuth2Constants.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -157,6 +157,7 @@ extern NSString *const MSID_ID_TOKEN_CACHE_TYPE;
157157
extern NSString *const MSID_LEGACY_ID_TOKEN_CACHE_TYPE;
158158
extern NSString *const MSID_PRT_TOKEN_CACHE_TYPE;
159159
extern NSString *const MSID_FRT_TOKEN_CACHE_TYPE;
160+
extern NSString *const MSID_BOUND_RT_TOKEN_CACHE_TYPE;
160161
extern NSString *const MSID_GENERAL_TOKEN_CACHE_TYPE;
161162
extern NSString *const MSID_GENERAL_CACHE_ITEM_TYPE;
162163
extern NSString *const MSID_APP_METADATA_CACHE_TYPE;
@@ -177,3 +178,4 @@ extern NSString *const MSID_CCS_REQUEST_ID_RESPONSE;
177178

178179
extern NSString *const MSID_CCS_REQUEST_SEQUENCE_KEY;
179180
extern NSString *const MSID_CCS_REQUEST_SEQUENCE_RESPONSE;
181+
extern NSString *const MSID_BOUND_DEVICE_ID_CACHE_KEY;

IdentityCore/src/MSIDOAuth2Constants.m

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -151,6 +151,7 @@
151151
NSString *const MSID_LEGACY_ID_TOKEN_CACHE_TYPE = @"V1IdToken";
152152
NSString *const MSID_PRT_TOKEN_CACHE_TYPE = @"PrimaryRefreshToken";
153153
NSString *const MSID_FRT_TOKEN_CACHE_TYPE = @"FamilyRefreshToken";
154+
NSString *const MSID_BOUND_RT_TOKEN_CACHE_TYPE = @"BoundRefreshToken";
154155
NSString *const MSID_GENERAL_TOKEN_CACHE_TYPE = @"token";
155156
NSString *const MSID_GENERAL_CACHE_ITEM_TYPE = @"general_cache_item";
156157
NSString *const MSID_APP_METADATA_CACHE_TYPE = @"appmetadata";
@@ -177,3 +178,5 @@
177178

178179
NSString *const MSID_CCS_REQUEST_SEQUENCE_KEY = @"x-ms-srs";
179180
NSString *const MSID_CCS_REQUEST_SEQUENCE_RESPONSE = @"ccs-request-sequence";
181+
182+
NSString *const MSID_BOUND_DEVICE_ID_CACHE_KEY = @"bound_device_id";
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,185 @@
1+
//
2+
// Copyright (c) Microsoft Corporation.
3+
// All rights reserved.
4+
//
5+
// This code is licensed under the MIT License.
6+
//
7+
// Permission is hereby granted, free of charge, to any person obtaining a copy
8+
// of this software and associated documentation files(the "Software"), to deal
9+
// in the Software without restriction, including without limitation the rights
10+
// to use, copy, modify, merge, publish, distribute, sublicense, and / or sell
11+
// copies of the Software, and to permit persons to whom the Software is
12+
// furnished to do so, subject to the following conditions :
13+
//
14+
// The above copyright notice and this permission notice shall be included in
15+
// all copies or substantial portions of the Software.
16+
//
17+
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
18+
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
19+
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
20+
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
21+
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
22+
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
23+
// THE SOFTWARE.
24+
25+
#import <Foundation/Foundation.h>
26+
#import "MSIDJsonSerializable.h"
27+
#import "MSIDConstants.h"
28+
29+
NS_ASSUME_NONNULL_BEGIN
30+
31+
/**
32+
* MATS Silent Status Enum
33+
*
34+
* Represents the outcome of a silent token request attempt.
35+
* Based on WebTokenRequestStatus values used in Windows WAM.
36+
*/
37+
typedef NS_ENUM(NSInteger, MSIDMATSSilentStatus) {
38+
/**
39+
* Silent token obtained successfully
40+
*/
41+
MSIDMATSSilentStatusSuccess = 0,
42+
43+
/**
44+
* User cancelled the silent token request
45+
*/
46+
MSIDMATSSilentStatusUserCancel = 1,
47+
48+
/**
49+
* Silent attempt concluded that user interaction is required
50+
*/
51+
MSIDMATSSilentStatusUserInteractionRequired = 3,
52+
53+
/**
54+
* Silent attempt hit a provider error (e.g., refresh token expired)
55+
*/
56+
MSIDMATSSilentStatusProviderError = 5
57+
};
58+
59+
typedef NSString *MSIDMATSDeviceJoinStatus NS_TYPED_ENUM;
60+
extern MSIDMATSDeviceJoinStatus const MSIDMATSDeviceJoinStatusNotJoined;
61+
extern MSIDMATSDeviceJoinStatus const MSIDMATSDeviceJoinStatusAADJ;
62+
63+
/**
64+
* Microsoft Authentication Telemetry System (MATS) Report
65+
*
66+
* This class represents detailed telemetry information about the token acquisition process
67+
* that the native broker returns to MSAL.js. MSAL.js will record these fields in its
68+
* telemetry if the broker provides them. This telemetry helps correlate broker operations
69+
* (like cache hits, device state, or errors) with MSAL.js events.
70+
*/
71+
@interface MSIDBrokerOperationBrowserNativeMessageMATSReport : NSObject <MSIDJsonSerializable>
72+
73+
/**
74+
* Indicates if the token came from cache.
75+
*
76+
* A boolean flag where YES (1) means the broker returned a cached token without a network call,
77+
* and NO (0) means a network request was made to acquire a new token. This helps measure
78+
* how often silent SSO worked via cache vs network.
79+
*
80+
* Example: YES (token was served from cache), NO (fresh call required)
81+
*/
82+
@property (nonatomic) BOOL isCached;
83+
84+
/**
85+
* Version of the broker handling the request.
86+
*
87+
* Example: "3.9.0"
88+
*/
89+
@property (nonatomic, nullable) NSString *brokerVersion;
90+
91+
/**
92+
* Device's AAD join status.
93+
*
94+
* Indicates the device's registration state in Entra ID (Azure AD). Possible values:
95+
* - MSIDMATSDeviceJoinStatusAADJ (@"aadj") - Device is Azure AD joined (managed by org)
96+
* - MSIDMATSDeviceJoinStatusNotJoined (@"not_joined") - Device is not joined to AAD
97+
* This field helps identify if device is corporate-managed or personal.
98+
*
99+
* Example: MSIDMATSDeviceJoinStatusAADJ (managed device), MSIDMATSDeviceJoinStatusNotJoined (personal device)
100+
*/
101+
@property (nonatomic, nullable) MSIDMATSDeviceJoinStatus deviceJoin;
102+
103+
/**
104+
* Type of prompt that occurred.
105+
*/
106+
@property (nonatomic) MSIDPromptType promptBehavior;
107+
108+
/**
109+
* Broker/IDP error code.
110+
*
111+
* A numeric code representing the error if the token request failed. 0 if the operation
112+
* succeeded or no specific error.
113+
*
114+
* Example: 0 (no error, success)
115+
*/
116+
@property (nonatomic) NSInteger apiErrorCode;
117+
118+
/**
119+
* Was UI shown?
120+
*
121+
* Boolean flag: YES if the broker showed any UI to the user. NO if the entire flow was silent/invisible.
122+
* This directly indicates if the user was interrupted with a prompt.
123+
*
124+
* Example: YES (user saw sign-in window), NO (completely silent SSO).
125+
*/
126+
@property (nonatomic) BOOL uiVisible;
127+
128+
/**
129+
* Silent attempt error code.
130+
*
131+
* If the broker attempted to get a token silently (using cached credentials or refresh
132+
* token) and that attempt failed, this is the error code from the silent try. 0 if
133+
* silent succeeded or no error was encountered silently.
134+
*
135+
* Example: 0 (silent succeeded or not attempted)
136+
*/
137+
@property (nonatomic) NSInteger silentCode;
138+
139+
/**
140+
* Silent attempt error message.
141+
*
142+
* A short text description of why the silent attempt failed, if an error occurred.
143+
* Including this helps debugging exact silent failure reasons.
144+
*
145+
* Example: @"" (silent succeeded), @"The web page and the redirect uri must be on the same origin."
146+
*/
147+
@property (nonatomic, nullable) NSString *silentMessage;
148+
149+
/**
150+
* Outcome of silent request (status code).
151+
*
152+
* Corresponds to the broker's internal status enum for a silent token attempt.
153+
* Values based on WebTokenRequestStatus:
154+
* - MSIDMATSSilentStatusSuccess (0) - Silent token obtained successfully
155+
* - MSIDMATSSilentStatusUserCancel (1) - User cancelled the silent token request
156+
* - MSIDMATSSilentStatusUserInteractionRequired (3) - Silent attempt concluded that user interaction is required
157+
* - MSIDMATSSilentStatusProviderError (5) - Silent attempt hit a provider error
158+
*
159+
* Example: MSIDMATSSilentStatusSuccess (silent success), MSIDMATSSilentStatusUserInteractionRequired (interaction required)
160+
*/
161+
@property (nonatomic) MSIDMATSSilentStatus silentStatus;
162+
163+
/**
164+
* HTTP response code from token endpoint.
165+
*
166+
* If the broker made a network request to AAD (for token, device code, etc.), this
167+
* captures the HTTP status code. 200 for success, 4xx/5xx for various errors.
168+
* Will be 0 if no network call occurred (e.g., fully cached token).
169+
*
170+
* Example: 200 (token obtained successfully), 400 (bad request), 500 (server error)
171+
*/
172+
@property (nonatomic) NSInteger httpStatus;
173+
174+
/**
175+
* JSON string representation of the report.
176+
*
177+
* Converts the MATS report into a JSON string format for easy logging or transmission.
178+
*
179+
* @return A JSON string representing the MATS report, or nil if serialization fails.
180+
*/
181+
- (NSString *)jsonString;
182+
183+
@end
184+
185+
NS_ASSUME_NONNULL_END
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,138 @@
1+
//
2+
// Copyright (c) Microsoft Corporation.
3+
// All rights reserved.
4+
//
5+
// This code is licensed under the MIT License.
6+
//
7+
// Permission is hereby granted, free of charge, to any person obtaining a copy
8+
// of this software and associated documentation files(the "Software"), to deal
9+
// in the Software without restriction, including without limitation the rights
10+
// to use, copy, modify, merge, publish, distribute, sublicense, and / or sell
11+
// copies of the Software, and to permit persons to whom the Software is
12+
// furnished to do so, subject to the following conditions :
13+
//
14+
// The above copyright notice and this permission notice shall be included in
15+
// all copies or substantial portions of the Software.
16+
//
17+
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
18+
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
19+
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
20+
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
21+
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
22+
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
23+
// THE SOFTWARE.
24+
25+
#import "MSIDBrokerOperationBrowserNativeMessageMATSReport.h"
26+
#import "NSDictionary+MSIDExtensions.h"
27+
#import "MSIDPromptType_Internal.h"
28+
29+
NSString *const MSID_MATS_IS_CACHED_KEY = @"is_cached";
30+
NSString *const MSID_MATS_BROKER_VERSION_KEY = @"broker_version";
31+
NSString *const MSID_MATS_DEVICE_JOIN_KEY = @"device_join";
32+
NSString *const MSID_MATS_PROMPT_BEHAVIOR_KEY = @"prompt_behavior";
33+
NSString *const MSID_MATS_API_ERROR_CODE_KEY = @"api_error_code";
34+
NSString *const MSID_MATS_UI_VISIBLE_KEY = @"ui_visible";
35+
NSString *const MSID_MATS_SILENT_CODE_KEY = @"silent_code";
36+
NSString *const MSID_MATS_SILENT_MESSAGE_KEY = @"silent_message";
37+
NSString *const MSID_MATS_SILENT_STATUS_KEY = @"silent_status";
38+
NSString *const MSID_MATS_HTTP_STATUS_KEY = @"http_status";
39+
NSString *const MSID_MATS_HTTP_EVENT_COUNT_KEY = @"http_event_count";
40+
41+
// Device Join Status Constants
42+
MSIDMATSDeviceJoinStatus const MSIDMATSDeviceJoinStatusAADJ = @"aadj";
43+
MSIDMATSDeviceJoinStatus const MSIDMATSDeviceJoinStatusNotJoined = @"not_joined";
44+
45+
@implementation MSIDBrokerOperationBrowserNativeMessageMATSReport
46+
47+
- (instancetype)init
48+
{
49+
self = [super init];
50+
if (self)
51+
{
52+
// Initialize with default values.
53+
_isCached = NO;
54+
_apiErrorCode = 0;
55+
_uiVisible = NO;
56+
_silentCode = 0;
57+
_silentStatus = MSIDMATSSilentStatusSuccess;
58+
_httpStatus = 0;
59+
}
60+
return self;
61+
}
62+
63+
- (NSString *)description
64+
{
65+
return [NSString stringWithFormat:@"MSIDBrokerOperationBrowserNativeMessageMATSReport: isCached: %@, brokerVersion: %@, deviceJoin: %@, promptBehavior: %@, apiErrorCode: %ld, uiVisible: %@, silentCode: %ld, silentMessage: %@, silentStatus: %ld, httpStatus: %ld",
66+
@(self.isCached),
67+
self.brokerVersion,
68+
self.deviceJoin,
69+
MSIDPromptParamFromType(self.promptBehavior),
70+
(long)self.apiErrorCode,
71+
@(self.uiVisible),
72+
(long)self.silentCode,
73+
self.silentMessage,
74+
(long)self.silentStatus,
75+
(long)self.httpStatus];
76+
}
77+
78+
#pragma mark - MSIDJsonSerializable
79+
80+
- (instancetype)initWithJSONDictionary:(NSDictionary *)json error:(NSError *__autoreleasing *)error
81+
{
82+
self = [super init];
83+
if (!self) return nil;
84+
85+
_isCached = [json msidBoolObjectForKey:MSID_MATS_IS_CACHED_KEY];
86+
_brokerVersion = [json msidStringObjectForKey:MSID_MATS_BROKER_VERSION_KEY];
87+
_deviceJoin = [json msidStringObjectForKey:MSID_MATS_DEVICE_JOIN_KEY];
88+
NSString *promptString = [json msidStringObjectForKey:MSID_MATS_PROMPT_BEHAVIOR_KEY];
89+
_promptBehavior = MSIDPromptTypeFromString(promptString);
90+
_apiErrorCode = [json msidIntegerObjectForKey:MSID_MATS_API_ERROR_CODE_KEY];
91+
_uiVisible = [json msidBoolObjectForKey:MSID_MATS_UI_VISIBLE_KEY];
92+
_silentCode = [json msidIntegerObjectForKey:MSID_MATS_SILENT_CODE_KEY];
93+
_silentMessage = [json msidStringObjectForKey:MSID_MATS_SILENT_MESSAGE_KEY];
94+
_silentStatus = [json msidIntegerObjectForKey:MSID_MATS_SILENT_STATUS_KEY];
95+
_httpStatus = [json msidIntegerObjectForKey:MSID_MATS_HTTP_STATUS_KEY];
96+
97+
return self;
98+
}
99+
100+
- (NSDictionary *)jsonDictionary
101+
{
102+
NSMutableDictionary *json = [NSMutableDictionary new];
103+
104+
json[MSID_MATS_IS_CACHED_KEY] = @(self.isCached);
105+
if (self.brokerVersion) json[MSID_MATS_BROKER_VERSION_KEY] = self.brokerVersion;
106+
if (self.deviceJoin) json[MSID_MATS_DEVICE_JOIN_KEY] = self.deviceJoin;
107+
108+
NSString *promptString = MSIDPromptParamFromType(self.promptBehavior);
109+
if (![NSString msidIsStringNilOrBlank:promptString]) json[MSID_MATS_PROMPT_BEHAVIOR_KEY] = promptString;
110+
111+
json[MSID_MATS_API_ERROR_CODE_KEY] = @(self.apiErrorCode);
112+
json[MSID_MATS_UI_VISIBLE_KEY] = @(self.uiVisible);
113+
json[MSID_MATS_SILENT_CODE_KEY] = @(self.silentCode);
114+
json[MSID_MATS_SILENT_MESSAGE_KEY] = self.silentMessage;
115+
json[MSID_MATS_SILENT_STATUS_KEY] = @(self.silentStatus);
116+
json[MSID_MATS_HTTP_STATUS_KEY] = @(self.httpStatus);
117+
118+
return json;
119+
}
120+
121+
- (NSString *)jsonString
122+
{
123+
NSDictionary *matsDict = [self jsonDictionary];
124+
if (matsDict)
125+
{
126+
NSString *matsString = [matsDict msidJSONSerializeWithContext:nil];
127+
if (!matsString)
128+
{
129+
MSID_LOG_WITH_CTX(MSIDLogLevelWarning, nil, @"Failed to serialize MATS report to JSON string.");
130+
}
131+
132+
return matsString;
133+
}
134+
135+
return nil;
136+
}
137+
138+
@end

IdentityCore/src/broker_operation/response/browser_native_message_response/MSIDBrowserNativeMessageGetTokenResponse.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@
2626
#import "MSIDBrokerNativeAppOperationResponse.h"
2727

2828
@class MSIDBrokerOperationTokenResponse;
29+
@class MSIDBrokerOperationBrowserNativeMessageMATSReport;
2930

3031
NS_ASSUME_NONNULL_BEGIN
3132

@@ -35,6 +36,8 @@ NS_ASSUME_NONNULL_BEGIN
3536
- (instancetype _Nullable)initWithTokenResponse:(nonnull MSIDBrokerOperationTokenResponse *)tokenResponse;
3637

3738
@property (nonatomic, nullable) NSString *state;
39+
@property (nonatomic, nullable) MSIDBrokerOperationBrowserNativeMessageMATSReport *matsReport;
40+
3841

3942
@end
4043

IdentityCore/src/broker_operation/response/browser_native_message_response/MSIDBrowserNativeMessageGetTokenResponse.m

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,13 +20,14 @@
2020
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
2121
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
2222
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
23-
// THE SOFTWARE.
23+
// THE SOFTWARE.
2424

2525

2626
#import "MSIDBrowserNativeMessageGetTokenResponse.h"
2727
#import "MSIDBrokerOperationTokenResponse.h"
2828
#import "MSIDTokenResponse.h"
2929
#import "MSIDOAuth2Constants.h"
30+
#import "MSIDBrokerOperationBrowserNativeMessageMATSReport.h"
3031

3132
@interface MSIDBrowserNativeMessageGetTokenResponse()
3233

@@ -80,6 +81,9 @@ - (NSDictionary *)jsonDictionary
8081
__auto_type propertiesJson = [NSMutableDictionary new];
8182
// TODO: once ests follow the latest protocol, this should be removed. Account ID should be read from accountJson.
8283
propertiesJson[@"UPN"] = accountJson[@"userName"];
84+
// Add MATS report as JSON string
85+
propertiesJson[@"MATS"] = [self.matsReport jsonString];
86+
8387
response[@"properties"] = propertiesJson;
8488

8589
return response;

0 commit comments

Comments
 (0)