{"record":{"id":"cfe311c4b67d2813","repo":"microg/GmsCore","slug":"client-not-admin-yet-tried-to-remove-work-account","errorCode":null,"errorMessage":"client not admin, yet tried to remove work account","messagePattern":"client not admin, yet tried to remove work account","errorType":"exception","errorClass":"SecurityException","httpStatus":null,"severity":"error","filePath":"play-services-auth-workaccount/core/src/main/kotlin/org/microg/gms/auth/workaccount/WorkAccountService.kt","lineNumber":146,"sourceCode":"                        callback?.onAccountRemoved(result)\n                    }\n                }.start()\n            }\n        }\n    }\n}\n\nclass UnauthorizedWorkAccountServiceImpl : IWorkAccountService.Stub() {\n    override fun setWorkAuthenticatorEnabled(enabled: Boolean) {\n        throw SecurityException(\"client not admin, yet tried to enable work authenticator\")\n    }\n\n    override fun addWorkAccount(callback: IWorkAccountCallback?, token: String?) {\n        throw SecurityException(\"client not admin, yet tried to add work account\")\n    }\n\n    override fun removeWorkAccount(callback: IWorkAccountCallback?, account: Account?) {\n        throw SecurityException(\"client not admin, yet tried to remove work account\")\n    }\n}","sourceCodeStart":128,"sourceCodeEnd":148,"githubUrl":"https://github.com/microg/GmsCore/blob/157c9d86ac46c195a86c2f15ab55c84036223f95/play-services-auth-workaccount/core/src/main/kotlin/org/microg/gms/auth/workaccount/WorkAccountService.kt#L128-L148","documentation":"WorkAccountService.addWorkAccount/removeWorkAccount are stubs that unconditionally throw SecurityException. The client that bound to the service is not recognized as a device admin/work-policy controller, so any attempt to manage work accounts is refused. This is a hard denial, not a transient failure.","triggerScenarios":"Calling IWorkAccountService.removeWorkAccount (or addWorkAccount) from a client that does not hold device-owner/work-profile-admin privileges; binding and invoking the method is allowed but the call immediately throws.","commonSituations":"A regular app (or a non-admin system caller) tries to provision or remove a work account via microG's WorkAccountService; testing the service from an ADB shell or another app without admin rights.","solutions":["Ensure the caller is the device owner or work profile admin (device-owner via dpm set-device-owner) before calling","Do not call these methods from ordinary apps; use the platform's DevicePolicyManager APIs instead","If you are extending microG, implement the admin check the stub expects or gate calls behind an admin permission"],"exampleFix":"// before\nworkAccountService.removeWorkAccount(callback, account)\n// after\nif (devicePolicyManager.isDeviceOwnerApp(context.packageName)) {\n    workAccountService.removeWorkAccount(callback, account)\n} else {\n    Log.w(TAG, \"Not device admin; refusing work account removal\")\n}","handlingStrategy":"try-catch","validationCode":"val isOwner = context.getSystemService(DevicePolicyManager::class.java).isDeviceOwnerApp(context.packageName)\nif (!isOwner) throw IllegalStateException(\"Caller is not device/work admin\")","typeGuard":"fun isAdmin(dp: DevicePolicyManager, pkg: String) = dp.isDeviceOwnerApp(pkg) || dp.isProfileOwnerApp(pkg)","tryCatchPattern":"try {\n    workAccountService.removeWorkAccount(callback, account)\n} catch (e: SecurityException) {\n    Log.w(TAG, \"Not permitted to manage work accounts\", e)\n    // fall back to DevicePolicyManager-based removal\n}","preventionTips":["Only invoke work-account management from the device-owner/profile-owner app","Check admin status before binding to the service","Use platform DevicePolicyManager APIs for non-admin callers"],"tags":["security","android","work-account","device-admin"],"backgroundTag":"permission-denied","analyzedSha":"157c9d86ac46c195a86c2f15ab55c84036223f95","analyzedAt":"2026-09-06T17:27:33.892Z","contentChangedAt":"2026-09-06T17:27:33.892Z","schemaVersion":2},"datasetVersion":"2026-09-14T00:17:10.932Z"}