Implementing Permissions System for Mini-Programs in Super App
When a mini-program inside Super App wants to read geolocation or send a notification — who asks the user for permission? The host app might already have it. The mini-program shouldn't have direct access. Between them must be a permission broker.
Two Permission Layers
The permissions system for mini-programs isn't just a wrapper over system ActivityCompat.requestPermissions. There are two independent layers:
First layer: platform permissions — same camera, location, contacts any Android/iOS app requests. Host app keeps them and delegates only what's explicitly allowed to mini-program.
Second layer: platform API permissions — access to Super App's own APIs: user profile storage, order history, payment methods, internal ecosystem contacts. This is completely custom layer, system permissions don't help here.
Mini-Program Manifest
Each mini-program ships with a manifest declaring needed permissions:
{
"miniAppId": "com.partner.food_delivery",
"version": "1.2.0",
"permissions": {
"system": ["LOCATION_FINE", "CAMERA"],
"platform": ["USER_PROFILE_READ", "PAYMENT_INITIATE", "ORDER_HISTORY_READ"]
},
"permissionRationale": {
"LOCATION_FINE": "To calculate delivery address",
"CAMERA": "To scan menu QR codes"
}
}
On mini-program install, user sees list of requested permissions — like when installing a regular Android app. Permissions not declared in manifest are unavailable even if host has them.
Permission Broker on Host Side
Central component — broker checking all calls to native APIs:
class MiniAppPermissionBroker(
private val permissionStore: MiniAppPermissionStore,
private val systemPermissionDelegate: SystemPermissionDelegate
) {
suspend fun requestPermission(
miniAppId: String,
permission: MiniAppPermission,
context: Activity
): PermissionResult {
// 1. Declared in manifest?
if (!manifestValidator.isDeclared(miniAppId, permission)) {
return PermissionResult.DENIED_NOT_DECLARED
}
// 2. Already granted?
val stored = permissionStore.getStatus(miniAppId, permission)
if (stored == PermissionStatus.GRANTED) return PermissionResult.GRANTED
if (stored == PermissionStatus.DENIED_PERMANENTLY) return PermissionResult.DENIED_PERMANENTLY
// 3. For system permissions — check host, then request
if (permission.isSystemPermission()) {
val hostHas = systemPermissionDelegate.hasPermission(permission.androidName)
if (!hostHas) {
// Request from user on behalf of host
val result = systemPermissionDelegate.request(permission.androidName, context)
if (result != GRANTED) return PermissionResult.DENIED_BY_USER
}
}
// 4. Show platform permission dialog
val userDecision = showPermissionDialog(miniAppId, permission, context)
permissionStore.save(miniAppId, permission, userDecision)
return userDecision
}
}
User Permission Management
User must be able to revoke any permission anytime. In Super App settings — screen with installed mini-programs and their permissions:
Mini-program: "Food Delivery"
├── Geolocation (precise) ............ ON [toggle]
├── Camera .......................... OFF [toggle]
├── User profile .................... ON [toggle]
└── Order history ................... ON [toggle]
Revoking permission works immediately — no mini-program restart. Broker on next API call returns PERMISSION_REVOKED, and mini-program must handle this error gracefully.
Runtime Check on Every API Call
Permissions can be revoked asynchronously while mini-program works. So every platform API call goes through broker, not just on init:
// Call from JS bridge
@JavascriptInterface
fun getUserLocation(callbackId: String) {
val miniAppId = currentMiniAppContext.id
coroutineScope.launch {
when (permissionBroker.checkPermission(miniAppId, MiniAppPermission.LOCATION_FINE)) {
PermissionResult.GRANTED -> {
val location = locationProvider.getLastLocation()
bridge.sendSuccess(callbackId, location.toJson())
}
PermissionResult.DENIED_PERMANENTLY -> {
bridge.sendError(callbackId, "PERMISSION_DENIED_PERMANENTLY")
}
else -> {
bridge.sendError(callbackId, "PERMISSION_REQUIRED")
}
}
}
}
Audit of Permission Usage
Every call to sensitive API is logged: timestamp, miniAppId, permission, granted or denied. Lets you detect a mini-program requesting geolocation every 5 seconds in background — and block it on platform.
Timeline
Permissions system with two layers, settings UI and audit trail: 2–3 days with ready permission store. Built from scratch including manifest validator and management screens — 4–6 days.







