WordPress plugin intelligence profile

Simple JWT Auth – JWT Authentication for WP REST API

JWT authentication for the WordPress REST API: access and refresh tokens with rotation, revocation, and validation for headless and decoupled apps.

WP PlugIndex snapshot

Current directory snapshot

Tested up to WordPress 7.1
Current version2.0.0
WordPress coverageTested up to WordPress 7.1
Minimum stackWP 7.0 · PHP 8.2
Directory statusactive
Last directory update26 Aug 20262mo ago
Active installationsNewAdoption metric — not a quality score
WordPress.org ratingNo WordPress.org ratings yet
Official packageDirect from WordPress.org

Repository metrics are context, not quality scores. Active installations measure adoption and can strongly reflect a plugin’s age and historical popularity. Ratings, recent updates and “Tested up to” metadata also do not guarantee modern code, error-free operation, security, feature completeness or suitability for a particular site. Why active installs don’t tell the whole story →

WP PlugIndex currently records Simple JWT Auth – JWT Authentication for WP REST API version 2.0.0 as active, updated 2mo ago and tested up to WordPress 7.1.

Overview

About this plugin

Simple JWT Auth – JWT Authentication for WordPress REST API secures and protects your WordPress REST API using JSON Web Tokens. It lets external applications authenticate WordPress users, obtain an access token and a refresh token, and call any REST endpoint with a standard Bearer header.

JSON Web Token (JWT) is an open standard ( RFC 7519 ) that defines a compact, self-contained way to transmit information securely between two parties. This plugin uses JWT to provide a modern, stateless authentication layer for headless WordPress builds.

Modern access-token and refresh-token architecture

  • Issues short-lived access tokens (stateless JWTs) alongside opaque refresh tokens.
  • Refresh-token rotation — every refresh issues a new access token and a new refresh token, so a leaked refresh token is quickly invalidated.
  • Reuse detection — re-presenting a rotated refresh token outside a short grace window revokes the entire token family and fires the simplejwt_auth_token_reuse_detected action.
  • Revocation — revoke a single refresh token, its whole rotation family, or all of a user’s sessions. Refresh tokens are also revoked automatically on logout and password reset.
  • Validation — a dedicated endpoint verifies an access token on demand, and a /me endpoint returns the authenticated user’s profile.

Secure by design

  • Signing keys ( secret_key, private_key, public_key ) are encrypted at rest with AES-256-GCM using a key-encryption-key (KEK) defined in wp-config.php.
  • Refresh tokens are opaque and stored only as SHA-256 hashes — the raw token is never written to the database.
  • Rate limiting (configurable) is applied to the token, refresh, and revoke endpoints to deter brute force.
  • Optional CORS support and optional XML-RPC disabling.

Modern and flexible

  • Requires PHP 8.2+ and WordPress 7.0+.
  • Supports HS256, HS384, HS512, RS256, RS384, RS512, ES256, and ES384 signing algorithms.
  • Extensible via filter and action hooks for payload, expiry, issuer, CORS headers, and the token response.

js, Vue, mobile apps, and any other external client. php constants.

  • Support & questions: WordPress support forum
  • Bug reports: GitHub issues tracker
  • Source code: GitHub repository

Enable PHP HTTP Authorization Header

HTTP Authorization is the mechanism clients use to send credentials to a server — a special Authorization header in the HTTP request. Many shared hosts have it disabled by default.

Shared hosts

htaccess file:

*) - [E=HTTP_AUTHORIZATION:%1]

WP Engine

htaccess file:

*)" HTTP_AUTHORIZATION=$1

Configuration

Simple JWT Auth uses a Key-Encryption-Key (KEK) to encrypt and decrypt the JWT signing keys ( secret_key, private_key, and public_key ) at rest. php with the SIMPLE_JWT_AUTH_ENCRYPT_KEY constant. The KEK must be exactly 32 characters long and must never be revealed.

define( 'SIMPLE_JWT_AUTH_ENCRYPT_KEY', 'your-32-char-encryption-key' );

Rotating the KEK invalidates the stored signing keys and requires re-entering them in the plugin settings (a simplejwt_kek_mismatch error is returned until then).

Signing keys via wp-config.php constants

php file. Constants take precedence over the plugin settings, and their values are used as-is (plaintext, not encrypted).

