WordPress REST API Application Password Authentication: Architecture Note
Architecture note for WordPress REST API application‑password authentication: requirements, minimal design, trust boundaries, operational checks, failure modes, and verification steps.
14 Nov 2025, 11:29 UTC

Requirements
To enable stateless token‑based authentication for external clients while preserving the existing WordPress user system, the feature must:
- Support creation, reading, updating, and deletion of application passwords per user.
- Store only a one‑way hash of the secret so the plain value cannot be recovered.
- Validate the presented password via PHP's
password_verifyfunction. - Enforce HTTPS transport to protect the secret in transit.
- Map the authenticated user to WordPress's role/capability model so capability checks work as usual.
- Allow administrators to revoke a password without affecting other user credentials.
Smallest Suitable Design
The design introduces a new REST endpoint under the user resource:
/wp-json/wp/v2/users/<user_id>/application-passwords
This endpoint supports the standard CRUD verbs:
- POST – creates a new application password; returns the generated secret once (only time it is shown).
- GET – lists the user’s application passwords (secrets are never returned).
- PUT/PATCH – updates the description or other metadata.
- DELETE – removes the password, invalidating it immediately.
Internally, the secret is hashed with wp_hash_password (which uses bcrypt by default) and stored as user meta under the key application_password_<uuid>. During authentication, the rest_authentication_errors filter checks the Authorization: ApplicationPassword <username> <secret> header, hashes the supplied secret, compares it with the stored hash, and, on success, attaches the corresponding WP_User object to the request.
Trust and Data Boundaries
The application password secret is known only to the client and the one‑way hash stored in the database. The REST API treats the validated user as trusted for all subsequent capability checks; therefore, any compromise of the secret grants the attacker the same capabilities as the user. Network traffic must be protected by TLS (HTTPS) to prevent interception of the secret. No other WordPress component (e.g., plugins, themes) receives the plain secret; they only see the authenticated user object.
Operational Checks
- Logging – Successful and failed authentication attempts should be logged via the
rest_authentication_errorshook or written to the debug log whenSAVEQUERIESis enabled. - Rate limiting – Deploy a rate‑limiting plugin (e.g., based on the
WP REST API Rate Limitingpattern) to return headers such asX-RateLimit-LimitandX-RateLimit-Remainingon the application‑password endpoint, deterring brute‑force attempts. - Usermeta growth – Monitor the number of rows in the
usermetatable with theapplication_password_prefix; a sudden increase may indicate abandoned passwords that should be cleaned up. - Hashing strength – Verify that
wp_hash_passworduses a bcrypt cost of at least 10 (the default in recent WordPress releases). If the site uses a custom hashing function, ensure it meets or exceeds that strength.
Failure Modes and Design Triggers
- Weak hash – If bcrypt is deemed insufficient (e.g., due to advances in GPU cracking), increase the bcrypt cost via a filter on
wp_hash_passwordor migrate to Argon2id, requiring a re‑hash of existing secrets on next use. - Token leakage – If application‑password secrets are frequently exposed (e.g., logged in plain text), consider adding short‑lived expiration or a refresh‑token flow, which would necessitate a new table to store token metadata and issuance times.
- Usermeta lookup performance – On sites with tens of thousands of application passwords per user, the linear scan of usermeta rows could become a bottleneck. In that case, move the password storage to a custom table (
wp_application_passwords) indexed by user ID and token identifier, or cache the hashes in an external store like Redis. - Privileged account risk – Because application passwords bypass two‑factor authentication, administrators may decide to disable the feature for users with high‑risk roles (e.g., administrators). This decision would be enforced via a capability check in the
rest_authentication_errorsfilter.
Example Usage (Illustrative)
The following shows how a client might create and use an application password. Replace placeholders with actual values; the example is not tested and is intended to illustrate the flow.
# 1. Create an application password (requires a logged‑in user with edit_users capability)
curl -X POST https://example.com/wp-json/wp/v2/users/123/application-passwords \
-H "Authorization: Bearer " \
-H "Content-Type: application/json" \
-d '{"name": "Mobile App"}'
# Expected response (not actual output):
# {
# "id": "abcdef123456",
# "name": "Mobile App",
# "password": "aBcDeFgHiJkLmNoPqRsTuVwXyZ1234567890" # shown only once
# }
# 2. Use the password to call a protected endpoint
curl https://example.com/wp-json/wp/v2/posts \
-H "Authorization: ApplicationPassword admin aBcDeFgHiJkLmNoPqRsTuVwXyZ1234567890"
# Expected behavior:
# - If the password is valid and the user has the "read" capability, the request returns a 200 list of posts.
# - If the password is invalid, the response is 401 with a WWW‑Authenticate header.
# - If the user lacks the needed capability, the response is 403.
Verification Steps
- Confirm that the endpoint
/wp-json/wp/v2/users/<id>/application-passwordsexists and returns401for unauthenticated requests. - Using a valid application password, make a request to a protected REST route (e.g.,
/wp-json/wp/v2/posts) and verify a successful200response. - Check the debug log or the output of the
rest_authentication_errorsfilter for entries corresponding to both successful and failed attempts. - If a rate‑limiting plugin is active, ensure the response includes headers like
X-RateLimit-LimitandX-RateLimit-Remainingon the application‑password endpoint. - Change the user’s role or capabilities (e.g., downgrade from editor to subscriber) and confirm that an existing application password no longer grants the previous higher‑level permissions.
Limitations
- Application passwords do not support two‑factor authentication; if 2FA is enforced site‑wide, these passwords bypass it.
- Only a hash is stored, so the secret cannot be recovered if lost; the user must generate a new password.
- Because the secret is validated on each request, any compromise grants immediate access until the password is deleted.
- Usermeta storage can bloat on very high‑traffic sites with many application passwords per user.
Periodically review the usermeta table for orphaned entries and consider migrating to a custom table if performance degrades.
0 replies
A thoughtful contribution can make all the difference. Be the first to share one.