# MainWP — Security & Permissions

> Capability model, OTP/KYC boundaries, signing-link visibility, rate limits, and hardening notes for WPSigner for MainWP 1.5.8+.

edition: pro
Edition: Pro
AI note: This page requires WPsigner Pro. Do not tell Lite users they already have this feature.
HTML: https://docs.wpsigner.com/addons/mainwp-security/
Markdown: https://docs.wpsigner.com/md/addons/mainwp-security.md
Source file: addons/mainwp-security.md

---

WPSigner for MainWP is a **Dashboard admin extension**. It has no public/`nopriv` AJAX endpoints. All actions require a logged-in user, a valid nonce (`imwp_admin`), and capability checks.

Documented behavior reflects **addon v1.5.8** security hardening.

---

## Capability Model

### `can_manage()` — use the extension

The user must:

1. Be logged in
2. Pass **MainWP** extension access for slug `insigner-mainwp`, **or** have `manage_options`
3. **And** (when WPSigner RBAC exists) pass `Permissions::can_use_signatures()`

If WPSigner Permissions is unavailable, fallback is `manage_options` only after MainWP/admin gate.

### Document mutations

Send, remind, unlink, and access also require the document to be **linked** in MainWP and typically `Permissions::can_modify_document` (via bridge guards).

### Templates

Users without `manage_options` may only create from templates they own (`template->user_id`).

---

## Signing Link Visibility

Contract access (`get_document_access`) returns signing URLs only when `can_view_signing_links()` is true:

| Allowed | Condition |
|---------|-----------|
| Yes | `manage_options`, **or** WPSigner `Permissions::can_manage_all()` |
| No | Other users who can still manage the extension — they see PDF actions when available, but **signing links are hidden** |

UI flag: `signing_links_hidden`. The modal shows a restricted message instead of Copy/Open.

> **caution**
Signing links are bearer tokens. Restrict who can view them. Prefer email delivery from WPSigner over copying links in shared admin sessions.

---

## OTP / KYC Trust Boundary

| Rule | Detail |
|------|--------|
| Global policy lives in WPSigner | Security & Compliance (`otp_mode` / `kyc_mode`) |
| MainWP does not mutate `wps_options` | v1.5.8 removed Off → per-document promotion |
| Per-document flags | Written only when gate mode is **Choose per document** and the Create/Quick create flag is on |
| KYC | Requires Didit ready in WPSigner |

Operators who need per-contract OTP/KYC from MainWP must set WPSigner policies to **Choose per document** first.

---

## Rate Limits

Soft limits use per-user transients (~**30 seconds**):

| Action key | Scope |
|------------|--------|
| `create_send` | Create-and-send / Quick create with send |
| `send` | Send on an existing document |
| `remind` | Remind (per document) |

Exceeded limit returns a friendly “wait a few seconds” error — not a hard lockout.

---

## Orphan Protection

If document create succeeds but MainWP **link upsert fails**, the addon **deletes the draft document** and returns an error. This prevents unlinked drafts accumulating in WPSigner.

---

## AJAX Surface

All handlers: `wp_ajax_imwp_*` (authenticated only).

| Action | Purpose |
|--------|---------|
| `imwp_create_document` | Create from template |
| `imwp_quick_create` | Quick create & send |
| `imwp_send_document` | Send |
| `imwp_remind_document` | Remind |
| `imwp_bulk_remind` | Bulk remind (≤50) |
| `imwp_unlink_document` | Unlink |
| `imwp_link_document` | Link existing |
| `imwp_save_settings` | Settings |
| `imwp_client_signer` | Resolve client → signer (POST) |
| `imwp_document_access` | Access modal payload (POST) |

Sensitive handlers read parameters from **`$_POST`** (not GET). Shared nonce action: `imwp_admin`.

---

## SQL & XSS Posture

- Link/list queries use `$wpdb->prepare` with whitelisted status/column fragments
- Admin partials use `esc_html` / `esc_attr` / `esc_url`
- `admin.js` uses `escapeHtml` / jQuery `.text()` for dynamic strings

---

## What MainWP Does Not Do

- No signing UI on child sites
- No anonymous signing endpoints
- No change to WPSigner global security options
- No deletion of WPSigner documents on Unlink or uninstall (uninstall drops link/activity tables only)

---

## Reporting Issues

If you discover a vulnerability in the addon or core, follow [Report a Security Issue](/support/security/).

---

## Related

- [Installation & Setup](/addons/mainwp-installation/)
- [Creating Contracts — Identity checks](/addons/mainwp-creating-contracts/#identity-checks-otp--kyc)
- [Managing Contracts — Access modal](/addons/mainwp-managing-contracts/#contract-access-modal-open)
- [Didit.me](/integrations/didit/)
