# Maui Permissions

> >

- **Type:** Skill
- **Install:** `agentstack add skill-davidortinau-maui-skills-maui-permissions`
- **Verified:** Yes — security-reviewed for prompt injection and unsafe behavior
- **Seller:** [davidortinau](https://agentstack.voostack.com/s/davidortinau)
- **Installs:** 0
- **Category:** [Agent Skills](https://agentstack.voostack.com/c/agent-skills)
- **Latest version:** 0.1.0
- **License:** MIT
- **Upstream author:** [davidortinau](https://github.com/davidortinau)
- **Source:** https://github.com/davidortinau/maui-skills/tree/main/plugins/maui-skills/skills/maui-permissions

## Install

```sh
agentstack add skill-davidortinau-maui-skills-maui-permissions
```

Requires the [AgentStack CLI](https://agentstack.voostack.com/docs/cli). Works with Claude Code, Cursor, and any MCP-compatible agent.

## About

# .NET MAUI Permissions — Gotchas & Best Practices

## Critical Anti-Patterns

### 1. Requesting without checking first

```csharp
// ❌ 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.

```csharp
// ❌ 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

```csharp
// ❌ 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:

```csharp
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:

```csharp
// ❌ 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:

```csharp
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

- [ ] `CheckStatusAsync` called before every `RequestAsync`
- [ ] No permission calls in constructors — use `OnAppearing` or commands
- [ ] All `PermissionStatus` values handled (`Denied`, `Restricted`, `Limited`)
- [ ] Android: `ShouldShowRationale` shown before re-requesting
- [ ] iOS: Settings navigation provided for denied permissions
- [ ] Android API 33+: using `Photos`/`Media` instead of `StorageRead`/`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](https://github.com/davidortinau)
- **Source:** [davidortinau/maui-skills](https://github.com/davidortinau/maui-skills)
- **License:** MIT

Install and usage instructions live in the source repository linked above.

## Pricing

- **Free** — Free

## Security capabilities

Automated source analysis of v0.1.0 — what this tool can access:

- **Network access:** no
- **Filesystem access:** no
- **Shell / process execution:** no
- **Environment & secrets:** no
- **Dynamic code execution:** no

*"Yes" means the capability is present in the source — more access means more to trust, not that it is unsafe.*


## Versions

- **0.1.0** — security scan: passed — Imported from the upstream source.

## Links

- Listing page: https://agentstack.voostack.com/l/skill-davidortinau-maui-skills-maui-permissions
- Seller: https://agentstack.voostack.com/s/davidortinau
- Browse the marketplace: https://agentstack.voostack.com/browse

---
Listed on AgentStack — the marketplace for AI agent skills and MCP servers. Every listing is security-reviewed. Creators keep 70%.
