BuddyNext - community engine for WordPress

REST: Auth and Account

Free & Pro

The authentication and account-management routes under buddynext/v1. They cover front-end login, registration, two-factor verification, email verification, member approval, and the self-service account flows (change password, change email, sign out everywhere, manage 2FA, mint/revoke application passwords, and unlink social providers). This page is for developers building a custom login UI, a mobile client, or any client that drives these flows over REST.

The front-end login UI driven by the auth and account REST routes documented on this page

Contract

These routes follow the same envelope, error shape, and nonce rules as the rest of the API - see the REST contract page (14-rest-contract) for the cross-surface conventions. Two things are specific to this surface:

  • Permission model is unusual here. Most BuddyNext routes gate on a capability or login state. The auth routes intentionally do not: the pre-login routes are public (permission_callback => __return_true) because the caller has no session yet. The account routes and the post-login auth routes gate on "logged in" only. Per-route detail is in the tables below.
  • Authenticated calls still need the cookie + wp_rest nonce. "Public" means no capability check, not "no authentication needed for state to be correct". A logged-in client calls these with the standard X-WP-Nonce header. If the nonce goes stale, mint a fresh one from GET /auth/nonce (see Notes).

Source: includes/Auth/AuthController.php (auth + app-password routes), includes/Auth/TwoFactorController.php (account/2fa routes), and includes/Auth/SocialLogin.php (the social-unlink route).

Auth routes

All paths below are prefixed with /wp-json/buddynext/v1.

Method Path Auth Purpose
POST /auth/login Public Log a user in by email/username + password. Returns a 2FA challenge token instead of a session when 2FA is enabled.
POST /auth/2fa Public Complete a 2FA challenge with twofa_token + code, finishing the login.
POST /auth/2fa/email-code Public Send a one-time 2FA code by email for the pending challenge (twofa_token).
GET /auth/register/config Public The signup contract plus a fresh guard bundle. Call this before /auth/register. See below.
POST /auth/register Public Create a new user account (email, user_login, password, optional terms_agreed, invite).
POST /auth/register/complete Public Finish a parked social sign-up: supply pending_token plus the things OAuth cannot (terms_agreed, required profile fields), then the account is created.
POST /auth/lost-password Public Start a password reset for user_login (email or username).
POST /auth/reset-password Public Complete a reset with key + login + new password.
POST /auth/approve/{id} Admin Approve a pending member (manual-approval registration mode).
POST /auth/verify/resend Logged in Resend the email-verification message for the current user.
GET /auth/verify/status Logged in Return the current user's email-verification status.
POST /auth/change-password Logged in Set a new password after verifying current_password. Returns 422 with field-keyed errors on failure.
POST /auth/change-email Logged in Change the current user's email (email).
POST /auth/sign-out-everywhere Logged in Destroy all of the current user's sessions on every device.
POST /auth/app-password Logged in Mint a WordPress Application Password for the current user (optional name). The plaintext password is returned once (201).
GET /auth/app-password Logged in List the current user's Application Passwords (metadata only, never the secret).
DELETE /auth/app-password/{uuid} Logged in Revoke one of the current user's Application Passwords.
GET /auth/nonce Public Mint a fresh wp_rest nonce for the current session (stale-nonce recovery).

The login, register, 2FA, lost-password, and reset-password routes register with permission_callback => __return_true. They are reachable by anyone, by design, because the caller is pre-session. /auth/approve/{id} checks an admin capability in its own callback; the verify/change/sign-out routes check require_auth (logged in).

GET /auth/register/config - read this before you register

A non-browser client (a native app, a headless front end) cannot register without calling this first. The anti-bot guard bundle - the time-trap token, the human-check question, the honeypot field name - is otherwise only minted inside the signup template. A client that posts straight to /auth/register without those values is scored as a bot and rejected.

The route is public and returns the signup contract plus a freshly minted guard bundle:

Key Type Meaning
mode string The site's registration mode.
terms bool Whether terms consent is required.
terms_url string Where the terms document lives.
fields array The required profile fields, each { key, label, type, required, options, description }.
reg_token string The time-trap token. Post it back on /auth/register.
honeypot_field string The name of the honeypot input. Send it empty.
challenge array { question, token } when the human check is enabled; an empty array when it is not.

Mint the bundle fresh per signup attempt - the tokens are single-use.

