Install
$ agentstack add skill-davidortinau-maui-skills-maui-permissions ✓ scanned · ✓ verified — works with Claude Code, Cursor, and more.
Security review
✓ PassedNo issues found. Passed automated security review. · v0.1.0 How review works →
- ✓ Prompt-injection patterns
- ✓ Secret / credential exfiltration
- ✓ Dangerous shell & filesystem operations
- ✓ Untrusted network calls
- ✓ Known-malicious package signatures
What it can access
- ✓ Network access No
- ✓ Filesystem access No
- ✓ Shell / process execution No
- ✓ Environment & secrets No
- ✓ Dynamic code execution No
From automated source analysis of v0.1.0. “Used” means the capability is present in the source — more access means more to trust, not that it’s unsafe.
About
.NET MAUI Permissions — Gotchas & Best Practices
Critical Anti-Patterns
1. Requesting without checking first
// ❌ Shows prompt even if already granted
var status = await Permissions.RequestAsync();
// ✅ Check first — avoids unnecessary prompts
var status = await Permissions.CheckStatusAsync();
if (status != PermissionStatus.Granted)
status = await Permissions.RequestAsync();
2. Calling permissions in a constructor
Permission APIs are async and require a UI context. Constructors can't await.
// ❌ Blocks the UI thread or throws
public MyViewModel()
{
var status = Permissions.RequestAsync().Result;
}
// ✅ Use OnAppearing, a command, or async initialization
protected override async void OnAppearing()
{
await CheckAndRequestPermissionAsync();
}
3. Ignoring Denied and Restricted status
// ❌ Only checks for Granted — misses edge cases
if (status == PermissionStatus.Granted) { /* proceed */ }
// ✅ Handle all relevant statuses
switch (status)
{
case PermissionStatus.Granted: /* proceed */ break;
case PermissionStatus.Limited: /* iOS partial access */ break;
case PermissionStatus.Denied:
case PermissionStatus.Restricted:
await ShowSettingsPromptAsync(); break;
}
Platform Pitfalls
⚠️ iOS: One-shot permission dialog
iOS shows the system permission dialog only once per permission, ever. After denial, RequestAsync returns Denied immediately without showing UI. You must guide the user to Settings → App → Permission.
⚠️ Android: ShouldShowRationale timing
ShouldShowRationale returns true only after a prior denial (but not after "Don't ask again"). Use it to show explanatory UI before re-requesting:
if (Permissions.ShouldShowRationale())
{
await Shell.Current.DisplayAlert("Permission needed",
"Camera access is required to scan barcodes.", "OK");
}
status = await Permissions.RequestAsync();
⚠️ Android API 33+: StorageRead/StorageWrite are dead
On Android 13+, StorageRead and StorageWrite always return Granted (scoped storage makes them meaningless). Use granular media permissions instead:
// ❌ Always returns Granted on API 33+ — gives false confidence
await Permissions.RequestAsync();
// ✅ Use the specific media permission
await Permissions.RequestAsync(); // photo access
await Permissions.RequestAsync(); // audio/video
⚠️ Windows: Most permissions always return Granted
Windows doesn't have runtime permission dialogs for most features. Declare capabilities in Package.appxmanifest instead.
Always-Check-Before-Request Pattern
The recommended pattern handles iOS one-shot and Android rationale:
public async Task CheckAndRequestPermissionAsync()
where T : Permissions.BasePermission, new()
{
var status = await Permissions.CheckStatusAsync();
if (status == PermissionStatus.Granted)
return status;
if (status == PermissionStatus.Denied && DeviceInfo.Platform == DevicePlatform.iOS)
return status; // iOS won't show dialog again
if (Permissions.ShouldShowRationale())
{
await Shell.Current.DisplayAlert("Permission needed",
"This feature requires the requested permission.", "OK");
}
return await Permissions.RequestAsync();
}
Checklist
- [ ]
CheckStatusAsynccalled before everyRequestAsync - [ ] No permission calls in constructors — use
OnAppearingor commands - [ ] All
PermissionStatusvalues handled (Denied,Restricted,Limited) - [ ] Android:
ShouldShowRationaleshown before re-requesting - [ ] iOS: Settings navigation provided for denied permissions
- [ ] Android API 33+: using
Photos/Mediainstead ofStorageRead/StorageWrite - [ ] Manifest/plist declarations match runtime permission requests
Source & license
This open-source skill is cataloged on AgentStack and links to its original source — we do not rehost the code.
- Author: davidortinau
- Source: davidortinau/maui-skills
- License: MIT
Install and usage instructions live in the source repository linked above.
Reviews
No reviews yet — be the first.
Write a review
Versions
- v0.1.0 Imported from the upstream source.