expo-mdm
v0.1.11
Published
An expo module to access Managed Device Management (MDM) features on iOS and Android.
Downloads
164
Maintainers
Readme
expo-mdm
A React Native library for integrating Mobile Device Management (MDM) capabilities into Expo applications.
Overview
This project has been created with the intention of integrating MDM functionality for React Native applications. While there is already a project called react-native-mdm that provides similar functionality, this library aims to provide better Expo compatibility and modern implementation.
Platform Support
- ✅ Android - Fully implemented
- ✅ iOS - Fully implemented
Both platforms support MDM configuration reading and app locking features.
Key Platform Differences
| Feature | Android | iOS |
|---------|---------|-----|
| Build-time Config | ✅ Required (app_restrictions.xml) | ❌ Not needed |
| Config Source | App defines schema, MDM sets values | MDM defines schema and sets values |
| Config Location | RestrictionsManager API | UserDefaults (com.apple.configuration.managed) |
| Info.plist | Not used | Not used |
| Plugin Setup | Required | Optional (no-op) |
Getting Started
Installation
npm install expo-mdm
# or
yarn add expo-mdmConfiguration
Android Configuration (Required)
Android requires build-time configuration to generate the app_restrictions.xml file that MDM providers use.
Add the expo-mdm plugin to your app.json or app.config.js:
{
"expo": {
"plugins": [
[
"expo-mdm",
{
"android": {
"QueryPackages": [
"com.azure.authenticator",
"UserDetailsClient.Droid",
"com.microsoft.windowsintune.companyportal"
],
"AppRestrictionsMap": {
"apiUrl": {
"title": "API URL",
"description": "The URL of the API server.",
"type": "string",
"defaultValue": "https://api.example.com"
},
"enableAnalytics": {
"title": "Enable Analytics",
"description": "Whether to enable analytics.",
"type": "bool",
"defaultValue": false
},
"maxRetries": {
"title": "Max Retries",
"description": "Maximum number of retry attempts",
"type": "integer",
"defaultValue": 3
}
}
}
}
]
]
}
}iOS Configuration (None Required)
iOS does NOT require any build-time configuration.
Unlike Android, iOS MDM works entirely at runtime:
- 🚫 No Info.plist entries needed
- 🚫 No build-time configuration files
- ✅ MDM providers (Intune, Jamf, Workspace ONE, etc.) define the configuration schema in their admin consoles
- ✅ Configuration is pushed directly to the device and stored in
UserDefaultsatcom.apple.configuration.managed - ✅ Your app simply reads the configuration at runtime using
getConfiguration()
Example: If you want to configure apiUrl and enableAnalytics for iOS:
- Log into your MDM provider's admin console (e.g., Microsoft Intune)
- Create an "App Configuration Policy" for your iOS app
- Add key-value pairs:
- Key:
apiUrl, Value:https://api.example.com - Key:
enableAnalytics, Value:true
- Key:
- Deploy the policy to your test devices
- Your app will automatically read these values via
getConfiguration()
Android Plugin Configuration Options
QueryPackages (Android Only)
The QueryPackages array specifies which package names your app can query for. This is essential for MDM functionality as it allows your app to detect and interact with specific MDM-related applications on Android.
Supported packages:
com.azure.authenticator- Microsoft Authenticator appUserDetailsClient.Droid- User details client for Androidcom.microsoft.windowsintune.companyportal- Microsoft Intune Company Portal
Example:
"QueryPackages": [
"com.azure.authenticator",
"com.microsoft.windowsintune.companyportal"
]AppRestrictionsMap (Android Only)
The AppRestrictionsMap defines the managed app configuration schema that MDM administrators will see when configuring your app. This generates the app_restrictions.xml file that Android MDM providers use. Each restriction has the following properties:
title- Display name for the restrictiondescription- Description of what the restriction doestype- Data type (string,bool,integer,choice,multi-select)defaultValue- Default value if not set by MDM
Supported types:
string- Text inputbool- Boolean (true/false)integer- Numeric inputchoice- Single selection from predefined optionsmulti-select- Multiple selections from predefined options
Example configurations:
"AppRestrictionsMap": {
"serverUrl": {
"title": "Server URL",
"description": "The URL of the backend server",
"type": "string",
"defaultValue": "https://api.company.com"
},
"debugMode": {
"title": "Debug Mode",
"description": "Enable debug logging",
"type": "bool",
"defaultValue": false
},
"maxRetries": {
"title": "Max Retries",
"description": "Maximum number of retry attempts",
"type": "integer",
"defaultValue": 3
}
}Testing
Testing MDM functionality requires different approaches for each platform. Below are detailed instructions for both Android and iOS.
Testing on Android
The easiest way to test MDM functionality on Android is to use TestDPC (Test Device Policy Controller), a free testing tool provided by Google.
Prerequisites
- Profile Support: You can use TestDPC with either work profile or personal profile
- Device Admin Setup: When setting TestDPC as device admin, ensure you have no Google accounts signed in if you're using your personal device
- Development Environment: Make sure you have Android development environment properly configured
Setup Steps
- Install TestDPC from Google Play Store
- Set up Device Admin:
- Open TestDPC
- Follow the device admin setup wizard
- Grant all necessary permissions
- Configure your test policies
- Install your app (either via ADB or as a managed app)
Testing App Restrictions
- Open TestDPC
- Navigate to "Manage app restrictions"
- Find your app in the list
- Configure the restrictions defined in your
AppRestrictionsMap:- Set values for
apiUrl,enableAnalytics, etc. - These values will be immediately available to your app
- Set values for
- Test in your app:
import { getConfiguration, isSupported } from 'expo-mdm'; const supported = await isSupported(); console.log('MDM Supported:', supported); const config = await getConfiguration(); console.log('MDM Config:', config); // Output: { apiUrl: "https://...", enableAnalytics: true }
Testing App Lock Mode (Kiosk Mode)
- In TestDPC, navigate to "Device Policy Management" → "Lock task mode"
- Add your app package name to the lock task whitelist
- In your app, test the lock functions:
import { lockApp, unlockApp, isAppLockingAllowed, isAppLocked } from 'expo-mdm'; const canLock = await isAppLockingAllowed(); if (canLock) { await lockApp(); // Enters kiosk mode // User cannot exit app or access other apps await unlockApp(); // Exits kiosk mode }
Alternative Android Testing with Microsoft Intune
For enterprise testing:
- Enroll device in Microsoft Intune
- Create app configuration policy in Intune admin center
- Deploy configuration to test devices
- Test with production MDM configuration
Testing on iOS
iOS MDM testing requires an Apple MDM solution. Here are several options:
Option 1: Microsoft Intune (Recommended for Development)
Set up Intune trial account:
- Sign up for a free Microsoft 365 trial
- Enable Intune in the admin center
Enroll your iOS device:
- Install Microsoft Intune Company Portal from App Store
- Sign in with your test account
- Follow enrollment instructions
Configure App Configuration Policy:
- In Intune admin center, navigate to Apps → App configuration policies
- Create a new policy for iOS/iPadOS managed apps
- Select your app
- Add configuration keys matching your
AppRestrictionsMap:<key>apiUrl</key> <string>https://api.example.com</string> <key>enableAnalytics</key> <true/> - Assign to test users/devices
Test in your app:
import { getConfiguration, isSupported } from 'expo-mdm'; const supported = await isSupported(); console.log('MDM Supported:', supported); const config = await getConfiguration(); console.log('MDM Config:', config); // Output: { apiUrl: "https://...", enableAnalytics: 1 }
Option 2: Apple Business Manager + MDM
For production testing:
- Enroll in Apple Business Manager
- Connect an MDM solution (Jamf, Workspace ONE, etc.)
- Create managed app configuration
- Deploy to test devices
Option 3: Manual Testing with Xcode (Development Only)
For quick local testing without MDM enrollment:
In your iOS project, add managed configuration to your scheme:
- Edit Scheme → Run → Arguments
- Add to Environment Variables:
com.apple.configuration.managed = {"apiUrl":"https://test.com","enableAnalytics":true}
Or manually set UserDefaults in your app (for development only):
// Development testing only - this will be replaced by actual MDM in production import { NativeModules } from 'react-native'; if (__DEV__) { // This simulates MDM configuration for testing NativeModules.UserDefaults?.setObject( { apiUrl: "https://test.com", enableAnalytics: true }, "com.apple.configuration.managed" ); }
Testing iOS Guided Access (App Lock Mode)
iOS uses Guided Access instead of a programmatic lock mode. To test:
Enable Guided Access:
- Go to Settings → Accessibility → Guided Access
- Toggle on and set a passcode
Configure Triple Click:
- Settings → Accessibility → Accessibility Shortcut
- Select Guided Access
Test in your app:
import { isAppLockingAllowed, isAppLocked } from 'expo-mdm'; const guidedAccessEnabled = await isAppLockingAllowed(); console.log('Guided Access Enabled:', guidedAccessEnabled); const inGuidedAccess = await isAppLocked(); console.log('Currently in Guided Access:', inGuidedAccess);Activate Guided Access:
- Triple-click the side/home button while in your app
- Or go to Settings → Guided Access → Start
Note: iOS does not allow programmatic entry/exit of Guided Access for security reasons. The lockApp() and unlockApp() methods will return false on iOS. Users must manually enable/disable Guided Access.
Testing Event Listeners
Both platforms support listening to MDM configuration changes:
import { addEventListener } from 'expo-mdm';
// Listen for configuration changes
const subscription = addEventListener('onManagedAppConfigChange', (event) => {
console.log('MDM config changed:', event.config);
});
// Listen for lock status changes
const lockSubscription = addEventListener('onAppLockStatusChange', (event) => {
console.log('Lock status changed:', event.isLocked);
});
// Clean up
subscription.remove();
lockSubscription.remove();Contributing
We welcome contributions! Please feel free to submit issues, feature requests, or pull requests.
Areas where we need help:
- Documentation improvements
- Additional features for both platforms
- Testing and bug reports
- Integration examples with different MDM providers
License
This project is licensed under the MIT License - see the LICENSE file for details.
Support
If you encounter any issues or have questions, please file an issue on our GitHub repository.
