react-native-intune 0.1.1 → 1.0.0-rc.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (36) hide show
  1. package/README.md +8 -4
  2. package/RNIntune.podspec +3 -3
  3. package/android/build.gradle +46 -25
  4. package/android/src/main/java/com/reactnativeintune/RNIntunePackage.kt +11 -6
  5. package/android/src/main/java/com/reactnativeintune/RNIntuneResetJournal.kt +1 -1
  6. package/ios/RNIntunePlistGuard.m +1 -1
  7. package/ios/RNIntuneResetJournal.m +1 -1
  8. package/lib/module/api/tokenProvider.js +3 -0
  9. package/lib/module/api/tokenProvider.js.map +1 -1
  10. package/lib/module/index.js +1 -0
  11. package/lib/module/index.js.map +1 -1
  12. package/lib/module/internal/decode.js +23 -2
  13. package/lib/module/internal/decode.js.map +1 -1
  14. package/lib/module/types.js +37 -6
  15. package/lib/module/types.js.map +1 -1
  16. package/lib/typescript/src/api/tokenProvider.d.ts +3 -0
  17. package/lib/typescript/src/api/tokenProvider.d.ts.map +1 -1
  18. package/lib/typescript/src/index.d.ts +1 -0
  19. package/lib/typescript/src/index.d.ts.map +1 -1
  20. package/lib/typescript/src/internal/decode.d.ts.map +1 -1
  21. package/lib/typescript/src/types.d.ts +61 -11
  22. package/lib/typescript/src/types.d.ts.map +1 -1
  23. package/package.json +7 -8
  24. package/plugin/transforms.js +155 -2
  25. package/plugin/withIntune.js +27 -0
  26. package/scripts/doctor.mjs +7 -3
  27. package/scripts/lib/checks.mjs +483 -27
  28. package/scripts/lib/edits.mjs +329 -0
  29. package/scripts/lib/project.mjs +13 -0
  30. package/scripts/setup.mjs +59 -9
  31. package/sdk-lock.json +9 -8
  32. package/sdk-versions.json +125 -28
  33. package/src/api/tokenProvider.ts +3 -0
  34. package/src/index.ts +1 -0
  35. package/src/internal/decode.ts +27 -8
  36. package/src/types.ts +66 -15
package/sdk-versions.json CHANGED
@@ -8,20 +8,56 @@
8
8
  "iOS 21.x requires iOS 17+ and Xcode 26. If a consumer must support iOS 16, they need",
9
9
  "SDK 20.x, which Microsoft supports for high-priority security fixes only. See SPEC.md."
10
10
  ],
