What it does
This page collects the security facts that matter most when you're deciding whether to trust WPClientHub with a client site.
Magic links
- Client sign-in links: 15 minutes, single-use, throttled 5 attempts per email / 20 per IP per 15 minutes, enumeration-safe (a known and an unknown email get the identical response).
- Client invitation links: 7 days, single-use.
- Team invitation links: 7 days, single-use, with a reminder email 48 hours after sending.
- Every magic link is stored hashed, never in plaintext, and is never queued for delivery — it is sent synchronously so the plaintext token never touches a queue backend.
What the WPClientHub One plugin can and can't do
Pairing a site gives your WPClientHub account administrative control over that site: syncing state, running updates you trigger, taking backups, one-time single-use login, toggling maintenance mode, and — from plugin version 0.5.0 — updating itself when you ask it to. It never auto-applies anything without you asking, including security releases; a security release is flagged urgent, never installed silently.
When you pair a site, WPClientHub generates a unique Ed25519 keypair for that site. The site receives and stores only the public key; the private signing key stays with WPClientHub, encrypted (see where the signing keys live). Every request from WPClientHub to the site carries a detached Ed25519 signature over a timestamp, a random nonce, and the exact request body, verified with libsodium before anything runs; requests more than 5 minutes off, or replayed nonces, are rejected outright. For data a site pushes back (sync results, activity log entries), the site generates its own bearer secret at pairing time — WPClientHub never chooses a site's own credentials.
Your WordPress password is never seen, stored, or transmitted by WPClientHub. One-click login uses a single-use, 60-second token instead. The one exception is a password you explicitly ask a site to generate for a new account in WordPress user management: the site creates it, it is shown to you once in that dialog, and it is never stored by WPClientHub. The user lists we keep hold logins, emails, roles and last sign-in — never passwords or password hashes.
Google Drive & Dropbox backup destinations
When you connect your agency’s Google Drive or Dropbox as backup storage, WPClientHub asks for the narrowest access each offers — Google’s drive.file (only files WPClientHub creates) and a Dropbox App folder (only Apps/<app name>). The long-lived grant is stored encrypted and never leaves WPClientHub; each backup or restore hands the site a short-lived access token, used for an hour at most and discarded when the job ends. Archives travel from the site to Google or Dropbox directly — WPClientHub never sees what is in them. Disconnecting revokes the grant at Google or Dropbox. See Google Drive & Dropbox backups.
Where the signing keys live
Each site's private signing key is stored in WPClientHub's database, encrypted at rest (AES-256) with the application's encryption key. That key is kept in the server's configuration, not in the database, and is never written into a database backup. The token a site uses to send data back, and every pairing token, are stored only as SHA-256 hashes. The other credentials you connect (Stripe restricted keys, backup storage, Search Console and Cloudflare tokens) and two-factor secrets are encrypted the same way.
If WPClientHub itself were breached
What an attacker could do depends on what they get:
- A copy of the database only, for example a leaked backup file. The signing keys in it are encrypted and useless without the application key, which isn't in the database. Tokens are hashes that can't be turned back into tokens. The backup file is itself encrypted with a separate key. A copy of the database can't be used to control a site.
- The whole WPClientHub server, database and application key together. An attacker could decrypt the signing keys and send signed requests to connected sites, with the same reach you have from the dashboard: updates, backups and restores, single-use logins and WordPress user changes. That is the scenario our server security is built around, and we would rather say so than call a breach harmless.
What limits the damage, and what you can do:
- Per-site keys. Every site has its own key pair, so a key only ever works on that one site.
- Signed requests with replay protection. Each request is signed over a timestamp, a random nonce and the exact body. A request more than 5 minutes old, or one seen before, is refused, so an intercepted request can't be reused.
- Revocation on the site. Disconnect on the WPClientHub One settings page deletes the site's copy of the public key, so no signature, old or stolen, is accepted again; deactivating the plugin stops it answering at all. Reconnect in WPClientHub issues a new key pair for the site, and the old one stops working the moment the site completes the handshake. Removing a site from the WPClientHub dashboard revokes its sync token but leaves the key on the site, so use the plugin's Disconnect, or Reconnect, to revoke the key itself.
Backups of the hub itself
WPClientHub's own database — the one holding every managed site's encrypted signing key and your account data — is backed up daily, encrypted, and verified every night by a test restore into a scratch database. An hourly check confirms the newest backup is fresh and encrypted, and emails us the moment it isn't.
Two-factor authentication
Any member can turn on two-factor authentication (TOTP — Google Authenticator, 1Password, Authy, any RFC 6238 app) from Settings → Security: scan a QR code, confirm the current six-digit code, and it's on. Ten single-use recovery codes are issued at that moment and shown exactly once — save them somewhere safe, since a lost phone with no saved codes means asking an owner or admin to remove and re-invite the account. Codes can be regenerated at any time, which invalidates the old set.
An owner can require two-factor authentication for everyone in the organization from the same page. Turning that on is refused unless the owner turning it on is themselves enrolled — otherwise a single-owner organization could lock out its only owner. A member caught by the requirement without two-factor on is redirected to Security to set it up on their very next request; nothing already open in their browser is interrupted or logged out.
Turning two-factor off, regenerating recovery codes, and signing out other sessions all require a fresh re-confirmation of your password (and, once enrolled, your second factor too) taken within the last ten minutes — the same "confirm it's you" prompt Laravel calls step-up re-authentication, just enforced on more than the one route it ships with by default.
Active sessions
Settings → Security also lists every browser currently signed in to your account — device/browser, IP address, and when it was last active — pulled directly from the session store, and lets you sign out every session except the one you're using right now. Changing your password does the same thing automatically: it invalidates every other session's "remember me" cookie and removes their session rows outright, so a stolen password stops being useful the moment you notice and change it.
Limits & defaults
| Setting | Default |
|---|---|
| Client magic link TTL | 15 minutes |
| Client / team invite TTL | 7 days |
| Request signature clock skew tolerance | 5 minutes |
| One-click login token | Single-use, 60 seconds |
| Site signing keys | Ed25519, one pair per site; private key encrypted at rest (AES-256); replaced on every Reconnect |
| Hub database backup | Daily, encrypted, test-restored nightly, freshness-checked hourly |
| Google Drive / Dropbox grant | Encrypted at rest, never sent to a site; sites get one-job access tokens used for an hour at most |
| Two-factor recovery codes | 10, single-use, regenerate any time |
| Two-factor code window | ±30 seconds, each code usable once |
| Two-factor code attempts | 5 per minute, per account and per IP |
| "Remember this device" at login | 30 days, opt-in per device |
| Step-up re-confirmation window | 10 minutes |