POST /auth/register/complete - finish a parked social sign-up

An OAuth provider can hand back an identity but not terms consent, and not the site's required profile fields. When that happens the sign-up is parked rather than failed, and the client is given a pending_token. Post it back here with terms_agreed and the outstanding fields, and the account is created.

Account (2FA) routes

Two-factor enrollment and management for the signed-in user. Every route here requires login (require_auth); there is no public or capability variant.

Method Path Auth Purpose
GET /account/2fa Logged in Return the current user's 2FA status (enabled, method, backup-codes remaining).
POST /account/2fa/setup Logged in Begin TOTP enrollment; returns the secret/QR provisioning data to confirm against.
POST /account/2fa/confirm Logged in Confirm enrollment with a code from the authenticator app, activating 2FA.
POST /account/2fa/disable Logged in Disable 2FA after re-verifying the account password.
POST /account/2fa/backup Logged in Regenerate backup codes after re-verifying the account password.

Social account linking

Social login itself runs over browser rewrite routes (/oauth/{provider}/ and /oauth/{provider}/callback/), not JSON REST. The one REST route on this surface unlinks a provider from the signed-in user.

Method Path Auth Purpose
DELETE /me/social/{provider} Logged in Unlink a linked social provider (e.g. google, facebook, github, discord) from the current user. Returns {"unlinked": true}.

Examples

Log in

A successful login with 2FA off returns the session result. With 2FA on, the response carries a twofa_token and the client must follow up with POST /auth/2fa.

curl -X POST https://example.com/wp-json/buddynext/v1/auth/login \
  -H 'Content-Type: application/json' \
  -d '{
    "user": "ada@example.com",
    "password": "correct horse battery staple",
    "remember": true
  }'

Parameters: user (required, email or username), password (required), remember (optional boolean, default false). When the account has 2FA enabled, complete the challenge:

curl -X POST https://example.com/wp-json/buddynext/v1/auth/2fa \
  -H 'Content-Type: application/json' \
  -d '{ "twofa_token": "<token-from-login>", "code": "123456" }'

Register

curl -X POST https://example.com/wp-json/buddynext/v1/auth/register \
  -H 'Content-Type: application/json' \
  -d '{
    "email": "grace@example.com",
    "user_login": "grace",
    "password": "a strong passphrase",
    "terms_agreed": true,
    "invite": "ABCD-1234"
  }'

Required: email, user_login, password. Optional: terms_agreed (boolean, default false), invite (string, used when the community runs invite-gated or referral registration). Registration honours the site's users_can_register setting and the configured approval mode - when manual approval is on, the new account stays pending until an admin calls POST /auth/approve/{id}.

Change password (validation envelope)

State-changing account calls send the cookie + nonce and return field-keyed 422 errors on validation failure:

curl -X POST https://example.com/wp-json/buddynext/v1/auth/change-password \
  -H 'Content-Type: application/json' \
  -H 'X-WP-Nonce: <wp_rest nonce>' \
  --cookie 'wordpress_logged_in_...=...' \
  -d '{ "current_password": "old-pass", "new_password": "new-pass-8+chars" }'
{
  "code": "rest_invalid_param",
  "message": "Current password does not match.",
  "data": {
    "status": 422,
    "fields": { "current_password": "Current password does not match." }
  }
}

Notes

  • Stale-nonce recovery. GET /auth/nonce mints a fresh wp_rest nonce for the current session and sends Cache-Control: no-store. The shared front-end REST client uses it to recover from a 403 on a stale nonce without a full page reload. It re-validates the auth cookie directly, so it never returns an anonymous nonce to a logged-in caller, and the minted nonce is usable only by the same session.
  • 2FA login is a two-step flow. POST /auth/login does not always return a session - if the account has 2FA on, it returns a twofa_token. The client then calls POST /auth/2fa (TOTP/backup code) or requests an email code first via POST /auth/2fa/email-code. Build clients to expect either outcome.
  • Re-authentication on sensitive 2FA changes. Disabling 2FA and regenerating backup codes both require the account password in the request body, not just an active session.
  • Free vs Pro. All routes on this page are part of Free (buddynext/v1). Pro adds its own account-scoped routes under buddynext-pro/v1 (billing, subscriptions, push preferences) - see REST: Pro namespace (24-rest-pro).