define( 'SIMPLE_JWT_AUTH_ALGORITHM', 'HS256' ); // HS256, HS384, HS512, RS256, RS384, RS512, ES256 or ES384.

define( 'SIMPLE_JWT_AUTH_SECRET_KEY', 'your-secret-key' ); // Required for HS* algorithms (min 32 chars). ' ); // Required for RS*/ES* signing. ' ); // Required for RS*/ES* verification.

  • SIMPLE_JWT_AUTH_ALGORITHM — overrides algorithm, the JWT signing algorithm.
  • SIMPLE_JWT_AUTH_SECRET_KEY — overrides secret_key, used for symmetric (HS256/384/512) signing and verification.
  • SIMPLE_JWT_AUTH_PRIVATE_KEY — overrides private_key, used for asymmetric (RSA/EC) signing.
  • SIMPLE_JWT_AUTH_PUBLIC_KEY — overrides public_key, used for asymmetric (RSA/EC) verification.

php”.

Enabling authentication

For a fresh install, authentication is disabled by default. Turn on Enable JWT in the plugin settings, choose an algorithm, and provide the required signing key(s) before issuing tokens.

REST Endpoints

The plugin registers the auth/v1 namespace with five endpoints:

  • POST /wp-json/auth/v1/token — Authenticate credentials; return an access token and a refresh token.
  • POST /wp-json/auth/v1/token/refresh — Rotate an access token (and refresh token) using a refresh token.
  • POST /wp-json/auth/v1/token/revoke — Revoke a refresh token and its rotation family.
  • POST /wp-json/auth/v1/token/validate — Validate an access token.
  • GET /wp-json/auth/v1/me — Return the authenticated user’s profile.

Generate a token

Submit a POST request with username and password:

com/wp-json/auth/v1/token' \ --header 'Content-Type: application/json' \ --data-raw '{ "username": "wordpress_username", "password": "wordpress_password" }'

Success response:

", "token_expires_in": 900, "refresh_token": "opaque-refresh-token", "refresh_expires_in": 1209600 } }

Store the access token and refresh token in your application (a secure cookie, localStorage, or a wrapper such as localForage ). Then pass the access token as a Bearer header on every protected request:

Authorization: Bearer your-access-token

For example, creating a post with an access token:

", "status": "publish" }'

Refresh a token

Access tokens are short-lived. When one expires, send the refresh token to /token/refresh (in the body or as a Bearer header) to rotate it and receive a new access token and refresh token:

com/wp-json/auth/v1/token/refresh' \ --header 'Content-Type: application/json' \ --data-raw '{ "refresh_token": "opaque-refresh-token" }'

The response has the same shape as the token response. Each rotation invalidates the previous refresh token.

Revoke a token

To invalidate a session, send the refresh token to /token/revoke:

com/wp-json/auth/v1/token/revoke' \ --header 'Content-Type: application/json' \ --data-raw '{ "refresh_token": "opaque-refresh-token" }'

Success response:

{ "code": "simplejwt_token_revoked", "message": "Token has been revoked", "data": { "status": 200 } }

Validate a token

Verify an access token with a POST request carrying the Bearer header:

'

Success response:

{ "code": "simplejwt_valid_token", "message": "Token is valid", "data": { "status": 200 } }

Current user

Get the authenticated user’s profile:

'

Success response:

com", "nicename": "username", "display_name": "User Name", "roles": ["administrator"] } }

REST Errors

status HTTP status. Common codes include:

  • simplejwt_missing_credentials — Username or password is missing.
  • simplejwt_invalid_username — The username is not registered on this site.
  • simplejwt_incorrect_password — The password is incorrect.
  • simplejwt_no_auth_header — The Authorization header is missing.
  • simplejwt_bad_auth_header — The Authorization header is malformed.
  • simplejwt_invalid_token — The access token is invalid (bad signature, malformed, or not yet valid).
  • simplejwt_expired_token — The access or refresh token has expired.
  • simplejwt_invalid_refresh_token — The refresh token is unknown or invalid.
  • simplejwt_reused_refresh_token — A rotated refresh token was reused; the token family was revoked.
  • simplejwt_revoked_token — The token has been revoked.
  • simplejwt_bad_issuer — The token issuer does not match this server.
  • simplejwt_unsupported_algorithm — The configured signing algorithm is unsupported.
  • simplejwt_rate_limited — Too many requests; please try again later.
  • simplejwt_bad_config — JWT authentication is not configured or is disabled.
  • simplejwt_bad_encryption_key — The key-encryption-key is not configured.
  • simplejwt_invalid_enckey_length — The key-encryption-key is not exactly 32 characters.
  • simplejwt_kek_mismatch — The key-encryption-key was rotated; re-enter the signing keys.

