Реалізація системи розрішень для мініпрограм у Super App
Коли мініпрограма всередині Super App хоче прочитати геолокацію або відправити сповіщення — хто повинен спитати дозвіл у користувача? Хост-додаток вже може його мати. Мініпрограма не повинна мати прямий доступ. Між ними потрібен permission broker.
Два шари розрішень
Система розрішень для мініпрограм — це не просто обʼєрнення над системним ActivityCompat.requestPermissions. Тут два незалежних шари:
Перший шар: платформенні розрішення — ті ж camera, location, contacts, що запитує будь-який Android/iOS додаток. Хост-додаток тримає їх у себе та делегує мініпрограмі тільки те, що явно дозволено.
Другий шар: платформенні API розрішення — доступ до API самої Super App: сховище профілю користувача, історія заказів, платіжні методи, контакти всередину екосистеми. Це повністю користувацький шар, системні розрішення тут не допомагають.
Маніфест мініпрограми
Кожна мініпрограма поставляється з маніфестом, де декларує потрібні 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": "Для розрахунку адреси доставки",
"CAMERA": "Для сканування QR-кодів меню"
}
}
При встановленні мініпрограми користувач видить список запрошуваних розрішень — як при встановленні звичайного Android-додатку. Розрішення, не задекларовані в маніфесті, недоступні навіть якщо хост їх має.
Permission Broker на боці хоста
Центральний компонент — broker, який перевіряє всі звернення до нативних API:
class MiniAppPermissionBroker(
private val permissionStore: MiniAppPermissionStore,
private val systemPermissionDelegate: SystemPermissionDelegate
) {
suspend fun requestPermission(
miniAppId: String,
permission: MiniAppPermission,
context: Activity
): PermissionResult {
// 1. Задекларовано у маніфесті?
if (!manifestValidator.isDeclared(miniAppId, permission)) {
return PermissionResult.DENIED_NOT_DECLARED
}
// 2. Вже видано?
val stored = permissionStore.getStatus(miniAppId, permission)
if (stored == PermissionStatus.GRANTED) return PermissionResult.GRANTED
if (stored == PermissionStatus.DENIED_PERMANENTLY) return PermissionResult.DENIED_PERMANENTLY
// 3. Для системних розрішень — перевіряємо хост, потім запитуємо
if (permission.isSystemPermission()) {
val hostHas = systemPermissionDelegate.hasPermission(permission.androidName)
if (!hostHas) {
// Запитуємо у користувача від імені хоста
val result = systemPermissionDelegate.request(permission.androidName, context)
if (result != GRANTED) return PermissionResult.DENIED_BY_USER
}
}
// 4. Показуємо платформенний діалог розрішення
val userDecision = showPermissionDialog(miniAppId, permission, context)
permissionStore.save(miniAppId, permission, userDecision)
return userDecision
}
}
Управління розрішеннями користувачем
Користувач має можливість відозвати будь-яке розрішення у будь-який момент. У настройках Super App — екран із переліком встановлених мініпрограм та їх розрішень:
Мініпрограма: "Доставка їжі"
├── Геолокація (точна) ............. ВКЛ [перемикач]
├── Камера .......................... ВИК [перемикач]
├── Профіль користувача ............ ВКЛ [перемикач]
└── Історія заказів ................. ВКЛ [перемикач]
Відозвання розрішення працює відразу — без перезапуску мініпрограми. Broker при наступному вызові API повернеме PERMISSION_REVOKED, і мініпрограма повинна коректно обробити цю помилку.
Runtime перевірка при кожному виклику API
Розрішення можуть бути відозвані асинхронно — поки мініпрограма працює. Тому кожен виклик платформенного API проходить через broker, а не тільки при ініціалізації:
// Виклик з JS-моста
@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")
}
}
}
}
Аудит використання розрішень
Кожен звернення до чутливого API логується: timestamp, miniAppId, permission, було ли granted або denied. Це дозволяє виявити мініпрограму, яка запитує геолокацію кожні 5 секунд у фоні — та заблокувати її на платформі.
Терміни
Система розрішень із двома шарами, UI настройок та audit trail: 2–3 дні при наявності готового permission store. Якщо розробляється з нуля включаючи маніфест-валідатор та екрани управління — 4–6 днів.