11
-
12
11
  "ios": {
13
12
  "repo": "microsoftconnect/ms-intune-app-sdk-ios",
14
- "tag": "21.8.0",
15
- "requires": { "ios": "17.0", "xcode": "26" },
13
+ "tag": "21.9.0",
14
+ "requires": {
15
+ "ios": "17.0",
16
+ "xcode": "26"
17
+ },
16
18
  "expect": [
17
- { "glob": "IntuneMAMSwift.xcframework", "as": "ios/IntuneMAMSwift.xcframework", "required": true },
18
- { "glob": "IntuneMAMSwiftStub.xcframework", "as": "ios/IntuneMAMSwiftStub.xcframework", "required": true },
19
- { "glob": "IntuneMAMTelemetry.xcframework", "as": "ios/IntuneMAMTelemetry.xcframework", "required": true },
20
- { "glob": "IntuneMAMResources.bundle", "as": "ios/IntuneMAMResources.bundle", "required": true },
21
- { "glob": "IntuneMAMConfigurator.dmg", "as": "ios/IntuneMAMConfigurator.dmg", "required": true, "extractDmg": "IntuneMAMConfigurator", "extractAs": "ios/IntuneMAMConfigurator" },
22
- { "glob": "REDIST.txt", "as": "ios/REDIST.txt", "required": false },
23
- { "glob": "Microsoft License Terms*.pdf", "as": "ios/", "required": false },
24
- { "glob": "ThirdPartyNotices.txt", "as": "ios/ThirdPartyNotices.txt", "required": false }
19
+ {
20
+ "glob": "IntuneMAMSwift.xcframework",
21
+ "as": "ios/IntuneMAMSwift.xcframework",
22
+ "required": true
23
+ },
24
+ {
25
+ "glob": "IntuneMAMSwiftStub.xcframework",
26
+ "as": "ios/IntuneMAMSwiftStub.xcframework",
27
+ "required": true
28
+ },
29
+ {
30
+ "glob": "IntuneMAMTelemetry.xcframework",
31
+ "as": "ios/IntuneMAMTelemetry.xcframework",
32
+ "required": true
33
+ },
34
+ {
35
+ "glob": "IntuneMAMResources.bundle",
36
+ "as": "ios/IntuneMAMResources.bundle",
37
+ "required": true
38
+ },
39
+ {
40
+ "glob": "IntuneMAMConfigurator.dmg",
41
+ "as": "ios/IntuneMAMConfigurator.dmg",
42
+ "required": true,
43
+ "extractDmg": "IntuneMAMConfigurator",
44
+ "extractAs": "ios/IntuneMAMConfigurator"
45
+ },
46
+ {
47
+ "glob": "REDIST.txt",
48
+ "as": "ios/REDIST.txt",
49
+ "required": false
50
+ },
51
+ {
52
+ "glob": "Microsoft License Terms*.pdf",
53
+ "as": "ios/",
54
+ "required": false
55
+ },
56
+ {
57
+ "glob": "ThirdPartyNotices.txt",
58
+ "as": "ios/ThirdPartyNotices.txt",
59
+ "required": false
60
+ }
25
61
  ],
26
62
  "_notes": [
27
63
  "IntuneMAMStatic.xcframework is the static variant — not used; the dynamic one keeps",
@@ -31,40 +67,101 @@
31
67
  "compliance) and 28 localizations. Without it the SDK's UI cannot render."
32
68
  ]
33
69
  },
34
-
35
70
  "android": {
36
71
  "repo": "microsoftconnect/ms-intune-app-sdk-android",
37
72
  "tag": "12.4.0",
38
73
  "expect": [
39
- { "glob": "**/Microsoft.Intune.MAM.SDK.aar", "as": "android/Microsoft.Intune.MAM.SDK.aar", "required": true },
40
- { "glob": "**/com.microsoft.intune.mam.build.jar", "as": "android/GradlePlugin/com.microsoft.intune.mam.build.jar", "required": true },
41
- { "glob": "**/Microsoft.Intune.MAM.SDK.Support*.jar","as": "android/support/", "required": false },
42
- { "glob": "**/*License*.pdf", "as": "android/", "required": false }
74
+ {
75
+ "glob": "**/Microsoft.Intune.MAM.SDK.aar",
76
+ "as": "android/Microsoft.Intune.MAM.SDK.aar",
77
+ "required": true
78
+ },
79
+ {
80
+ "glob": "**/com.microsoft.intune.mam.build.jar",
81
+ "as": "android/GradlePlugin/com.microsoft.intune.mam.build.jar",
82
+ "required": true
83
+ },
84
+ {
85
+ "glob": "**/Microsoft.Intune.MAM.SDK.Support*.jar",
86
+ "as": "android/support/",
87
+ "required": false
88
+ },
89
+ {
90
+ "glob": "**/*License*.pdf",
91
+ "as": "android/",
92
+ "required": false
93
+ }
43
94
  ]
44
95
  },