Available Hooks

Simple JWT Auth is developer-friendly and exposes filter and action hooks to override its default behaviour.

simplejwt_cors_allow_headers (filter)

Modify the CORS Access-Control-Allow-Headers value. Default: Access-Control-Allow-Headers, Content-Type, Authorization.

add_filter( 'simplejwt_cors_allow_headers', function ( $headers ) { return $headers; } );

simplejwt_auth_iss (filter)

Change the token iss (issuer) claim. Default: get_bloginfo( 'url' ).

add_filter( 'simplejwt_auth_iss', function ( $iss ) { return $iss; } );

simplejwt_not_before (filter)

Change the token nbf (not-before) claim. Default: the issue time.

add_filter( 'simplejwt_not_before', function ( $not_before, $issued_at ) { return $not_before; }, 10, 2 );

simplejwt_auth_expire (filter)

Change the token exp (expiry) claim. Default: time() + access token lifetime (900 seconds by default).

add_filter( 'simplejwt_auth_expire', function ( $expire, $issued_at ) { return $expire; }, 10, 2 );

simplejwt_payload_before_sign (filter)

Modify the JWT payload before it is signed. id ).

add_filter( 'simplejwt_payload_before_sign', function ( $payload, $user ) { return $payload; }, 10, 2 );

simplejwt_token_before_dispatch (filter)

Modify the token response before it is returned to the client. The response includes the access token, refresh token, and their lifetimes.

add_filter( 'simplejwt_token_before_dispatch', function ( $data, $user ) { return $data; }, 10, 2 );

simplejwt_auth_token_reuse_detected (action)

Fired when refresh-token reuse is detected and a token family is revoked. Arguments: $user_id, $family_id, $ip.

add_action( 'simplejwt_auth_token_reuse_detected', function ( $user_id, $family_id, $ip ) { // Alert, log, or revoke further sessions here. }, 10, 3 );

simplejwt_rate_limit_max (filter)

Change the maximum number of attempts allowed within the rate-limit window. Default: 10.

add_filter( 'simplejwt_rate_limit_max', function ( $max ) { return $max; } );

simplejwt_rate_limit_window (filter)

Change the rate-limit window, in seconds. Default: MINUTE_IN_SECONDS (60).

add_filter( 'simplejwt_rate_limit_window', function ( $window ) { return $window; } );

Postman Collection

A ready-to-use Postman collection is bundled with the plugin. Open Simple JWT Auth → Documentation in your WordPress admin and click Download Postman Collection, then import the JSON into Postman. The collection preconfigures your site URL and includes the token, refresh, revoke, validate, and /me requests.

WordPress.org metadata

Plugin tags

Topics listed for this plugin in the WordPress.org directory.

  • jwt authentication
  • rest api
  • wp api
  • oauth
Compatibility & maintenance

Compatibility & maintenance

Tested with WP 7.1

Marked as tested with the current WordPress 7.1 release.

Current WordPress releaseWordPress 7.1
WordPress.org Tested up to7.1
Minimum requirementsWordPress 7.0 · PHP 8.2
Update activity26 Aug 20262mo ago

“Tested up to” is directory metadata supplied by the plugin author. WP PlugIndex does not turn an older declaration into a claim that the plugin is incompatible.

Changes recorded by WP PlugIndex

Recent tracked changes

No tracked version or compatibility changes have been recorded by WP PlugIndex yet.

Tracking & source

WP PlugIndex tracks public WordPress.org directory metadata and records selected version, compatibility and availability changes as they are detected.

Plugin watchlist

Watch this plugin

Sign in to your WP PlugIndex account or create a free account. Free accounts can watch up to 5 plugins and see what changed.

Watch limit

Watchlist limit reached

You have reached your current active watch limit.