react-native-intune 0.1.0 → 0.1.1-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.
- package/README.md +1 -1
- package/android/src/main/java/com/reactnativeintune/RNIntuneAuthCallback.kt +9 -4
- package/package.json +9 -2
- package/plugin/transforms.js +101 -59
- package/plugin/withIntune.js +3 -3
- package/scripts/doctor.mjs +12 -2
- package/scripts/lib/checks.mjs +123 -31
- package/sdk-lock.json +2 -2
package/README.md
CHANGED
|
@@ -90,7 +90,7 @@ app project** — `Info.plist`, entitlements, `AndroidManifest.xml`, your app mo
|
|
|
90
90
|
`build.gradle`, your `Application` class — and autolinking does not touch any of them.
|
|
91
91
|
|
|
92
92
|
**Expo:** add the config plugin and run `expo prebuild`. Nearly everything is applied for
|
|
93
|
-
you, including the `
|
|
93
|
+
you, including the auth callback in your `Application` class.
|
|
94
94
|
|
|
95
95
|
```json
|
|
96
96
|
{ "expo": { "plugins": [["react-native-intune", { "androidSignatureHash": "…" }]] } }
|
|
@@ -13,20 +13,25 @@ import java.util.concurrent.atomic.AtomicLong
|
|
|
13
13
|
/**
|
|
14
14
|
* Supplies the MAM service token (SPEC §13.4, §6.1.2).
|
|
15
15
|
*
|
|
16
|
-
* **The consumer registers this from `Application.
|
|
16
|
+
* **The consumer registers this from `Application.onCreate()`,** not from JS. The SDK
|
|
17
17
|
* can ask for a token before any React context exists — it has its own enrollment retry
|
|
18
18
|
* schedule that survives restarts — so registration has to happen earlier than JS is
|
|
19
19
|
* able to run. The module supplies the class; the wiring is one line in the host app:
|
|
20
20
|
*
|
|
21
21
|
* ```kotlin
|
|
22
|
-
* class MainApplication :
|
|
23
|
-
* override fun
|
|
24
|
-
* super.
|
|
22
|
+
* class MainApplication : Application(), ReactApplication {
|
|
23
|
+
* override fun onCreate() {
|
|
24
|
+
* super.onCreate()
|
|
25
25
|
* RNIntuneAuthCallback.register(this)
|
|
26
26
|
* }
|
|
27
27
|
* }
|
|
28
28
|
* ```
|
|
29
29
|
*
|
|
30
|
+
* `Application`, not `MAMApplication`, and `onCreate`, not `onMAMCreate`: the MAM Gradle
|
|
31
|
+
* plugin rewrites both at build time. Written in source, `MAMApplication` does not
|
|
32
|
+
* compile — this module declares the SDK as `implementation`, which is not transitive,
|
|
33
|
+
* so it is not on the app module's classpath (issue #5).
|
|
34
|
+
*
|
|
30
35
|
* `acquireToken` is called **on a background thread and must return synchronously**, so
|
|
31
36
|
* this blocks that thread while JS answers. That is why the timeout is not optional: the
|
|
32
37
|
* SDK's thread is held for its duration.
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "react-native-intune",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.1-rc.1",
|
|
4
4
|
"description": "Microsoft Intune app protection policies (MAM) for React Native",
|
|
5
5
|
"main": "./lib/module/index.js",
|
|
6
6
|
"types": "./lib/typescript/src/index.d.ts",
|
|
@@ -22,6 +22,7 @@
|
|
|
22
22
|
"app.plugin.js",
|
|
23
23
|
"plugin",
|
|
24
24
|
"scripts",
|
|
25
|
+
"!scripts/verify-expo.mjs",
|
|
25
26
|
"sdk-versions.json",
|
|
26
27
|
"sdk-lock.json",
|
|
27
28
|
"!vendor",
|
|
@@ -43,12 +44,13 @@
|
|
|
43
44
|
],
|
|
44
45
|
"scripts": {
|
|
45
46
|
"fetch-sdks": "node scripts/fetch-sdks.mjs",
|
|
47
|
+
"verify:expo": "node scripts/verify-expo.mjs",
|
|
46
48
|
"postinstall": "node scripts/fetch-sdks.mjs --soft --quiet",
|
|
47
49
|
"example": "yarn workspace react-native-intune-example",
|
|
48
50
|
"clean": "del-cli android/build example/android/build example/android/app/build example/ios/build lib",
|
|
49
51
|
"prepare": "bob build",
|
|
50
52
|
"typecheck": "yarn workspace react-native-intune-example ensure-tenant && tsc",
|
|
51
|
-
"test": "jest",
|
|
53
|
+
"test": "NODE_OPTIONS=--experimental-vm-modules jest",
|
|
52
54
|
"release": "release-it --only-version",
|
|
53
55
|
"lint": "eslint \"**/*.{js,ts,tsx}\""
|
|
54
56
|
},
|
|
@@ -145,6 +147,11 @@
|
|
|
145
147
|
"modulePathIgnorePatterns": [
|
|
146
148
|
"<rootDir>/example/node_modules",
|
|
147
149
|
"<rootDir>/lib/"
|
|
150
|
+
],
|
|
151
|
+
"testMatch": [
|
|
152
|
+
"**/__tests__/**/*.[jt]s?(x)",
|
|
153
|
+
"**/?(*.)+(spec|test).[jt]s?(x)",
|
|
154
|
+
"**/__tests__/**/*.test.mjs"
|
|
148
155
|
]
|
|
149
156
|
},
|
|
150
157
|
"commitlint": {
|
package/plugin/transforms.js
CHANGED
|
@@ -63,14 +63,18 @@ function withMamClasspath(contents, { javassist = '3.29.2-GA' } = {}) {
|
|
|
63
63
|
}
|
|
64
64
|
|
|
65
65
|
/**
|
|
66
|
-
*
|
|
67
|
-
* callback.
|
|
66
|
+
* Registers the auth callback in the generated Application class.
|
|
68
67
|
*
|
|
69
|
-
*
|
|
70
|
-
*
|
|
71
|
-
*
|
|
72
|
-
*
|
|
73
|
-
*
|
|
68
|
+
* **It deliberately does not touch the superclass.** An earlier version rewrote
|
|
69
|
+
* `: Application()` to `: MAMApplication()` in source, which does not compile in a
|
|
70
|
+
* consumer app: this package declares the MAM AAR as `implementation files(...)`, and
|
|
71
|
+
* `implementation` is not transitive, so the SDK's classes are not on the app module's
|
|
72
|
+
* compile classpath. The superclass resolves to an error type and every member
|
|
73
|
+
* inheriting from it fails with it — see issue #5.
|
|
74
|
+
*
|
|
75
|
+
* The superclass is the MAM Gradle plugin's job, in bytecode, which is what Microsoft's
|
|
76
|
+
* own guidance describes and what this repository's example app has always relied on.
|
|
77
|
+
* Rewriting it here was both broken and redundant.
|
|
74
78
|
*
|
|
75
79
|
* The callback goes in `onCreate` deliberately: the MAM plugin rewrites `onCreate` into
|
|
76
80
|
* `onMAMCreate` at build time, so writing `onMAMCreate` here would leave a method the
|
|
@@ -80,69 +84,107 @@ function withMamClasspath(contents, { javassist = '3.29.2-GA' } = {}) {
|
|
|
80
84
|
* act on beats a generated file that does not compile.
|
|
81
85
|
*/
|
|
82
86
|
function withMamApplication(contents, language) {
|
|
83
|
-
|
|
84
|
-
|
|
87
|
+
const isKotlin = language === 'kt';
|
|
88
|
+
|
|
89
|
+
const migrated = revertLegacySuperclass(contents, isKotlin);
|
|
90
|
+
if (migrated === null) {
|
|
91
|
+
return null;
|
|
92
|
+
}
|
|
93
|
+
if (/RNIntuneAuthCallback\s*\.\s*register/.test(migrated)) {
|
|
94
|
+
return migrated;
|
|
85
95
|
}
|
|
86
96
|
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
97
|
+
// `MAMApplication` is accepted as well as `Application`: an app that has wired the SDK
|
|
98
|
+
// into its own module may extend it directly, and the plugin's rewrite is then a no-op
|
|
99
|
+
// rather than an error. What matters is that there is an Application subclass at all —
|
|
100
|
+
// with none, the Gradle plugin has nothing to transform.
|
|
101
|
+
const appClass = isKotlin
|
|
102
|
+
? /class\s+\w+\s*:\s*(?:MAM)?Application\s*\(\s*\)/
|
|
103
|
+
: /class\s+\w+\s+extends\s+(?:MAM)?Application\b/;
|
|
104
|
+
if (!appClass.test(migrated)) {
|
|
92
105
|
return null;
|
|
93
106
|
}
|
|
94
107
|
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
);
|
|
108
|
+
const next = addImports(migrated, [
|
|
109
|
+
`import com.reactnativeintune.RNIntuneAuthCallback${isKotlin ? '' : ';'}`,
|
|
110
|
+
]);
|
|
111
|
+
if (next === null) {
|
|
112
|
+
return null;
|
|
101
113
|
}
|
|
102
114
|
|
|
103
|
-
//
|
|
104
|
-
//
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
'import com.reactnativeintune.RNIntuneAuthCallback;',
|
|
113
|
-
];
|
|
114
|
-
const missing = imports.filter((i) => !next.includes(i));
|
|
115
|
-
if (missing.length > 0) {
|
|
116
|
-
const lastImport = [...next.matchAll(/^import .*$/gm)].pop();
|
|
117
|
-
if (!lastImport) {
|
|
118
|
-
return null;
|
|
119
|
-
}
|
|
120
|
-
const at = lastImport.index + lastImport[0].length;
|
|
121
|
-
next = `${next.slice(0, at)}\n${missing.join('\n')}${next.slice(at)}`;
|
|
115
|
+
// After `super.onCreate()`, not at the top of the method. The superclass is what
|
|
116
|
+
// initialises the MAM machinery the callback registers with, so registering first is
|
|
117
|
+
// registering against nothing — and it fails the way everything here fails, by
|
|
118
|
+
// reporting a licensing-shaped enrollment status that retries quietly.
|
|
119
|
+
const onCreate = isKotlin
|
|
120
|
+
? /(override\s+fun\s+onCreate\s*\(\s*\)\s*\{[^\n]*\n\s*super\.onCreate\s*\(\s*\)\s*\n)/
|
|
121
|
+
: /(public\s+void\s+onCreate\s*\(\s*\)\s*\{[^\n]*\n\s*super\.onCreate\s*\(\s*\)\s*;\s*\n)/;
|
|
122
|
+
if (!onCreate.test(next)) {
|
|
123
|
+
return null;
|
|
122
124
|
}
|
|
125
|
+
const end = isKotlin ? '' : ';';
|
|
126
|
+
const call =
|
|
127
|
+
` ${CALLBACK_COMMENT}\n` +
|
|
128
|
+
` // time, so this belongs here rather than in an onMAMCreate we write.\n` +
|
|
129
|
+
` RNIntuneAuthCallback.register(this)${end}\n`;
|
|
130
|
+
|
|
131
|
+
return next.replace(onCreate, `$1${call}`);
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
/**
|
|
135
|
+
* The first line of the comment this transform writes above the callback. It is also
|
|
136
|
+
* how a file this transform has already touched is recognised — see below.
|
|
137
|
+
*/
|
|
138
|
+
const CALLBACK_COMMENT = `// ${MARKER}: the MAM plugin rewrites onCreate into onMAMCreate at build`;
|
|
123
139
|
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
140
|
+
/**
|
|
141
|
+
* Undoes what 0.1.0 wrote, so the fix reaches people the defect already reached.
|
|
142
|
+
*
|
|
143
|
+
* 0.1.0 rewrote the superclass to `MAMApplication` in source and added its import. The
|
|
144
|
+
* idempotency guard above would leave such a file untouched — it already registers the
|
|
145
|
+
* callback — so `expo prebuild` without `--clean` would keep a file that does not
|
|
146
|
+
* compile, on the version that is supposed to fix it.
|
|
147
|
+
*
|
|
148
|
+
* Only a file carrying this transform's own comment is changed. That comment is proof
|
|
149
|
+
* the superclass was ours to write; an app that extends `MAMApplication` deliberately,
|
|
150
|
+
* with the SDK on its own classpath, is left alone.
|
|
151
|
+
*/
|
|
152
|
+
function revertLegacySuperclass(contents, isKotlin) {
|
|
153
|
+
if (!contents.includes(CALLBACK_COMMENT)) {
|
|
154
|
+
return contents;
|
|
155
|
+
}
|
|
156
|
+
const superclass = isKotlin
|
|
157
|
+
? /(class\s+\w+\s*:\s*)MAMApplication(\s*\(\s*\))/
|
|
158
|
+
: /(class\s+\w+\s+extends\s+)MAMApplication\b/;
|
|
159
|
+
if (!superclass.test(contents)) {
|
|
160
|
+
return contents;
|
|
143
161
|
}
|
|
162
|
+
const reverted = contents
|
|
163
|
+
.replace(superclass, isKotlin ? '$1Application$2' : '$1Application')
|
|
164
|
+
.replace(
|
|
165
|
+
/^import com\.microsoft\.intune\.mam\.client\.app\.MAMApplication;?\n/m,
|
|
166
|
+
''
|
|
167
|
+
);
|
|
168
|
+
return addImports(reverted, [
|
|
169
|
+
`import android.app.Application${isKotlin ? '' : ';'}`,
|
|
170
|
+
]);
|
|
171
|
+
}
|
|
144
172
|
|
|
145
|
-
|
|
173
|
+
/**
|
|
174
|
+
* Adds each missing import after the last existing one. Null when the file has no
|
|
175
|
+
* import to anchor on, which a generated Application class always has.
|
|
176
|
+
*/
|
|
177
|
+
function addImports(contents, imports) {
|
|
178
|
+
const missing = imports.filter((i) => !contents.includes(i));
|
|
179
|
+
if (missing.length === 0) {
|
|
180
|
+
return contents;
|
|
181
|
+
}
|
|
182
|
+
const lastImport = [...contents.matchAll(/^import .*$/gm)].pop();
|
|
183
|
+
if (!lastImport) {
|
|
184
|
+
return null;
|
|
185
|
+
}
|
|
186
|
+
const at = lastImport.index + lastImport[0].length;
|
|
187
|
+
return `${contents.slice(0, at)}\n${missing.join('\n')}${contents.slice(at)}`;
|
|
146
188
|
}
|
|
147
189
|
|
|
148
190
|
module.exports = {
|
package/plugin/withIntune.js
CHANGED
|
@@ -285,9 +285,9 @@ const withMamApplicationClass = (config) =>
|
|
|
285
285
|
// leaving the app unprotected. A quiet skip here is the worst outcome available.
|
|
286
286
|
WarningAggregator.addWarningAndroid(
|
|
287
287
|
MARKER,
|
|
288
|
-
'Could not
|
|
289
|
-
'recognised. THE APP WILL BUILD AND RUN WITHOUT PROTECTION until this
|
|
290
|
-
'done by hand. Run `npx react-native-intune doctor` for the exact change.'
|
|
288
|
+
'Could not register the MAM auth callback in MainApplication — its shape was ' +
|
|
289
|
+
'not recognised. THE APP WILL BUILD AND RUN WITHOUT PROTECTION until this ' +
|
|
290
|
+
'is done by hand. Run `npx react-native-intune doctor` for the exact change.'
|
|
291
291
|
);
|
|
292
292
|
return c;
|
|
293
293
|
}
|
package/scripts/doctor.mjs
CHANGED
|
@@ -128,7 +128,9 @@ function main() {
|
|
|
128
128
|
}
|
|
129
129
|
process.stdout.write(`\n${platform === 'ios' ? 'iOS' : 'Android'}\n`);
|
|
130
130
|
for (const r of forPlatform) {
|
|
131
|
-
process.stdout.write(
|
|
131
|
+
process.stdout.write(
|
|
132
|
+
` ${glyph(r.state, r.check.severity)} ${r.check.title}${suffix(r)}\n`
|
|
133
|
+
);
|
|
132
134
|
}
|
|
133
135
|
}
|
|
134
136
|
|
|
@@ -183,7 +185,15 @@ function main() {
|
|
|
183
185
|
process.exit(s.blocking.length > 0 ? 1 : 0);
|
|
184
186
|
}
|
|
185
187
|
|
|
186
|
-
|
|
188
|
+
/**
|
|
189
|
+
* An advisory never reads as MISS or WRONG. It does not fail the exit code by design,
|
|
190
|
+
* and a line that shouts while the detail underneath says "advisory" teaches people to
|
|
191
|
+
* discount the ones that do matter.
|
|
192
|
+
*/
|
|
193
|
+
function glyph(state, severity) {
|
|
194
|
+
if (severity === 'advisory' && state !== 'ok' && state !== 'skip') {
|
|
195
|
+
return 'note';
|
|
196
|
+
}
|
|
187
197
|
return { ok: 'ok ', missing: 'MISS', wrong: 'WRONG', unknown: '? ', skip: '-- ' }[
|
|
188
198
|
state
|
|
189
199
|
];
|
package/scripts/lib/checks.mjs
CHANGED
|
@@ -49,30 +49,58 @@ export const KEYCHAIN_GROUPS = [
|
|
|
49
49
|
|
|
50
50
|
// ---------------------------------------------------------------- android
|
|
51
51
|
|
|
52
|
+
/**
|
|
53
|
+
* Source text with `//` and block comments removed, for Gradle, Kotlin and Java alike.
|
|
54
|
+
*
|
|
55
|
+
* Every check that reads source for a declaration goes through this, because matching
|
|
56
|
+
* text that includes comments is how `android-mam-application` reported this
|
|
57
|
+
* repository's own example as passing for as long as it existed (issue #5). A `//` is
|
|
58
|
+
* only a comment after whitespace or at the start of a line, so `https://` in a
|
|
59
|
+
* repository URL survives.
|
|
60
|
+
*/
|
|
61
|
+
function stripComments(src) {
|
|
62
|
+
return src.replace(/\/\*[\s\S]*?\*\//g, '').replace(/(^|\s)\/\/.*$/gm, '$1');
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
/**
|
|
66
|
+
* An actual application of the plugin: `apply plugin: "…"`, `id "…"` or `id("…")`. The
|
|
67
|
+
* closing quote is required, so the `com.microsoft.intune.mam.build` classpath entry in
|
|
68
|
+
* the root build file does not count.
|
|
69
|
+
*/
|
|
70
|
+
const MAM_PLUGIN_APPLIED =
|
|
71
|
+
/(?:apply\s+plugin\s*:\s*|\bid\s*\(?\s*)['"]com\.microsoft\.intune\.mam['"]/;
|
|
72
|
+
|
|
73
|
+
/**
|
|
74
|
+
* Whether the MAM Gradle plugin is applied to the app module. Shared by the plugin check
|
|
75
|
+
* and the Application check, which both depend on the answer and must never disagree
|
|
76
|
+
* about it.
|
|
77
|
+
*/
|
|
78
|
+
function mamPluginApplied(project) {
|
|
79
|
+
const a = project.android;
|
|
80
|
+
if (a.appBuildGradleKts && !a.appBuildGradle) {
|
|
81
|
+
return {
|
|
82
|
+
state: 'unknown',
|
|
83
|
+
detail:
|
|
84
|
+
'app/build.gradle.kts — the Kotlin DSL is not inspected. Check by hand that ' +
|
|
85
|
+
'the plugin is applied.',
|
|
86
|
+
};
|
|
87
|
+
}
|
|
88
|
+
const src = read(a.appBuildGradle);
|
|
89
|
+
if (!src) {
|
|
90
|
+
return { state: 'unknown', detail: 'android/app/build.gradle not found' };
|
|
91
|
+
}
|
|
92
|
+
return MAM_PLUGIN_APPLIED.test(stripComments(src))
|
|
93
|
+
? { state: 'ok' }
|
|
94
|
+
: { state: 'missing' };
|
|
95
|
+
}
|
|
96
|
+
|
|
52
97
|
const androidChecks = [
|
|
53
98
|
{
|
|
54
99
|
id: 'android-mam-plugin',
|
|
55
100
|
platform: 'android',
|
|
56
101
|
title: 'MAM Gradle plugin applied to the app module',
|
|
57
102
|
severity: 'silent',
|
|
58
|
-
inspect
|
|
59
|
-
const a = project.android;
|
|
60
|
-
if (a.appBuildGradleKts && !a.appBuildGradle) {
|
|
61
|
-
return {
|
|
62
|
-
state: 'unknown',
|
|
63
|
-
detail:
|
|
64
|
-
'app/build.gradle.kts — the Kotlin DSL is not inspected. Check by hand ' +
|
|
65
|
-
'that the plugin is applied.',
|
|
66
|
-
};
|
|
67
|
-
}
|
|
68
|
-
const src = read(a.appBuildGradle);
|
|
69
|
-
if (!src) {
|
|
70
|
-
return { state: 'unknown', detail: 'android/app/build.gradle not found' };
|
|
71
|
-
}
|
|
72
|
-
return /com\.microsoft\.intune\.mam/.test(src)
|
|
73
|
-
? { state: 'ok' }
|
|
74
|
-
: { state: 'missing' };
|
|
75
|
-
},
|
|
103
|
+
inspect: mamPluginApplied,
|
|
76
104
|
why:
|
|
77
105
|
'The plugin rewrites bytecode across the app and every dependency. Applied to ' +
|
|
78
106
|
'the library instead of the app module it rewrites only the library, builds ' +
|
|
@@ -174,8 +202,18 @@ ${BROKER_PACKAGES.map((p) => ` <package android:name="${p}" />`).join('\n
|
|
|
174
202
|
{
|
|
175
203
|
id: 'android-mam-application',
|
|
176
204
|
platform: 'android',
|
|
177
|
-
title: 'Application class
|
|
205
|
+
title: 'Application class is transformed into a MAMApplication',
|
|
178
206
|
severity: 'silent',
|
|
207
|
+
/**
|
|
208
|
+
* What makes the app a MAMApplication is the Gradle plugin's bytecode rewrite, not
|
|
209
|
+
* the text of the source file — so this reads both, and a plain `Application`
|
|
210
|
+
* subclass with the plugin applied is the correct, expected shape.
|
|
211
|
+
*
|
|
212
|
+
* The previous version matched the substring `MAMApplication` anywhere in the
|
|
213
|
+
* source. It reported this repository's own example as passing because the word
|
|
214
|
+
* appears in a *comment* there, which is why nobody noticed it was asserting the
|
|
215
|
+
* wrong thing entirely. Issue #5.
|
|
216
|
+
*/
|
|
179
217
|
inspect(project) {
|
|
180
218
|
const sources = project.android.applicationSources;
|
|
181
219
|
if (sources.length === 0) {
|
|
@@ -184,26 +222,78 @@ ${BROKER_PACKAGES.map((p) => ` <package android:name="${p}" />`).join('\n
|
|
|
184
222
|
detail: 'no Application subclass found in android/app/src/main',
|
|
185
223
|
};
|
|
186
224
|
}
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
225
|
+
|
|
226
|
+
// A class declaration, never a mention: comments are stripped first, so neither
|
|
227
|
+
// an explanatory comment nor a commented-out example counts.
|
|
228
|
+
const declared = /class\s+\w+\s*(?::\s*|\s+extends\s+)MAMApplication\b/;
|
|
229
|
+
const explicit = sources.find((f) => declared.test(stripComments(read(f))));
|
|
230
|
+
const a = project.android;
|
|
231
|
+
|
|
232
|
+
if (explicit) {
|
|
233
|
+
// Legitimate only when the app module has the SDK on its own compile classpath.
|
|
234
|
+
// This package declares the AAR as `implementation`, which is not transitive,
|
|
235
|
+
// so without that the supertype does not resolve — exactly the shape a 0.1.0
|
|
236
|
+
// Expo prebuild wrote, which must not read as protected (issue #5).
|
|
237
|
+
if (a.appBuildGradleKts && !a.appBuildGradle) {
|
|
238
|
+
return {
|
|
239
|
+
state: 'unknown',
|
|
240
|
+
detail:
|
|
241
|
+
`${rel(project, explicit)} extends MAMApplication directly, and the ` +
|
|
242
|
+
'Kotlin DSL build file is not inspected — check by hand that the MAM SDK ' +
|
|
243
|
+
'is on the app module\u2019s classpath, or this does not compile.',
|
|
244
|
+
};
|
|
245
|
+
}
|
|
246
|
+
const gradle = stripComments(read(a.appBuildGradle));
|
|
247
|
+
if (/Microsoft\.Intune\.MAM\.SDK/.test(gradle)) {
|
|
248
|
+
return {
|
|
249
|
+
state: 'ok',
|
|
250
|
+
detail: `${rel(project, explicit)} extends MAMApplication directly`,
|
|
251
|
+
};
|
|
252
|
+
}
|
|
253
|
+
return {
|
|
254
|
+
state: 'wrong',
|
|
255
|
+
detail:
|
|
256
|
+
`${rel(project, explicit)} extends MAMApplication in source, but the MAM ` +
|
|
257
|
+
'SDK is not on the app module\u2019s classpath, so it does not compile. If ' +
|
|
258
|
+
'expo prebuild wrote it, re-run prebuild with this version.',
|
|
259
|
+
};
|
|
260
|
+
}
|
|
261
|
+
|
|
262
|
+
const plugin = mamPluginApplied(project);
|
|
263
|
+
if (plugin.state === 'unknown') {
|
|
264
|
+
return {
|
|
265
|
+
state: 'unknown',
|
|
266
|
+
detail: `whether the plugin transforms the class cannot be read: ${plugin.detail}`,
|
|
267
|
+
};
|
|
268
|
+
}
|
|
269
|
+
if (plugin.state === 'ok') {
|
|
270
|
+
return {
|
|
271
|
+
state: 'ok',
|
|
272
|
+
detail: `${rel(project, sources[0])} — superclass rewritten by the Gradle plugin`,
|
|
273
|
+
};
|
|
190
274
|
}
|
|
191
275
|
return {
|
|
192
276
|
state: 'wrong',
|
|
193
|
-
detail:
|
|
277
|
+
detail:
|
|
278
|
+
`found ${sources.map((f) => rel(project, f)).join(', ')}, and the MAM ` +
|
|
279
|
+
'Gradle plugin is not applied to the app module — so nothing rewrites it',
|
|
194
280
|
};
|
|
195
281
|
},
|
|
196
282
|
why:
|
|
197
283
|
'One of the three omissions that build and run while protecting nothing. The ' +
|
|
198
|
-
'plugin transforms an Application subclass
|
|
199
|
-
'
|
|
284
|
+
'Gradle plugin transforms an Application subclass into a MAMApplication; with ' +
|
|
285
|
+
'none to transform, or with the plugin not applied, the SDK is present and inert.',
|
|
200
286
|
instruction: () =>
|
|
201
|
-
`
|
|
202
|
-
|
|
287
|
+
`Keep your Application class extending android.app.Application — the MAM Gradle
|
|
288
|
+
plugin rewrites the superclass at build time, and writing MAMApplication in source
|
|
289
|
+
does not compile unless you put the SDK on your app module's classpath yourself.
|
|
290
|
+
|
|
291
|
+
Apply the plugin (see the "MAM Gradle plugin applied to the app module" check), and
|
|
292
|
+
register the auth callback from onCreate, which the plugin rewrites into onMAMCreate:
|
|
203
293
|
|
|
204
|
-
class MainApplication :
|
|
205
|
-
override fun
|
|
206
|
-
super.
|
|
294
|
+
class MainApplication : Application(), ReactApplication {
|
|
295
|
+
override fun onCreate() {
|
|
296
|
+
super.onCreate()
|
|
207
297
|
RNIntuneAuthCallback.register(this)
|
|
208
298
|
// ... the rest of your existing onCreate body
|
|
209
299
|
}
|
|
@@ -222,7 +312,9 @@ ${BROKER_PACKAGES.map((p) => ` <package android:name="${p}" />`).join('\n
|
|
|
222
312
|
if (sources.length === 0) {
|
|
223
313
|
return { state: 'missing', detail: 'no Application subclass found' };
|
|
224
314
|
}
|
|
225
|
-
return sources.some((f) =>
|
|
315
|
+
return sources.some((f) =>
|
|
316
|
+
/RNIntuneAuthCallback\s*\.\s*register/.test(stripComments(read(f)))
|
|
317
|
+
)
|
|
226
318
|
? { state: 'ok' }
|
|
227
319
|
: { state: 'missing' };
|
|
228
320
|
},
|
package/sdk-lock.json
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
"ios": {
|
|
3
3
|
"repo": "microsoftconnect/ms-intune-app-sdk-ios",
|
|
4
4
|
"tag": "21.8.0",
|
|
5
|
-
"fetchedAt": "2026-09-
|
|
5
|
+
"fetchedAt": "2026-09-28T11:15:17.927Z",
|
|
6
6
|
"files": {
|
|
7
7
|
"ios/IntuneMAMSwift.xcframework": "f38f0f130bd2adf0dd936acb26958f6c6ebc0b2d5a4bf886d4c8c7d4ae0870ab",
|
|
8
8
|
"ios/IntuneMAMSwiftStub.xcframework": "a03e563feec36f719bfca34b061e590e1c54a03650a3855a8298988e49b9ea9c",
|
|
@@ -17,7 +17,7 @@
|
|
|
17
17
|
"android": {
|
|
18
18
|
"repo": "microsoftconnect/ms-intune-app-sdk-android",
|
|
19
19
|
"tag": "12.4.0",
|
|
20
|
-
"fetchedAt": "2026-09-
|
|
20
|
+
"fetchedAt": "2026-09-28T11:15:18.852Z",
|
|
21
21
|
"files": {
|
|
22
22
|
"android/Microsoft.Intune.MAM.SDK.aar": "b7e5d0bb86d18f054d8901f168f82e64c79a57334fbb6227136b108b0514e998",
|
|
23
23
|
"android/GradlePlugin/com.microsoft.intune.mam.build.jar": "04ae1e0fbb6b5dfcbf8bd42e8d4f41ad758332ee6c238780a764a9ab1b67446d",
|