45
-
46
96
  "toolchain": {
47
97
  "_comment": [
48
98
  "A version bump forces a conscious check against the MAM compatibility matrix and the",
49
99
  "host app's toolchain (SPEC.md O-E).",
50
100
  "",
51
101
  "msal_android has no requirement from Microsoft to satisfy. The Android SDK guide says",
52
- "only \"integrate MSAL\" and links the MSAL repository, unlike iOS where the coupling is",
53
- "hard. So this is our choice, pinned for reproducibility rather than compatibility:",
54
- "8.4.2, the current stable, released 2026-08-21.",
55
- "",
56
- "msal_ios is exact, not a range: the Intune SDK does not carry its own MSAL, it drives",
102
+ "only \"integrate MSAL\" and links the MSAL repository, unlike iOS where the coupling is",
103
+ "hard. So this is our choice, pinned for reproducibility rather than compatibility:",
104
+ "8.5.0, released 2026-09-16 with a fix for CVE-2026-54399 in Apache HttpCore.",
105
+ "",
106
+ "msal_ios is exact, not a range: the Intune SDK does not carry its own MSAL, it drives",
57
107
  "the app's. Two consumers on different MSALs is the same hazard pinning the Intune SDK",
58
108
  "exists to prevent. Resolved by CocoaPods on 2026-09-04, not guessed.",
59
109
  "",
60
110
  "`requires` is the compatibility-matrix row for the pinned Android tag above (12.4.0 ->",
61
- "the 12.0.0 row). Microsoft tests these combinations; mixing rows is untested. These are",
62
- "the library's own defaults in android/build.gradle. The consumer's app module, where the",
63
- "MAM Gradle plugin actually runs, must land on the same row -- that is O-E, still open."
111
+ "the 12.0.0 row). Microsoft tests these combinations; mixing rows is untested by Microsoft.",
112
+ "",
113
+ "`verified` is what this project has built and run itself, off that row: a fresh app,",
114
+ "debug and release, with the MAM plugin's report confirming the Application rewrite. It",
115
+ "is not Microsoft's claim and doctor says so. Only versions read from an actual build",
116
+ "log are listed -- an entry missing agp means the build file did not state it, not that",
117
+ "any AGP works. Add a row by running verify:bare or verify:expo, never by reasoning."
64
118
  ],
65
119
  "javassist": "3.29.2-GA",
66
- "msal_ios": "2.15.0",
67
- "msal_android": "8.4.2",
68
- "requires": { "android": "16", "gradle": "8.11.1", "agp": "8.9.1", "kotlin": "2.1.21", "java": "17" }
120
+ "msal_ios": "2.16.0",
121
+ "msal_android": "8.5.0",
122
+ "requires": {
123
+ "android": "16",
124
+ "gradle": "8.11.1",
125
+ "agp": "8.9.1",
126
+ "kotlin": "2.1.21",
127
+ "java": "17"
128
+ },
129
+ "verified": [
130
+ {
131
+ "reactNative": "0.74.7",
132
+ "gradle": "8.6",
133
+ "kotlin": "1.9.22",
134
+ "java": "17",
135
+ "date": "2026-09-30",
136
+ "ran": "verify:bare --rn 0.74.7 --platform android: debug and release, rewrite confirmed"
137
+ },
138
+ {
139
+ "reactNative": "0.85.0",
140
+ "gradle": "9.3.1",
141
+ "kotlin": "2.1.20",
142
+ "agp": "8.12.0",
143
+ "java": "17",
144
+ "date": "2026-09-30",
145
+ "ran": "example app: root assembleDebug, assembleRelease with R8, rewrite confirmed; enrollment on a Pixel 6"
146
+ },
147
+ {
148
+ "reactNative": "0.86.3 (Expo SDK 57)",
149
+ "gradle": "9.3.1",
150
+ "kotlin": "2.1.20",
151
+ "agp": "8.12.0",
152
+ "java": "17",
153
+ "date": "2026-09-30",
154
+ "ran": "verify:expo --device: debug and release, rewrite confirmed; enrollment on a Pixel 6 through Company Portal"
155
+ },
156
+ {
157
+ "reactNative": "0.87.1",
158
+ "gradle": "9.4.1",
159
+ "kotlin": "2.2.0",
160
+ "agp": "9.2.1",
161
+ "java": "17",
162
+ "date": "2026-09-30",
163
+ "ran": "verify:bare: debug and release, rewrite confirmed"
164
+ }
165
+ ]
69
166
  }
