AJAX and REST endpoints

Every endpoint reachable without signing in, what token each needs, and which of them can be used to find out whether an account exists.

  • Advanced
  • 9 min read
  • Applies to 2.0

The shape of it

Everything the front end does goes through admin-ajax.php. REST is used for two things only: the Form Builder's admin screen, and the return leg of an OAuth handshake.

The six REST routes

RouteMethodWho may call it
attrua_pro/v2/form-fields/{target}GET, POSTmanage_options
attrua_pro/v2/form-fields/{target}/reorderPOSTmanage_options
attrua_pro/v2/form-fields/{target}/{field_id}PATCH, DELETEmanage_options
attrua_pro/v2/form-field-typesGETmanage_options
attributes-pro/v1/smtp-oauth/{provider}GETanyone
attributes-pro/v1/social/{provider}GETanyone

{target}, {field_id} and {provider} are written above the way you would type them. WordPress holds them as patterns, which is the form wp rest route list prints and the form to match against if you are filtering routes:

attrua_pro/v2/form-fields/(?P<target>[a-z0-9_-]+)
attrua_pro/v2/form-fields/(?P<target>[a-z0-9_-]+)/reorder
attrua_pro/v2/form-fields/(?P<target>[a-z0-9_-]+)/(?P<field_id>[a-z0-9_]+)
attrua_pro/v2/form-field-types
attributes-pro/v1/smtp-oauth/(?P<provider>[a-z0-9_-]+)
attributes-pro/v1/social/(?P<provider>[a-z0-9_-]+)

There is no REST API for member data. The four attrua_pro/v2 routes all run the same manage_options check and exist to let the Form Builder screen talk to its own storage. If you are looking for a way to read accounts, profiles or submissions over REST, this plugin does not offer one.

The two attributes-pro/v1 routes are genuinely public — their permission_callback returns true for everyone, and that is not an oversight. They are where Google, Microsoft, Facebook and GitHub send the browser back after a sign-in or a mailbox connection, and a provider's redirect carries no WordPress cookie or nonce. Asking for a capability there would refuse every real return.

What proves such a request instead is the state token checked inside the handler: single-use, minted when the administrator pressed Connect, and carrying the user id so the callback can insist the person finishing the handshake is the one who started it.

The practical consequence is for your firewall, not your code: if you filter /wp-json/, these two paths must stay reachable from the outside or social sign-in and SMTP OAuth both break at the last step, with no error on your side.

Endpoints reachable without signing in

Sixteen in Pro, two in the free plugin. Every one verifies a nonce, named in the table below.

ActionNonceFor
attrua_auth_requestattrua-auth-requestdispatching an alternative sign-in method
attrua_pro_check_usernameattrua_pro_check_usernamelive "is this username taken?"
attrua_pro_check_emailattrua_pro_check_emaillive "is this email registered?"
attrua_pw2fa_verifyattrua_pw2fa_verifysubmitting a second-factor code
attrua_pw2fa_resendattrua_pw2fa_resendresending that code
attrua_verify_2faattrua_2fa_noncesubmitting a 2FA code on the other flow
attrua_qr_check_userattrua_qr_check_userstep 1 of QR sign-in
attrua_qr_login_generateattrua_qr_generateproducing the QR challenge
attrua_qr_login_pollattrua_qr_pollpolling for approval
attrua_request_magic_linkattrua-magic-link-nonce"email me a sign-in link"
attrua_social_redirectattrua_social_redirectgetting the social provider's URL
attrua_step_login_check_userattrua_step_login_check_userstep 1 of two-step sign-in
attrua_totp_pl_check_userattrua_totp_pl_check_userstep 1 of passwordless authenticator sign-in
attrua_step_navigateattrua_step_navigatenext/back on multi-step registration
attrua_check_username (free)attrua_registerlive username check
attrua_check_email (free)attrua_registerlive email check

One deliberate exception, attrua_refresh_login_nonce, verifies nothing — because its whole job is to hand out a fresh nonce to a page served from cache. Requiring a token to get a token would be circular, and it returns only a logged-out visitor's nonce, which the sign-in form already contains in plain sight.

Coming from version 1.x? Three public endpoints have been removed, and you should check that nothing of yours called them.

  • attrua_ajax_login and attrua_ajax_register verified no nonce, no capability and no rate limit, and their only handlers wrote the submitted values straight into the audit log. No flow ever used them — anyone could fill the audit table with invented attempts under invented usernames, corrupting the record an administrator reads after an incident.
  • ip_manager_bridge_emergency_init was a debugging escape hatch, registered for logged-out visitors, that force-initialised the IP manager and returned a status string.

One name you may find in the source but never on the wire. attrua_pro_validate_field is written as a public AJAX action in src/Pro/Forms/Validation/AjaxValidator.php, and a scan of the source will report it. It never registers: the class that would call add_action() is not instantiated, having been dropped from the forms service provider along with two others whose clients did not exist. There is no such endpoint on a running site, and nothing of yours should call it.

What a nonce does and does not prove

A nonce on a public endpoint proves the request came from a page this site served. It does not prove who is asking, because the page it came from is public and anyone may fetch it.

So treat a nonce here as a defence against cross-site request forgery — which is what it is — and not as authorisation. Where a public endpoint reveals something, the nonce does not stop a determined caller from asking.

Account enumeration

Several endpoints necessarily answer "does this account exist?" — a live availability check on a registration form cannot work otherwise, and a two-step sign-in has to know whether to show the second step.

EndpointWhat it reveals
attrua_pro_check_username, attrua_check_usernamewhether a username is taken
attrua_pro_check_email, attrua_check_emailwhether an email is registered
attrua_step_login_check_useron success, the account's ID and display name
attrua_qr_check_user, attrua_totp_pl_check_useron success, display name and avatar

None of these is rate limited. The sign-in throttle counts failed authentication attempts; it does not apply to a lookup that never tries to authenticate.

This is the usual trade-off, and most membership plugins make it the same way — but make it knowingly. If your member list is itself sensitive, put a rate limit in front of admin-ajax.php at the web-server or WAF level, because the plugin does not do it for you.

The one exception is attrua_request_magic_link, which does limit requests per email address per hour — though the limit is applied after the account lookup, so probes for addresses that do not exist are not counted.

Second-factor codes

attrua_pw2fa_verify allows five incorrect codes before the pending sign-in is discarded and the member must start again. The count travels with the pending session, so clearing a cookie does not reset it.

Coming from version 1.x? There was no limit. A six-digit code with unlimited attempts inside its ten-minute life is a million guesses against an attacker who already has the password, which is to say the second factor bought nothing.

Admin endpoints

The rest of the surface — 62 further AJAX actions in Pro, six in the free plugin — requires a signed-in user, and each checks both a nonce and a capability, usually manage_options. Those are implementation details of the admin screens rather than an interface to build against; the hooks are the supported extension point.

Something missing or out of date? Tell support.