@capawesome/capacitor-intune
v0.1.2
Published
Unofficial Capacitor plugin for Microsoft Intune app protection policies (MAM) on Android and iOS.
Maintainers
Readme
Capacitor Intune Plugin
Unofficial Capacitor plugin for Microsoft Intune.[^1]
Features
The Capacitor Intune plugin integrates the Microsoft Intune App SDK for Mobile Application Management (MAM) into Capacitor apps. Here are some of the key features:
- 🖥️ Cross-platform: Supports Android and iOS.
- 🛡️ App Protection Policies: Automatic enforcement of PIN, copy/paste and screenshot restrictions after the native integration.
- 🔐 File Protection: Encrypt, inspect and decrypt files through the Intune App SDK to honor the "Encrypt org data" policy on iOS, where the SDK does not encrypt files on its own.
- 🔑 MSAL: Acquire tokens interactively or silently via the Microsoft Authentication Library.
- 🧾 Enrollment: Register and enroll accounts in Mobile Application Management (MAM) — without device enrollment.
- 🚦 App Protection Conditional Access: Remediate compliance when Microsoft Entra ID requires an app protection policy before issuing tokens.
- 📋 Typed Policy Introspection: Read the applied app protection policy as typed booleans to adapt your UI.
- ⚙️ App Configuration: Read the application configuration deployed via the MAM channel, including conflict information.
- 🧹 Selective Wipe Events: Get notified when the Intune service requests a wipe so you can purge the web layer storage (e.g. IndexedDB, Local Storage) that the SDK cannot wipe itself.
- 🩺 Diagnostics: Show the Intune diagnostic console from JavaScript.
- 📌 Current SDK Pins: Built against current Microsoft Intune App SDK and MSAL versions — Microsoft blocks apps that ship outdated SDKs.
- 🤝 Compatibility: Works alongside the Managed Configurations plugin, which covers the MDM channel (see Choosing between the MAM and MDM channel).
- 📦 CocoaPods & SPM: Supports CocoaPods and Swift Package Manager for iOS.
- 🔁 Up-to-date: Always supports the latest Capacitor version.
Missing a feature? Just open an issue and we'll take a look!
Use Cases
The Intune plugin is typically used in line-of-business apps that are distributed to employees of organizations that manage corporate data with Microsoft Intune, for example:
- App protection without device enrollment: Protect corporate data in your app on personal (BYOD) devices via Mobile Application Management (MAM).
- Conditional access: Combine with Microsoft Entra conditional access policies that require an Intune-protected app (see Handle App Protection Conditional Access).
- Policy-aware UI: Read the applied app protection policy and hide or disable features (e.g. local export) that the policy does not allow.
- Encrypt organization data: Protect recordings, downloads and exports on iOS when the app protection policy requires file encryption.
- Per-tenant configuration: Read the application configuration that the organization's IT administrator has deployed for the signed-in account.
- Selective wipe: Clean up the web layer storage of your app when the organization wipes its corporate data.
- Ionic enterprise migration: Migrate from the discontinued Ionic enterprise Intune integration to a maintained, free plugin.
Compatibility
| Plugin Version | Capacitor Version | Status | | -------------- | ----------------- | -------------- | | 0.x.x | >=8.x.x | Active support |
Installation
You can use our AI-Assisted Setup to install the plugin. Add the Capawesome Skills to your AI tool using the following command:
npx skills add capawesome-team/skills --skill capacitor-pluginsThen use the following prompt:
Use the `capacitor-plugins` skill from `capawesome-team/skills` to install the `@capawesome/capacitor-intune` plugin in my project.If you prefer Manual Setup, install the plugin by running the following commands and follow the platform-specific instructions below:
npm install @capawesome/capacitor-intune
npx cap syncThis plugin requires a Microsoft Intune tenant with Intune licenses and an app registration in Microsoft Entra ID. Create the app registration in the Microsoft Entra admin center, note its Application (client) ID and configure the platform-specific redirect URIs (see below). The acquireToken(...) scopes you request must be exposed or granted on this app registration.
[!IMPORTANT] The Intune App SDKs are developed and licensed by Microsoft (see Third-Party Notices). This plugin declares them as dependencies and downloads them from Microsoft's official repositories at build or install time. It does not bundle or modify them.
Android
The Microsoft Intune App SDK for Android is downloaded automatically from Microsoft's official GitHub repository during the Gradle build. However, the Intune App SDK requires several changes to your app project that no plugin can make for you. Follow the steps below or use Trapeze to automate them.
Variables
This plugin will use the following project variables (defined in your app's variables.gradle file):
$intuneMamSdkVersionversion of the Microsoft Intune App SDK for Android (default:12.4.0)$msalVersionversion ofcom.microsoft.identity.client:msal(default:8.4.0)
MAM Build Plugin
The Intune App SDK relies on a Gradle build plugin that rewrites the Android base classes of your app and all Capacitor plugins to their MAM equivalents. Only the app module can apply this plugin — it cannot be applied by a library. Add the following to the buildscript block of your android/build.gradle file:
buildscript {
repositories {
google()
mavenCentral()
+ ivy {
+ url 'https://raw.githubusercontent.com/microsoftconnect/ms-intune-app-sdk-android'
+ patternLayout { artifact '[revision]/GradlePlugin/[artifact].[ext]' }
+ metadataSources { artifact() }
+ content { includeGroup 'com.microsoft.intune.mam.build' }
+ }
}
dependencies {
classpath 'com.android.tools.build:gradle:8.13.0'
+ classpath 'org.javassist:javassist:3.29.2-GA'
+ classpath 'com.microsoft.intune.mam.build:com.microsoft.intune.mam.build:12.4.0@jar'
}
}Then apply the plugin in your android/app/build.gradle file:
apply plugin: 'com.android.application'
+apply plugin: 'com.microsoft.intune.mam'Application Class
The Intune App SDK requires an Application class that registers the MAM components before any other code runs. Set the ready-made class provided by this plugin in the application tag of your android/app/src/main/AndroidManifest.xml file:
<application
android:name="io.capawesome.capacitorjs.plugins.intune.IntuneApplication"
...>If your app already uses a custom Application class, call the initializer yourself instead:
import io.capawesome.capacitorjs.plugins.intune.Intune;
public class MyApplication extends Application {
@Override
public void onCreate() {
super.onCreate();
Intune.initialize(this);
}
}It's also recommended to disable predictive back gestures in the application tag since the Intune App SDK does not support them yet:
<application
android:enableOnBackInvokedCallback="false"
...>MSAL Configuration
Create the file android/app/src/main/res/raw/auth_config.json with your MSAL configuration:
{
"client_id": "YOUR_CLIENT_ID",
"authorization_user_agent": "DEFAULT",
"redirect_uri": "msauth://YOUR_PACKAGE_NAME/YOUR_BASE64_URL_ENCODED_PACKAGE_SIGNATURE",
"account_mode": "MULTIPLE",
"broker_redirect_uri_registered": true,
"client_capabilities": "protapp",
"authorities": [
{
"type": "AAD",
"audience": {
"type": "AzureADMultipleOrgs"
}
}
]
}The client_capabilities entry declares that your app supports App Protection Conditional Access. On iOS, the plugin declares it automatically.
You can generate the base64-encoded signature hash of your signing key with:
keytool -exportcert -alias YOUR_KEY_ALIAS -keystore YOUR_KEYSTORE | openssl sha1 -binary | openssl base64Make sure the same redirect URI (msauth://YOUR_PACKAGE_NAME/YOUR_BASE64_URL_ENCODED_PACKAGE_SIGNATURE) is registered as an Android platform redirect URI on your app registration in the Microsoft Entra admin center.
Next, add the following activity to the application tag of your android/app/src/main/AndroidManifest.xml file so that MSAL can receive the redirect from the Microsoft sign-in flow:
<activity android:name="com.microsoft.identity.client.BrowserTabActivity" android:exported="true">
<intent-filter>
<action android:name="android.intent.action.VIEW" />
<category android:name="android.intent.category.DEFAULT" />
<category android:name="android.intent.category.BROWSABLE" />
<data android:scheme="msauth" android:host="YOUR_PACKAGE_NAME" android:path="/YOUR_BASE64_ENCODED_PACKAGE_SIGNATURE" />
</intent-filter>
</activity>Gradle Properties
Add the following to your android/gradle.properties file. Without it, release builds may report MAM Enabled: No in the diagnostic console:
android.enableResourceOptimizations=falseCompany Portal
App protection policies are only applied when the Company Portal app is installed on the device. The user does not need to sign in to the Company Portal. Without it, your app behaves as unmanaged.
iOS
The Microsoft Intune App SDK for iOS requires iOS 17+ as deployment target and a current Xcode version. This deviates from the usual iOS 15 minimum of the Capawesome plugin collection. Make sure the deployment target of your app is set to iOS 17.0 or later (in ios/App/App.xcodeproj and, if you use Swift Package Manager, in ios/App/CapApp-SPM/Package.swift).
The SDK is consumed from Microsoft's official GitHub repository: via Swift Package Manager it is resolved like any other package; via CocoaPods it is downloaded automatically during pod install (Microsoft does not publish a CocoaPods pod).
Info.plist
Add the IntuneMAMSettings dictionary to your ios/App/App/Info.plist file (the keys keep their legacy ADAL names for historical reasons):
<key>IntuneMAMSettings</key>
<dict>
<key>ADALAuthority</key>
<string>https://login.microsoftonline.com/YOUR_TENANT_ID</string>
<key>ADALClientId</key>
<string>YOUR_CLIENT_ID</string>
<key>ADALRedirectUri</key>
<string>msauth.YOUR_BUNDLE_ID://auth</string>
</dict>This plugin also uses these values to configure MSAL, so no separate MSAL configuration is needed.
Next, register the MSAL redirect URL scheme and the query schemes in the same file:
<key>CFBundleURLTypes</key>
<array>
<dict>
<key>CFBundleURLName</key>
<string>MSAL</string>
<key>CFBundleURLSchemes</key>
<array>
<string>msauth.YOUR_BUNDLE_ID</string>
</array>
</dict>
</array>
<key>LSApplicationQueriesSchemes</key>
<array>
<string>msauthv2</string>
<string>msauthv3</string>
<string>http-intunemam</string>
<string>https-intunemam</string>
</array>Make sure the redirect URI (msauth.YOUR_BUNDLE_ID://auth) is registered as an iOS/macOS platform redirect URI on your app registration in the Microsoft Entra admin center. The plugin handles the MSAL redirect automatically — no AppDelegate changes are required.
Finally, add a Face ID usage description if your app does not have one yet, since app protection policies may require biometric unlock:
<key>NSFaceIDUsageDescription</key>
<string>This app uses Face ID to secure corporate data.</string>Keychain Sharing
Enable the Keychain Sharing capability for your app target in Xcode and add the following keychain groups (in this order):
com.example.app(your bundle ID, usually already present)com.microsoft.intune.mamcom.microsoft.adalcache
IntuneMAMConfigurator
Microsoft ships the IntuneMAMConfigurator tool with the SDK repository. It applies the minimum required Info.plist changes for Intune management (including the -intunemam query scheme variants for every scheme your app queries) and is idempotent:
IntuneMAMConfigurator -i ios/App/App/Info.plist -e ios/App/App/App.entitlementsRunning it is recommended before you ship, especially if your app passes additional URL schemes to canOpenURL.
Automated Setup with Trapeze
Most of the host app changes above can be automated with Trapeze, which is also used by Capawesome Cloud. Save the following configuration as trapeze.yaml and run npx @trapezedev/configure run trapeze.yaml with the CLIENT_ID, TENANT_ID, PACKAGE_NAME, BUNDLE_ID and SIGNATURE_HASH variables set:
vars:
CLIENT_ID:
TENANT_ID:
PACKAGE_NAME:
BUNDLE_ID:
SIGNATURE_HASH:
platforms:
android:
gradle:
- file: build.gradle
target:
buildscript:
dependencies:
insert: |
classpath 'org.javassist:javassist:3.29.2-GA'
classpath 'com.microsoft.intune.mam.build:com.microsoft.intune.mam.build:12.4.0@jar'
- file: build.gradle
target:
buildscript:
repositories:
insert: |
ivy {
url 'https://raw.githubusercontent.com/microsoftconnect/ms-intune-app-sdk-android'
patternLayout { artifact '[revision]/GradlePlugin/[artifact].[ext]' }
metadataSources { artifact() }
content { includeGroup 'com.microsoft.intune.mam.build' }
}
- file: app/build.gradle
target:
insert: |
apply plugin: 'com.microsoft.intune.mam'
manifest:
- file: AndroidManifest.xml
target: manifest/application
attrs:
android:name: io.capawesome.capacitorjs.plugins.intune.IntuneApplication
android:enableOnBackInvokedCallback: 'false'
- file: AndroidManifest.xml
target: manifest/application
inject: |
<activity android:name="com.microsoft.identity.client.BrowserTabActivity" android:exported="true">
<intent-filter>
<action android:name="android.intent.action.VIEW" />
<category android:name="android.intent.category.DEFAULT" />
<category android:name="android.intent.category.BROWSABLE" />
<data android:scheme="msauth" android:host="$PACKAGE_NAME" android:path="/$SIGNATURE_HASH" />
</intent-filter>
</activity>
res:
- path: raw
file: auth_config.json
text: |
{
"client_id": "$CLIENT_ID",
"authorization_user_agent": "DEFAULT",
"redirect_uri": "msauth://$PACKAGE_NAME/$SIGNATURE_HASH",
"account_mode": "MULTIPLE",
"broker_redirect_uri_registered": true,
"client_capabilities": "protapp",
"authorities": [
{
"type": "AAD",
"audience": {
"type": "AzureADMultipleOrgs"
}
}
]
}
ios:
targets:
App:
buildSettings:
IPHONEOS_DEPLOYMENT_TARGET: '17.0'
entitlements:
- keychain-access-groups:
[
'$BUNDLE_ID',
'com.microsoft.intune.mam',
'com.microsoft.adalcache',
]
plist:
- entries:
- IntuneMAMSettings:
ADALAuthority: https://login.microsoftonline.com/$TENANT_ID
ADALClientId: $CLIENT_ID
ADALRedirectUri: msauth.$BUNDLE_ID://auth
- CFBundleURLTypes:
- CFBundleURLName: MSAL
CFBundleURLSchemes:
- msauth.$BUNDLE_ID
- LSApplicationQueriesSchemes:
- msauthv2
- msauthv3
- http-intunemam
- https-intunemam
- NSFaceIDUsageDescription: This app uses Face ID to secure corporate data.[!NOTE] Trapeze cannot edit
gradle.properties, so the Gradle Properties step must still be done manually. Review the result after running Trapeze: some list-valued Info.plist entries may be merged rather than replaced, and the Gradle insertions are not idempotent — run them only once.
Web
This plugin does not provide a web implementation. All methods reject with an unimplemented error on the web platform.
Configuration
No configuration required for this plugin.
Usage
The following examples show how to use the plugin.
Sign in and enroll an account
Acquire a token via MSAL and enroll the returned account in Mobile Application Management (MAM). The enrollment itself is asynchronous — listen for the enrollmentChange event to get the result:
import { Intune } from '@capawesome/capacitor-intune';
const signInAndEnroll = async () => {
await Intune.addListener('enrollmentChange', event => {
console.log('Enrollment status:', event.status);
});
const { accountId } = await Intune.acquireToken({
scopes: ['https://graph.microsoft.com/.default'],
});
await Intune.registerAndEnrollAccount({ accountId });
};Handle App Protection Conditional Access
If your organization requires an app protection policy via Conditional Access, Microsoft Entra ID only issues tokens once Intune manages the app. In this case, the token acquisition is rejected with the PROTECTION_POLICY_REQUIRED error code. Remediate the compliance and retry the token acquisition:
import { ErrorCode, Intune } from '@capawesome/capacitor-intune';
const acquireToken = async (scopes: string[]) => {
try {
return await Intune.acquireToken({ scopes });
} catch (error) {
if (error.code !== ErrorCode.ProtectionPolicyRequired) {
throw error;
}
const { status } = await Intune.remediateCompliance(error.data);
if (status !== 'compliant') {
throw error;
}
return Intune.acquireTokenSilent({ accountId: error.data.accountId, scopes });
}
};Read the app protection policy
Adapt your UI to the applied app protection policy:
import { Intune } from '@capawesome/capacitor-intune';
const applyPolicy = async () => {
const { account } = await Intune.getEnrolledAccount();
if (!account) {
return;
}
const policy = await Intune.getPolicy({ accountId: account.accountId });
if (!policy.saveToPersonalStorageAllowed) {
// Hide your export/download buttons.
}
};Protect files
On iOS, the Intune App SDK does not encrypt files on its own. Protect every file that contains organization data, and decrypt it before handing it to other plugins or native components:
import { Intune } from '@capawesome/capacitor-intune';
const protectFile = async (path: string) => {
const { account } = await Intune.getEnrolledAccount();
if (!account) {
return;
}
await Intune.protectFile({ path, accountId: account.accountId });
};
const decryptFile = async (path: string, destination: string) => {
const { encrypted } = await Intune.isFileEncrypted({ path });
if (encrypted) {
await Intune.decryptFile({ path, destination });
}
};Read the app configuration
Read the configuration values that the organization's IT administrator has deployed:
import { Intune } from '@capawesome/capacitor-intune';
const readAppConfig = async () => {
const { account } = await Intune.getEnrolledAccount();
if (!account) {
return;
}
const { values } = await Intune.getAppConfig({ accountId: account.accountId });
console.log('Server URL:', values['com.example.serverUrl']);
};Handle selective wipe
The Intune App SDK wipes the data it manages, but it does not wipe the web layer storage of your Capacitor app. Register the wipeRequested listener as early as possible and purge your web storage when it fires:
import { Intune } from '@capawesome/capacitor-intune';
const registerWipeListener = async () => {
await Intune.addListener('wipeRequested', async () => {
localStorage.clear();
sessionStorage.clear();
const databases = await indexedDB.databases();
for (const database of databases) {
if (database.name) {
indexedDB.deleteDatabase(database.name);
}
}
});
};API
acquireToken(...)acquireTokenSilent(...)decryptFile(...)getAppConfig(...)getEnrolledAccount()getPolicy(...)getSdkVersion()isFileEncrypted(...)loginAndEnrollAccount()protectFile(...)registerAndEnrollAccount(...)remediateCompliance(...)showDiagnosticConsole()unenrollAccount(...)addListener('appConfigChange', ...)addListener('enrollmentChange', ...)addListener('policyChange', ...)addListener('wipeRequested', ...)removeAllListeners()- Interfaces
- Type Aliases
acquireToken(...)
acquireToken(options: AcquireTokenOptions) => Promise<AcquireTokenResult>Acquire an access token interactively via the Microsoft Authentication Library (MSAL).
This presents the Microsoft sign-in UI if necessary. Use the returned
accountId to enroll the account via registerAndEnrollAccount(...).
If the tenant requires an app protection policy, the call is rejected
with the PROTECTION_POLICY_REQUIRED error code. Use
remediateCompliance(...) in this case.
Only available on Android and iOS.
| Param | Type |
| ------------- | ------------------------------------------------------------------- |
| options | AcquireTokenOptions |
Returns: Promise<AcquireTokenResult>
Since: 0.1.0
acquireTokenSilent(...)
acquireTokenSilent(options: AcquireTokenSilentOptions) => Promise<AcquireTokenResult>Acquire an access token silently via the Microsoft Authentication Library (MSAL) for an already signed-in account.
If the tenant requires an app protection policy, the call is rejected
with the PROTECTION_POLICY_REQUIRED error code. Use
remediateCompliance(...) in this case.
Only available on Android and iOS.
| Param | Type |
| ------------- | ------------------------------------------------------------------------------- |
| options | AcquireTokenSilentOptions |
Returns: Promise<AcquireTokenResult>
Since: 0.1.0
decryptFile(...)
decryptFile(options: DecryptFileOptions) => Promise<void>Decrypt a file that was encrypted by the Intune App SDK.
On iOS, encrypted files can only be read through the Intune App SDK. Call this method before handing a file to other consumers such as the Filesystem plugin, a media player or an uploader.
On Android, the app reads encrypted files transparently, so this method is rarely needed. The file is tagged with the unmanaged identity, which removes the encryption and takes the file out of the scope of a selective wipe.
Only available on Android and iOS.
| Param | Type |
| ------------- | ----------------------------------------------------------------- |
| options | DecryptFileOptions |
Since: 0.1.1
getAppConfig(...)
getAppConfig(options: GetAppConfigOptions) => Promise<GetAppConfigResult>Get the application configuration values that the organization's IT administrator has deployed for the given account via the MAM channel.
For configuration deployed via the MDM channel (device enrollment), use the Managed Configurations plugin instead.
Only available on Android and iOS.
| Param | Type |
| ------------- | ------------------------------------------------------------------- |
| options | GetAppConfigOptions |
Returns: Promise<GetAppConfigResult>
Since: 0.1.0
getEnrolledAccount()
getEnrolledAccount() => Promise<GetEnrolledAccountResult>Get the account that is currently enrolled in Mobile Application Management (MAM).
Only available on Android and iOS.
Returns: Promise<GetEnrolledAccountResult>
Since: 0.1.0
getPolicy(...)
getPolicy(options: GetPolicyOptions) => Promise<GetPolicyResult>Get the app protection policy that is currently applied for the given account.
Only available on Android and iOS.
| Param | Type |
| ------------- | ------------------------------------------------------------- |
| options | GetPolicyOptions |
Returns: Promise<GetPolicyResult>
Since: 0.1.0
getSdkVersion()
getSdkVersion() => Promise<GetSdkVersionResult>Get the versions of the Intune App SDK and the Microsoft Authentication Library (MSAL) that the plugin was built with.
Only available on Android and iOS.
Returns: Promise<GetSdkVersionResult>
Since: 0.1.0
isFileEncrypted(...)
isFileEncrypted(options: IsFileEncryptedOptions) => Promise<IsFileEncryptedResult>Check whether a file is encrypted by the Intune App SDK.
On Android, this reflects whether the file is tagged with a managed identity whose app protection policy uses file encryption, since the Intune App SDK for Android does not expose the encryption state of a single file.
Only available on Android and iOS.
| Param | Type |
| ------------- | ------------------------------------------------------------------------- |
| options | IsFileEncryptedOptions |
Returns: Promise<IsFileEncryptedResult>
Since: 0.1.1
loginAndEnrollAccount()
loginAndEnrollAccount() => Promise<void>Sign in and enroll an account using the login UI provided by the Intune App SDK.
On Android, use acquireToken(...) followed by
registerAndEnrollAccount(...) instead.
Only available on iOS.
Since: 0.1.0
protectFile(...)
protectFile(options: ProtectFileOptions) => Promise<void>Protect a file or directory for the given account.
On iOS, the Intune App SDK does not encrypt files on its own. Call this
method for every file that contains organization data if the app
protection policy requires file encryption (see
GetPolicyResult.fileEncryptionRequired). The file is encrypted in
place if the policy requires it. For a directory, all files it currently
contains are protected; files added later must be protected separately.
Encrypted files can only be read through the Intune App SDK, so use
decryptFile(...) before reading them with other plugins.
On Android, the Intune App SDK encrypts files automatically and the app reads them transparently. This method tags the file or directory with the account so that it is in the scope of a selective wipe. Files added to a protected directory later inherit the protection.
Only available on Android and iOS.
| Param | Type |
| ------------- | ----------------------------------------------------------------- |
| options | ProtectFileOptions |
Since: 0.1.1
registerAndEnrollAccount(...)
registerAndEnrollAccount(options: RegisterAndEnrollAccountOptions) => Promise<void>Register an account for Mobile Application Management (MAM) and enroll it in the Intune service.
Call this after a successful acquireToken(...) call. The enrollment
itself is asynchronous; listen for the enrollmentChange event to get
the enrollment result.
Only available on Android and iOS.
| Param | Type |
| ------------- | ------------------------------------------------------------------------------------------- |
| options | RegisterAndEnrollAccountOptions |
Since: 0.1.0
remediateCompliance(...)
remediateCompliance(options: RemediateComplianceOptions) => Promise<RemediateComplianceResult>Bring the app into compliance with the app protection policy of an account so that Microsoft Entra ID grants tokens for it.
Call this when acquireToken(...) or acquireTokenSilent(...) is
rejected with the PROTECTION_POLICY_REQUIRED error code, passing the
data of the error as options. The Intune App SDK registers and
enrolls the account as needed. If the returned status is compliant,
retry the token acquisition.
On iOS, the Intune App SDK may restart the app during the remediation if the account was not enrolled before. In that case, the promise is never settled and the app should retry the sign-in after the restart.
Only available on Android and iOS.
| Param | Type |
| ------------- | --------------------------------------------------------------------------------- |
| options | RemediateComplianceOptions |
Returns: Promise<RemediateComplianceResult>
Since: 0.1.2
showDiagnosticConsole()
showDiagnosticConsole() => Promise<void>Show the diagnostic console of the Intune App SDK.
The console allows the user to inspect the SDK state and collect logs for support requests.
Only available on Android and iOS.
Since: 0.1.0
unenrollAccount(...)
unenrollAccount(options: UnenrollAccountOptions) => Promise<void>Unenroll an account from Mobile Application Management (MAM) and unregister it from the Intune service.
Only available on Android and iOS.
| Param | Type |
| ------------- | ------------------------------------------------------------------------- |
| options | UnenrollAccountOptions |
Since: 0.1.0
addListener('appConfigChange', ...)
addListener(eventName: 'appConfigChange', listenerFunc: (event: AppConfigChangeEvent) => void) => Promise<PluginListenerHandle>Called when the application configuration changes.
Use getAppConfig(...) to read the new configuration values.
Only available on Android and iOS.
| Param | Type |
| ------------------ | ----------------------------------------------------------------------------------------- |
| eventName | 'appConfigChange' |
| listenerFunc | (event: AppConfigChangeEvent) => void |
Returns: Promise<PluginListenerHandle>
Since: 0.1.0
addListener('enrollmentChange', ...)
addListener(eventName: 'enrollmentChange', listenerFunc: (event: EnrollmentChangeEvent) => void) => Promise<PluginListenerHandle>Called when the enrollment state of an account changes, for example when an enrollment attempt succeeds or fails.
Only available on Android and iOS.
| Param | Type |
| ------------------ | ------------------------------------------------------------------------------------------- |
| eventName | 'enrollmentChange' |
| listenerFunc | (event: EnrollmentChangeEvent) => void |
Returns: Promise<PluginListenerHandle>
Since: 0.1.0
addListener('policyChange', ...)
addListener(eventName: 'policyChange', listenerFunc: (event: PolicyChangeEvent) => void) => Promise<PluginListenerHandle>Called when the app protection policy changes.
Use getPolicy(...) to read the new policy values.
Only available on Android and iOS.
| Param | Type |
| ------------------ | ----------------------------------------------------------------------------------- |
| eventName | 'policyChange' |
| listenerFunc | (event: PolicyChangeEvent) => void |
Returns: Promise<PluginListenerHandle>
Since: 0.1.0
addListener('wipeRequested', ...)
addListener(eventName: 'wipeRequested', listenerFunc: (event: WipeRequestedEvent) => void) => Promise<PluginListenerHandle>Called when the Intune service requests a selective wipe of the account's data.
The Intune App SDK wipes the data it manages, but it does not wipe the web layer storage of your Capacitor app (e.g. IndexedDB, Local Storage). Use this event to clean up any data your web code has persisted.
The event is delivered even if the wipe was requested while your app was not running (see the documentation for details).
Only available on Android and iOS.
| Param | Type |
| ------------------ | ------------------------------------------------------------------------------------- |
| eventName | 'wipeRequested' |
| listenerFunc | (event: WipeRequestedEvent) => void |
Returns: Promise<PluginListenerHandle>
Since: 0.1.0
removeAllListeners()
removeAllListeners() => Promise<void>Remove all listeners for this plugin.
Since: 0.1.0
Interfaces
AcquireTokenResult
| Prop | Type | Description | Since |
| ----------------- | --------------------------- | ----------------------------------------------------------------------------------------------------------------------- | ----- |
| accessToken | string | The acquired access token. | 0.1.0 |
| accountId | string | The Microsoft Entra object ID (OID) of the signed-in account. Use this identifier for all other methods of this plugin. | 0.1.0 |
| idToken | string | null | The raw ID token of the signed-in account, if available. | 0.1.0 |
| tenantId | string | null | The Microsoft Entra tenant ID of the signed-in account, if available. | 0.1.0 |
| username | string | null | The username (usually the UPN) of the signed-in account, if available. | 0.1.0 |
AcquireTokenOptions
| Prop | Type | Description | Default | Since |
| ----------------- | --------------------- | ------------------------------------------------------------------------------------------------------ | ------------------ | ----- |
| forcePrompt | boolean | Whether or not to force the account selection prompt to be shown, even if a user is already signed in. | false | 0.1.0 |
| loginHint | string | The username to pre-fill in the sign-in UI. | | 0.1.0 |
| scopes | string[] | The scopes to request the access token for. | | 0.1.0 |
AcquireTokenSilentOptions
| Prop | Type | Description | Default | Since |
| ------------------ | --------------------- | ---------------------------------------------------------------------------- | ------------------ | ----- |
| accountId | string | The Microsoft Entra object ID (OID) of the account to acquire the token for. | | 0.1.0 |
| forceRefresh | boolean | Whether or not to ignore any cached token and force a token refresh. | false | 0.1.0 |
| scopes | string[] | The scopes to request the access token for. | | 0.1.0 |
DecryptFileOptions
| Prop | Type | Description | Since |
| ----------------- | ------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ----- |
| destination | string | The absolute path or file:// URI to write the decrypted copy to. If not provided, the file is decrypted in place. An existing file at the destination is overwritten. | 0.1.1 |
| path | string | The absolute path or file:// URI of the encrypted file. | 0.1.1 |
GetAppConfigResult
| Prop | Type | Description | Since |
| --------------- | ----------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------- | ----- |
| conflicts | AppConfigConflict[] | The configuration keys for which multiple conflicting values have been deployed. | 0.1.0 |
| values | Record<string, string> | The merged application configuration values. For keys with conflicting values, the value that the Intune App SDK returns first is used. | 0.1.0 |
AppConfigConflict
| Prop | Type | Description | Since |
| ------------ | --------------------- | ------------------------------------------------------------- | ----- |
| key | string | The configuration key for which conflicting values exist. | 0.1.0 |
| values | string[] | All values that have been deployed for the configuration key. | 0.1.0 |
GetAppConfigOptions
| Prop | Type | Description | Since |
| --------------- | ------------------- | -------------------------------------------------------------------------------------------- | ----- |
| accountId | string | The Microsoft Entra object ID (OID) of the account to get the application configuration for. | 0.1.0 |
GetEnrolledAccountResult
| Prop | Type | Description | Since |
| ------------- | ------------------------------------------------------------------- | --------------------------------------------------------- | ----- |
| account | EnrolledAccount | null | The enrolled account or null if no account is enrolled. | 0.1.0 |
EnrolledAccount
| Prop | Type | Description | Since |
| --------------- | --------------------------- | --------------------------------------------------------------------- | ----- |
| accountId | string | The Microsoft Entra object ID (OID) of the enrolled account. | 0.1.0 |
| username | string | null | The username (usually the UPN) of the enrolled account, if available. | 0.1.0 |
GetPolicyResult
| Prop | Type | Description | Since |
| ---------------------------------- | -------------------- | -------------------------------------------------------------------------------------------------------------------------------- | ----- |
| contactSyncAllowed | boolean | Whether or not the policy allows syncing contacts to the device. | 0.1.0 |
| fileEncryptionRequired | boolean | Whether or not the policy requires files to be encrypted. On Android, this reflects whether file encryption is currently in use. | 0.1.0 |
| managedBrowserRequired | boolean | Whether or not the policy requires links to be opened in a managed browser (e.g. Microsoft Edge). | 0.1.0 |
| pinRequired | boolean | Whether or not the policy requires a PIN to access the app. | 0.1.0 |
| saveToPersonalStorageAllowed | boolean | Whether or not the policy allows saving files to personal (local) storage. | 0.1.0 |
| screenCaptureAllowed | boolean | Whether or not the policy allows taking screenshots. | 0.1.0 |
GetPolicyOptions
| Prop | Type | Description | Since |
| --------------- | ------------------- | ---------------------------------------------------------------------------------------- | ----- |
| accountId | string | The Microsoft Entra object ID (OID) of the account to get the app protection policy for. | 0.1.0 |
GetSdkVersionResult
| Prop | Type | Description | Since |
| ---------------------- | --------------------------- | ------------------------------------------------------------------------- | ----- |
| intuneSdkVersion | string | The version of the Intune App SDK. | 0.1.0 |
| msalVersion | string | null | The version of the Microsoft Authentication Library (MSAL), if available. | 0.1.0 |
IsFileEncryptedResult
| Prop | Type | Description | Since |
| --------------- | -------------------- | ----------------------------------------------------------- | ----- |
| encrypted | boolean | Whether or not the file is encrypted by the Intune App SDK. | 0.1.1 |
IsFileEncryptedOptions
| Prop | Type | Description | Since |
| ---------- | ------------------- | -------------------------------------------------------- | ----- |
| path | string | The absolute path or file:// URI of the file to check. | 0.1.1 |
ProtectFileOptions
| Prop | Type | Description | Since |
| --------------- | ------------------- | ----------------------------------------------------------------------- | ----- |
| accountId | string | The Microsoft Entra object ID (OID) of the account that owns the file. | 0.1.1 |
| path | string | The absolute path or file:// URI of the file or directory to protect. | 0.1.1 |
RegisterAndEnrollAccountOptions
| Prop | Type | Description | Since |
| --------------- | ------------------- | -------------------------------------------------------------------------------------------------------------- | ----- |
| accountId | string | The Microsoft Entra object ID (OID) of the account to register and enroll, as returned by acquireToken(...). | 0.1.0 |
RemediateComplianceResult
| Prop | Type | Description | Since |
| ------------------ | ------------------------------------------------------------- | ---------------------------------------------------------------------------------------------------------- | ----- |
| errorMessage | string | null | A localized error message that can be displayed to the user if the account is not compliant, if available. | 0.1.2 |
| errorTitle | string | null | A localized error title that can be displayed to the user if the account is not compliant, if available. | 0.1.2 |
| status | ComplianceStatus | The compliance status of the account after the remediation. | 0.1.2 |
RemediateComplianceOptions
| Prop | Type | Description | Default | Since |
| --------------- | -------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------ | ----- |
| accountId | string | The Microsoft Entra object ID (OID) of the account to remediate. | | 0.1.2 |
| authority | string | The authority URL of the account. Only available on Android. Required on Android. | | 0.1.2 |
| silent | boolean | Whether or not to remediate without showing any UI of the Intune App SDK. On iOS, the status interactionRequired is returned if the remediation cannot be completed without user interaction. | false | 0.1.2 |
| tenantId | string | The Microsoft Entra tenant ID of the account. Only available on Android. Required on Android. | | 0.1.2 |
| username | string | The username (usually the UPN) of the account. Only available on Android. Required on Android. | | 0.1.2 |
UnenrollAccountOptions
| Prop | Type | Description | Default | Since |
| --------------- | -------------------- | ----------------------------------------------------------------------------------------------------- | ------------------ | ----- |
| accountId | string | The Microsoft Entra object ID (OID) of the account to unenroll. | | 0.1.0 |
| wipe | boolean | Whether or not the account's data should be wiped as part of the unenrollment. Only available on iOS. | false | 0.1.0 |
PluginListenerHandle
| Prop | Type |
| ------------ | ----------------------------------------- |
| remove | () => Promise<void> |
AppConfigChangeEvent
| Prop | Type | Description | Since |
| --------------- | --------------------------- | -------------------------------------------------------------------------- | ----- |
| accountId | string | null | The Microsoft Entra object ID (OID) of the affected account, if available. | 0.1.0 |
EnrollmentChangeEvent
| Prop | Type | Description | Since |
| --------------- | ------------------------------------------------------------- | -------------------------------------------------------------------------- | ----- |
| accountId | string | null | The Microsoft Entra object ID (OID) of the affected account, if available. | 0.1.0 |
| status | EnrollmentStatus | The new enrollment status of the account. | 0.1.0 |
PolicyChangeEvent
| Prop | Type | Description | Since |
| --------------- | --------------------------- | -------------------------------------------------------------------------- | ----- |
| accountId | string | null | The Microsoft Entra object ID (OID) of the affected account, if available. | 0.1.0 |
WipeRequestedEvent
| Prop | Type | Description | Since |
| --------------- | --------------------------- | -------------------------------------------------------------------------- | ----- |
| accountId | string | null | The Microsoft Entra object ID (OID) of the affected account, if available. | 0.1.0 |
Type Aliases
ComplianceStatus
The compliance status of an account.
canceled: The user canceled the remediation. Only available on iOS.clientError: The remediation failed due to a client issue, such as a missing or invalid token. Only available on Android.companyPortalRequired: The Company Portal app must be installed. If it is already installed, the app must be restarted. Only available on Android.compliant: The account is compliant. Retry the token acquisition.interactionRequired: The remediation requires user interaction. CallremediateCompliance(...)again withsilentset tofalse. Only available on iOS.networkFailure: The Intune service could not be reached. Retry when the network connection is restored.notCompliant: The account is not compliant.pending: The Intune service did not respond in time. Retry later. Only available on Android.serviceFailure: The compliance data could not be retrieved from the Intune service. Retry later.unknown: The status is unknown. Only available on Android.
'canceled' | 'clientError' | 'companyPortalRequired' | 'compliant' | 'interactionRequired' | 'networkFailure' | 'notCompliant' | 'pending' | 'serviceFailure' | 'unknown'
EnrollmentStatus
The enrollment status of an account.
'enrolled' | 'failed' | 'pending' | 'unenrolled'
Platform Support
Not every feature is available on all platforms. The following table lists the notable per-platform differences of the plugin's API:
| Method / Option | Android | iOS | Web |
| ------------------------------------- | :-----: | :-: | :-: |
| loginAndEnrollAccount() | ❌ | ✅ | ❌ |
| unenrollAccount(...) (wipe option) | ❌ | ✅ | ❌ |
Additional notes:
- On Android, use
acquireToken(...)followed byregisterAndEnrollAccount(...)instead ofloginAndEnrollAccount(). The Intune App SDK for Android does not provide its own login UI. - On Android,
remediateCompliance(...)requires theauthority,tenantIdandusernameoptions. All of them are included in thedataof thePROTECTION_POLICY_REQUIREDerror. - On Android,
fileEncryptionRequiredreflects whether file encryption is currently in use by the Intune App SDK, which is the closest equivalent the SDK exposes. - The
wipeRequestedevent is persisted and replayed on the next app launch if no listener was registered when the wipe arrived. In rare cases the event may be delivered more than once, so make sure your wipe handler is idempotent. - On iOS, the Intune App SDK does not encrypt files on its own. Call
protectFile(...)for every file that contains organization data. On Android, file encryption is automatic andprotectFile(...)only tags the file with the account so that it is in the scope of a selective wipe. - On iOS, encrypted files can only be read through the Intune App SDK. Reading them with the Filesystem plugin or handing them to other native plugins (e.g. media players or uploaders) yields the encrypted content. Call
decryptFile(...)first. On Android, the app reads encrypted files transparently, sodecryptFile(...)is rarely needed. - On Android,
isFileEncrypted(...)reflects whether the file is tagged with a managed identity whose policy uses file encryption, since the Intune App SDK does not expose the encryption state of a single file. - Policy enforcement (PIN, copy/paste and screenshot restrictions, etc.) is performed automatically by the Intune App SDK once the native integration is in place. The JavaScript API exists for the parts that enforcement cannot do: enrolling accounts, reading configuration, adapting your UI to the policy, protecting files on iOS, and cleaning up web storage on selective wipe.
Choosing between the MAM and MDM channel
Organizations can deliver app configuration through two different channels, and administrators frequently mix them up. This plugin covers the MAM channel; the Managed Configurations plugin covers the MDM channel:
| | MAM channel (this plugin) | MDM channel (Managed Configurations) |
| -------------------------- | ----------------------------------------------- | --------------------------------------------------------- |
| Device enrollment required | No | Yes |
| Delivered via | Intune App SDK (Intune service) | RestrictionsManager / com.apple.configuration.managed |
| Targeted at | The signed-in account (identity) | The device |
| EMM vendor | Microsoft Intune only | Any EMM/MDM vendor |
| Typical scenario | BYOD / app protection without device management | Corporate-owned, fully managed devices |
If your organization deploys the app configuration policy with "Managed apps" as the delivery channel in the Intune admin center, use this plugin. If it is deployed with "Managed devices", use the Managed Configurations plugin. Apps that must support both scenarios should read both channels.
Testing
Exercising Mobile Application Management (MAM) requires infrastructure that cannot be simulated locally:
- A Microsoft Intune tenant with Intune licenses and a licensed test user.
- An app protection policy and (optionally) an app configuration policy targeted at the test user and your app.
- On Android, the Company Portal app installed on the test device.
Without a tenant, the plugin compiles and loads, but enrollment fails with AccountNotLicensed-style errors and no policy is applied. Building this plugin (e.g. via npm run verify) only proves that the code compiles — it does not exercise any MAM functionality.
FAQ
How is this plugin different from other similar plugins?
It integrates the Microsoft Intune App SDK for Mobile Application Management on Android and iOS through a fully typed API, enforcing app protection policies and exposing them as typed booleans so you can adapt your UI, acquiring tokens via MSAL, and enrolling accounts without device enrollment. It emits selective-wipe events so you can purge the web-layer storage (such as IndexedDB and Local Storage) that the SDK cannot reach itself, and it is built against current Intune and MSAL versions, which matters because Microsoft blocks apps that ship outdated SDKs. It is a free, actively maintained plugin that stays current with the latest Capacitor version.
Do I need a Microsoft Intune tenant to use this plugin?
Yes. This plugin wraps the Microsoft Intune App SDK, which requires a Microsoft Intune tenant, Intune licenses, and an app registration in Microsoft Entra ID (see Testing).
Why does the plugin require iOS 17?
The current Microsoft Intune App SDK for iOS requires iOS 17 or later. Microsoft actively blocks apps built with outdated Intune App SDK versions, so this plugin always tracks Microsoft's current SDK line instead of pinning an older one.
Does this plugin enforce the app protection policies?
Mostly, yes. Once the native integration (MAM build plugin on Android, SDK integration on iOS) is in place, the Intune App SDK enforces PIN, data transfer and screenshot restrictions itself. File encryption is automatic on Android, but on iOS the app must protect its files via protectFile(...). The JavaScript API of this plugin is for the parts enforcement cannot do: enrollment, introspection, file protection on iOS, and cleaning up web storage on selective wipe.
What is the difference between this plugin and the Managed Configurations plugin?
This plugin reads the MAM channel (Intune App SDK, no device enrollment required). The Managed Configurations plugin reads the MDM channel (device enrollment required, works with any EMM vendor). See Choosing between the MAM and MDM channel.
Can I use this plugin with Ionic, React, Vue or Angular?
Yes, the plugin is framework-agnostic. It works in any Capacitor app regardless of the web framework, including Ionic with Angular, React, or Vue, as well as plain JavaScript projects.
Related Plugins
- App Integrity: Verify app and device integrity using the Play Integrity API and App Attest.
- Managed Configurations: Access managed configuration settings deployed by an MDM channel, the counterpart to Intune's MAM channel.
- Root Detection: Detect rooted and jailbroken devices.
Newsletter
Stay up to date with the latest news and updates about the Capawesome, Capacitor, and Ionic ecosystem by subscribing to our Capawesome Newsletter.
Changelog
See CHANGELOG.md.
License
See LICENSE.
Third-Party Notices
The Microsoft Intune App SDKs for Android and iOS are proprietary software, licensed by Microsoft under the Microsoft License Terms Intune App SDK. This plugin does not bundle or modify the Intune App SDKs — it declares them as dependencies and downloads them from Microsoft's official repositories at build or install time. By building an app with this plugin, you accept Microsoft's license terms for the Intune App SDK. The Microsoft Authentication Library (MSAL) is licensed under the MIT license. Using the plugin requires a Microsoft Intune tenant with appropriate licenses. The MIT license of this plugin covers the wrapper code only, not the Microsoft SDKs.
Note that Microsoft's license terms also place obligations on your app as the distributor of the Intune App SDK binaries — among other things regarding your app's own functionality and copyright notice, license compatibility, and indemnification. Review the license terms linked above before shipping your app.
[^1]: This project is not affiliated with, endorsed by, sponsored by, or approved by Microsoft Corporation or any of its