70
167
  }
@@ -20,6 +20,9 @@ let tokenProvider: TokenProvider | null = null;
20
20
  * Registers the provider for `authMode: 'external'`. Return `null` from it to say
21
21
  * "cannot get a token right now" — the SDK treats that as needing authorization rather
22
22
  * than as a hard failure.
23
+ *
24
+ * @internal Outside the contract in 1.x: `external` is kept working but has not been run
25
+ * on a device against a tenant. Planned for 2.0 — see `AuthMode`.
23
26
  */
24
27
  export function setTokenProvider(provider: TokenProvider | null): void {
25
28
  tokenProvider = provider;
package/src/index.ts CHANGED
@@ -115,6 +115,7 @@ const Intune = {
115
115
  setResetHandler,
116
116
  getPolicy,
117
117
  getDiagnostics,
118
+ /** @internal Outside the contract in 1.x — see `AuthMode`. */
118
119
  setTokenProvider,
119
120
  onEnrollmentResult,
120
121
  onUnenrollmentResult,
@@ -97,19 +97,38 @@ const RESET_STAGES = new Set<string>(Object.values(ResetStage));
97
97
 
98
98
  const RESET_REASONS = new Set<string>([
99
99
  'logout',
100
- 'tenant_changed',
101
- 'intune_disabled',
102
- 'remote_wipe',
103
- 'account_switch',
104
- 'support_reset',
100
+ 'tenantChanged',
101
+ 'intuneDisabled',
102
+ 'remoteWipe',
103
+ 'accountSwitch',
104
+ 'supportReset',
105
105
  'resume',
106
106
  ]);
107
107
 
108
+ /**
109
+ * The spellings before 1.0. The journal is persisted on the device, so an app updated
110
+ * while a reset is unfinished — the process killed mid-reset, or a service wipe waiting
111
+ * for the next launch — reads a reason an older version wrote. Dropping it to null would
112
+ * lose exactly the case the reason exists for: telling the user their administrator
113
+ * removed access.
114
+ */
115
+ const LEGACY_RESET_REASONS: Record<string, ResetReason> = {
116
+ tenant_changed: 'tenantChanged',
117
+ intune_disabled: 'intuneDisabled',
118
+ remote_wipe: 'remoteWipe',
119
+ account_switch: 'accountSwitch',
120
+ support_reset: 'supportReset',
121
+ };
122
+
108
123
  /** Null rather than a guess: an unrecognised reason is not worth acting on. */
109
124
  export function toResetReason(value: unknown): ResetReason | null {
110
- return typeof value === 'string' && RESET_REASONS.has(value)
111
- ? (value as ResetReason)
112
- : null;
125
+ if (typeof value !== 'string') {
126
+ return null;
127
+ }
128
+ if (RESET_REASONS.has(value)) {
129
+ return value as ResetReason;
130
+ }
131
+ return LEGACY_RESET_REASONS[value] ?? null;
113
132
  }
114
133
 
115
134
  export function toResetStage(value: unknown): ResetStage | null {
package/src/types.ts CHANGED
@@ -18,6 +18,10 @@
18
18
  *
19
19
  * The blocking distinction in SPEC §8 is load-bearing and easy to get backwards:
20
20
  * `NotLicensed` and `NotTargeted` must NOT block the user, `Failed` must.
21
+ *
22
+ * Gains members in minor versions, as Microsoft's SDKs add statuses — give a `switch`
23
+ * over it a `default`, and treat what lands there as not blocking. See
24
+ * [Versioning](https://bayazetyan.github.io/react-native-intune/docs/versioning).
21
25
  */
22
26
  export enum EnrollmentStatus {
23
27
  Succeeded = 'succeeded',
@@ -61,20 +65,33 @@ export enum FileProtectionLevel {
61
65
  None = 'none',
62
66
  }
63
67
 
64
- /** Why a reset was started. Recorded in the journal and surfaced in `getDiagnostics`. */
68
+ /**
69
+ * Why a reset was started. Recorded in the journal and surfaced in `getDiagnostics`.
70
+ *
71
+ * camelCase, like every other string value in this API. Before 1.0 these were
72
+ * snake_case (`remote_wipe`); a journal written by an older version is still read, and
73
+ * its reason reported in the new spelling.
74
+ *
75
+ * Gains members in minor versions — give a `switch` over it a `default`.
76
+ */
65
77
  export type ResetReason =
66
78
  | 'logout'
67
- | 'tenant_changed'
68
- | 'intune_disabled'
69
- | 'remote_wipe'
70
- | 'account_switch'
71
- | 'support_reset'
79
+ | 'tenantChanged'
80
+ | 'intuneDisabled'
81
+ | 'remoteWipe'
82
+ | 'accountSwitch'
83
+ | 'supportReset'
72
84
  | 'resume';
73
85
 
74
86
  /**
75
- * Who owns MSAL. `builtin` is the module (SPEC §3.1) and the default. `external` is for
76
- * apps that already have their own MSAL and supply MAM tokens via `setTokenProvider`;
77
- * it is a supported mode, not a documentation fallback.
87
+ * Who owns MSAL. `'builtin'` — the module — is the default and the only mode supported
88
+ * in 1.x.
89
+ *
90
+ * `'external'` is for apps that already have their own MSAL and answer the SDK's token
91
+ * requests themselves.
92
+ * **Not supported in 1.x.** `authMode: 'external'` is kept working in the code, but it has
93
+ * not been run on a device against a tenant, so it is undocumented and outside the semver
94
+ * contract — it may change in any release. It is planned for 2.0.
78
95
  */
79
96
  export type AuthMode = 'builtin' | 'external';
80
97
 
@@ -89,7 +106,7 @@ export type IntuneConfig = {
89
106
  authority: string;
90
107
  /** Must match the platform format — see the README. */
91
108
  redirectUri: string;
92
- /** Default `'builtin'`. */
109
+ /** Default `'builtin'`, and the only value supported in 1.x. */
93
110
  authMode?: AuthMode;
94
111
  /** Default `false`. */
95
112
  verboseLogging?: boolean;
@@ -168,7 +185,10 @@ export type EnrollParams = {
168
185
  /** Entra object ID. Never a UPN — that is what `upn` below is for. */
169
186
  accountId: string;
170
187
  /**
171
- * **Android, `authMode: 'external'` only.** `registerAccountForMAM` takes the UPN as
188
+ * @internal
189
+ *
190
+ * **Android, `authMode: 'external'` only** — outside the contract in 1.x, see `AuthMode`.
191
+ * `registerAccountForMAM` takes the UPN as
172
192
  * well as the object ID and will not accept null for it, and in `external` mode the
173
193
  * module has no MSAL cache to resolve one from.
174
194
  *
@@ -208,9 +228,19 @@ export type BrokerStatus = {
208
228
  export type EnrollmentResult = {
209
229
  status: EnrollmentStatus;
210
230
  accountId: string | null;
211
- /** Raw platform constant *name*, e.g. `LicensedNotTargeted`. For support bundles. */
231
+ /**
232
+ * Raw platform constant *name*, e.g. `LicensedNotTargeted`. For support bundles.
233
+ *
234
+ * **Outside the semver contract.** It is the SDK's own name and changes when
235
+ * Microsoft's SDK does — log it, do not branch on it. Branch on `status`. See
236
+ * [Versioning](https://bayazetyan.github.io/react-native-intune/docs/versioning).
237
+ */
212
238
  nativeCode: string;
213
- /** SDK debug string. Safe to log — contains no token. */
239
+ /**
240
+ * SDK debug string. Safe to log — contains no token.
241
+ *
242
+ * **Outside the semver contract**, like `nativeCode`.
243
+ */
214
244
  nativeMessage: string;
215
245
  restartRequired: boolean;
216
246
  };
@@ -224,6 +254,11 @@ export type EnrollmentResult = {
224
254
  export type IntuneState = {
225
255
  configured: boolean;
226
256
  configuredTenantId: string | null;
257
+ /**
258
+ * What the SDK reports as registered. An array because that is the SDK's shape; with
259
+ * one identity per installation it holds zero or one account in practice, and more
260
+ * than one means an earlier registration was left behind.
261
+ */
227
262
  registeredAccountIds: string[];
228
263
  enrolledAccountId: string | null;
229
264
  /** `null` means no account is registered. */
@@ -283,12 +318,21 @@ export type PolicySnapshot = {
283
318
  raw: Record<string, string>;
284
319
  };
285
320
 
286
- /** Opaque key/value for support bundles. Contains no tokens and no UPNs. */
321
+ /**
322
+ * Opaque key/value for support bundles. Contains no tokens and no UPNs.
323
+ *
324
+ * **Outside the semver contract**: the keys follow whatever is useful to diagnose and
325
+ * may change in any release. See [Versioning](https://bayazetyan.github.io/react-native-intune/docs/versioning).
326
+ */
287
327
  export type Diagnostics = Record<string, string>;
288
328
 
289
329
  // ---------------------------------------------------------------- events
290
330
 
291
- /** Payload of a native-initiated MAM service token request. See SPEC §13.4. */
331
+ /**
332
+ * Payload of a native-initiated MAM service token request, for `authMode: 'external'`.
333
+ *
334
+ * @internal Outside the contract in 1.x — see `AuthMode`.
335
+ */
292
336
  export type TokenRequest = {
293
337
  resourceId: string;
294
338
  tenantId: string;
@@ -299,6 +343,8 @@ export type TokenRequest = {
299
343
  /**
300
344
  * Return the access token, or `null` to signal "cannot get a token right now". Runs on a
301
345
  * background thread's behalf, and the SDK may ask with no JS call in flight.
346
+ *
347
+ * @internal Outside the contract in 1.x — see `AuthMode`.
302
348
  */
303
349
  export type TokenProvider = (
304
350
  request: TokenRequest
@@ -335,6 +381,7 @@ export type IntuneEvents = {
335
381
  unenrollmentResult: EnrollmentResult;
336
382
  wipeRequested: WipeRequest;
337
383
  restartRequired: RestartRequest;
384
+ /** @internal The `external` token bridge — outside the contract in 1.x. */
338
385
  tokenRequest: TokenRequest & { requestId: string };
339
386
  brokerStatusChanged: BrokerStatus;
340
387
  };
@@ -347,6 +394,9 @@ export type IntuneEvents = {
347
394
  *
348
395
  * `E_NATIVE` appearing in logs is a signal to extend the native mapping table, not an
349
396
  * acceptable steady state.
397
+ *
398
+ * Gains members in minor versions — give a `switch` over it a `default`. See
399
+ * [Versioning](https://bayazetyan.github.io/react-native-intune/docs/versioning).
350
400
  */
351
401
  export type IntuneErrorCode =
352
402
  | 'E_NOT_CONFIGURED'
@@ -354,6 +404,7 @@ export type IntuneErrorCode =
354
404
  | 'E_SDK_UNAVAILABLE'
355
405
  | 'E_BROKER_MISSING'
356
406
  | 'E_INVALID_ACCOUNT_ID'
407
+ // The next three are reached only with authMode 'external', outside the contract in 1.x.
357
408
  | 'E_TOKEN_PROVIDER_FAILED'
358
409
  | 'E_TOKEN_PROVIDER_MISSING'
359
410
  | 'E_EXTERNAL_AUTH_MODE'