Skip to content

Security

Nick Hamze edited this page Sep 1, 2026 · 6 revisions

Security

Fleet prefers an OpenStation OAuth connection. It never asks for, receives, or stores a person's normal WordPress password.

Authority and scope

The OAuth scope is site:manage: Fleet may call the WordPress REST API as the user who approved the connection. It is deliberately broad and described as Full API access on the consent screen.

That scope does not bypass WordPress. Every Core and plugin route still runs its normal permission_callback, capability checks, validation, and sanitization. Because Fleet is intended for whole-site management, OpenStation requires manage_options to approve a connection.

Authorization request protection

  • Authorization Code flow with PKCE S256 is mandatory.
  • Fleet generates a high-entropy verifier and stores it encrypted only for the pending request.
  • state binds the browser return to the initiating hub user and request.
  • The callback checks the RFC 9207 iss response against the discovered issuer.
  • The first approval binds a UUID client id to an exact HTTPS callback URI; later requests must match byte-for-byte.
  • Authorization codes are single-use, expire after five minutes, and are bound to the client, callback, and PKCE challenge.
  • Metadata and all OAuth endpoints must share the managed site's HTTPS origin.

Token lifecycle

  • Access tokens expire after 15 minutes.
  • Refresh tokens rotate on every use.
  • Refresh tokens expire after 30 days of inactivity and after 90 days absolutely.
  • Reusing an old refresh token revokes the entire token family.
  • Token and revocation responses use Cache-Control: no-store.

The managed site stores SHA-256 hashes, not plaintext bearer tokens. Its grant record includes the client, approving user, exact redirect URI, timestamps, scope, expiry, and revocation state.

Storage on the hub

Connections belong to the current hub user and are stored in user meta. OAuth access and refresh tokens are encrypted with sodium secret-box encryption. Fleet derives the encryption key from the hub's WordPress authentication salt.

Changing that salt makes stored credentials unreadable; reconnect affected sites after a salt rotation. Fleet loads WordPress's bundled sodium compatibility layer when the PHP extension is unavailable and refuses to save a secret if encryption cannot be provided.

The API console retains its latest sanitized result in a short-lived per-user transient only long enough to display it and limits the stored response size. It never stores or displays authorization headers.

Request boundary

  • Hub and managed site must use HTTPS.
  • Target URLs cannot contain embedded credentials, query strings, or fragments.
  • Remote calls use WordPress safe HTTP functions and keep unsafe-URL rejection enabled.
  • Fleet is limited to publicly reachable production targets; it does not weaken Core's SSRF protections for private or loopback addresses.
  • OAuth discovery endpoints must share the target site's origin.
  • API-console routes are resolved only beneath the already-discovered REST root; they cannot select a new host.
  • Fleet blocks the hub from connecting to itself while allowing path-based multisite siblings on the same host.

Local hub authorization

Only hub users with manage_options can open Fleet or submit actions. Every local mutation uses a WordPress nonce. Notice messages use fixed codes rather than arbitrary query-string content.

Revocation

An OAuth grant can be revoked by:

  • disconnecting the site from Fleet;
  • the approving user or an administrator under Users → Profile → Fleet connections;
  • changing the approving user's password;
  • deleting that user; or
  • replaying an older refresh token.

Disconnect removes the local record only after the managed site confirms revocation. The revocation endpoint returns success for unknown tokens so it cannot be used as a token oracle.

Application Password bootstrap

If OpenStation is absent, Fleet can temporarily use a named WordPress Application Password to reach the Core Plugins API. The password is encrypted with the same hub storage, acts as the approving WordPress user, and is individually revocable. Disconnect and reconnect after OpenStation is active to replace the bootstrap connection with OAuth.

Uninstall and support reports

Plugin uninstall cannot reliably contact every managed site. Disconnect sites first. If Fleet was removed while a grant remained, revoke it from the managed user's profile.

Never log, paste, or include access tokens, refresh tokens, Application Passwords, authorization headers, callback URLs, salts, or ordinary login passwords in issue reports.

Clone this wiki locally