{"id":207856,"date":"2024-10-31T08:29:57","date_gmt":"2024-10-31T08:29:57","guid":{"rendered":"https:\/\/wordpress.org\/plugins\/simple-jwt-auth\/"},"modified":"2026-08-26T19:37:16","modified_gmt":"2026-08-26T19:37:16","slug":"simple-jwt-auth","status":"publish","type":"plugin","link":"https:\/\/frp.wordpress.org\/plugins\/simple-jwt-auth\/","author":23122325,"comment_status":"closed","ping_status":"closed","template":"","meta":{"version":"2.0.0","stable_tag":"2.0.0","tested":"7.1","requires":"7.0","requires_php":"8.2","requires_plugins":null,"header_name":"Simple JWT Auth","header_author":"Sayan Dey","header_description":"Extends the WordPress REST API using JSON Web Tokens for robust authentication and authorization. It provides a secure and reliable way to access and manage WordPress data from external applications, making it ideal for building headless CMS solutions.","assets_banners_color":"444444","last_updated":"2026-08-26 19:37:16","external_support_url":"","external_repository_url":"","donate_link":"https:\/\/github.com\/sayandey18","header_plugin_uri":"https:\/\/github.com\/sayandey18\/simple-jwt-auth","header_author_uri":"https:\/\/github.com\/sayandey18","rating":5,"author_block_rating":0,"active_installs":0,"downloads":981,"num_ratings":1,"support_threads":0,"support_threads_resolved":0,"author_block_count":0,"sections":["description","installation","faq","changelog"],"tags":{"1.0.0":{"tag":"1.0.0","author":"sayandey18","date":"2024-10-31 08:47:46","revision":3179291},"1.0.1":{"tag":"1.0.1","author":"sayandey18","date":"2024-10-31 09:04:50","revision":3179302},"1.0.2":{"tag":"1.0.2","author":"sayandey18","date":"2024-11-17 13:30:38","revision":3190642},"2.0.0":{"tag":"2.0.0","author":"sayandey18","date":"2026-08-26 19:37:16","revision":3667722}},"upgrade_notice":{"2.0.0":"<p>Version 2.0.0 is a major release requiring PHP 8.2+ and WordPress 7.0+. It introduces refresh-token rotation and revocation and now enforces the Enable JWT setting. Existing installs with configured signing keys are automatically kept enabled.<\/p>"},"ratings":{"1":0,"2":0,"3":0,"4":0,"5":1},"assets_icons":{"icon-128x128.png":{"filename":"icon-128x128.png","revision":3667229,"resolution":"128x128","location":"assets","locale":"","width":128,"height":128},"icon-256x256.png":{"filename":"icon-256x256.png","revision":3667229,"resolution":"256x256","location":"assets","locale":"","width":256,"height":256},"icon.svg":{"filename":"icon.svg","revision":3667229,"resolution":false,"location":"assets","locale":false}},"assets_banners":{"banner-1544x500.jpg":{"filename":"banner-1544x500.jpg","revision":3667229,"resolution":"1544x500","location":"assets","locale":"","width":1544,"height":500},"banner-772x250.jpg":{"filename":"banner-772x250.jpg","revision":3667229,"resolution":"772x250","location":"assets","locale":"","width":772,"height":250}},"assets_blueprints":{},"all_blocks":[],"tagged_versions":["1.0.0","1.0.1","1.0.2","2.0.0"],"block_files":[],"assets_screenshots":[],"screenshots":{"1":"Simple JWT Auth Settings","2":"Simple JWT Auth Options","3":"Simple JWT Auth Documentation"}},"plugin_section":[],"plugin_tags":[38851,245165,2061,23853,2300],"plugin_category":[38],"plugin_contributors":[233896],"plugin_business_model":[],"class_list":["post-207856","plugin","type-plugin","status-publish","hentry","plugin_tags-jwt","plugin_tags-jwt-authentication","plugin_tags-oauth","plugin_tags-rest-api","plugin_tags-wp-api","plugin_category-authentication","plugin_contributors-sayandey18","plugin_committers-sayandey18"],"banners":{"banner":"https:\/\/ps.w.org\/simple-jwt-auth\/assets\/banner-772x250.jpg?rev=3667229","banner_2x":"https:\/\/ps.w.org\/simple-jwt-auth\/assets\/banner-1544x500.jpg?rev=3667229","banner_rtl":false,"banner_2x_rtl":false},"icons":{"svg":"https:\/\/ps.w.org\/simple-jwt-auth\/assets\/icon.svg?rev=3667229","icon":"https:\/\/ps.w.org\/simple-jwt-auth\/assets\/icon.svg?rev=3667229","icon_2x":false,"generated":false},"screenshots":[],"raw_content":"<!--section=description-->\n<p>Simple JWT Auth \u2013 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.<\/p>\n\n<p>JSON Web Token (JWT) is an open standard (<a href=\"https:\/\/tools.ietf.org\/html\/rfc7519\">RFC 7519<\/a>) 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.<\/p>\n\n<p><strong>Modern access-token and refresh-token architecture<\/strong><\/p>\n\n<ul>\n<li>Issues short-lived <strong>access tokens<\/strong> (stateless JWTs) alongside opaque <strong>refresh tokens<\/strong>.<\/li>\n<li><strong>Refresh-token rotation<\/strong> \u2014 every refresh issues a new access token <em>and<\/em> a new refresh token, so a leaked refresh token is quickly invalidated.<\/li>\n<li><strong>Reuse detection<\/strong> \u2014 re-presenting a rotated refresh token outside a short grace window revokes the entire token family and fires the <code>simplejwt_auth_token_reuse_detected<\/code> action.<\/li>\n<li><strong>Revocation<\/strong> \u2014 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.<\/li>\n<li><strong>Validation<\/strong> \u2014 a dedicated endpoint verifies an access token on demand, and a <code>\/me<\/code> endpoint returns the authenticated user's profile.<\/li>\n<\/ul>\n\n<p><strong>Secure by design<\/strong><\/p>\n\n<ul>\n<li>Signing keys (<code>secret_key<\/code>, <code>private_key<\/code>, <code>public_key<\/code>) are encrypted at rest with AES-256-GCM using a key-encryption-key (KEK) defined in <code>wp-config.php<\/code>.<\/li>\n<li>Refresh tokens are opaque and stored only as SHA-256 hashes \u2014 the raw token is never written to the database.<\/li>\n<li>Rate limiting (configurable) is applied to the token, refresh, and revoke endpoints to deter brute force.<\/li>\n<li>Optional CORS support and optional XML-RPC disabling.<\/li>\n<\/ul>\n\n<p><strong>Modern and flexible<\/strong><\/p>\n\n<ul>\n<li>Requires PHP 8.2+ and WordPress 7.0+.<\/li>\n<li>Supports HS256, HS384, HS512, RS256, RS384, RS512, ES256, and ES384 signing algorithms.<\/li>\n<li>Extensible via filter and action hooks for payload, expiry, issuer, CORS headers, and the token response.<\/li>\n<\/ul>\n\n<p><strong>Built for developers<\/strong> \u2014 authenticate WordPress from React, Next.js, Vue, mobile apps, and any other external client. Configuration can live in the plugin settings or be overridden with <code>wp-config.php<\/code> constants.<\/p>\n\n<ul>\n<li>Support &amp; questions: <a href=\"https:\/\/wordpress.org\/support\/plugin\/simple-jwt-auth\/\">WordPress support forum<\/a><\/li>\n<li>Bug reports: <a href=\"https:\/\/github.com\/sayandey18\/simple-jwt-auth\/issues\">GitHub issues tracker<\/a><\/li>\n<li>Source code: <a href=\"https:\/\/github.com\/sayandey18\/simple-jwt-auth\">GitHub repository<\/a><\/li>\n<\/ul>\n\n<h3>Enable PHP HTTP Authorization Header<\/h3>\n\n<p>HTTP Authorization is the mechanism clients use to send credentials to a server \u2014 a special <code>Authorization<\/code> header in the HTTP request. Many shared hosts have it disabled by default.<\/p>\n\n<h4>Shared hosts<\/h4>\n\n<p>Add the following to your <code>.htaccess<\/code> file:<\/p>\n\n<pre><code>RewriteEngine on\nRewriteCond %{HTTP:Authorization} ^(.*)\nRewriteRule ^(.*) - [E=HTTP_AUTHORIZATION:%1]\n<\/code><\/pre>\n\n<h4>WP Engine<\/h4>\n\n<p>Add the following to your <code>.htaccess<\/code> file:<\/p>\n\n<pre><code>SetEnvIf Authorization \"(.*)\" HTTP_AUTHORIZATION=$1\n<\/code><\/pre>\n\n<h3>Configuration<\/h3>\n\n<p>Simple JWT Auth uses a <strong>Key-Encryption-Key (KEK)<\/strong> to encrypt and decrypt the JWT signing keys (<code>secret_key<\/code>, <code>private_key<\/code>, and <code>public_key<\/code>) at rest. Define it in <code>wp-config.php<\/code> with the <code>SIMPLE_JWT_AUTH_ENCRYPT_KEY<\/code> constant. The KEK must be exactly 32 characters long and must never be revealed.<\/p>\n\n<pre><code>define( 'SIMPLE_JWT_AUTH_ENCRYPT_KEY', 'your-32-char-encryption-key' );\n<\/code><\/pre>\n\n<p>Rotating the KEK invalidates the stored signing keys and requires re-entering them in the plugin settings (a <code>simplejwt_kek_mismatch<\/code> error is returned until then).<\/p>\n\n<h4>Signing keys via wp-config.php constants<\/h4>\n\n<p>Instead of storing signing keys in the database, define them directly in <code>wp-config.php<\/code> file. Constants take precedence over the plugin settings, and their values are used as-is (plaintext, not encrypted).<\/p>\n\n<pre><code>define( 'SIMPLE_JWT_AUTH_ALGORITHM', 'HS256' );            \/\/ HS256, HS384, HS512, RS256, RS384, RS512, ES256 or ES384.\ndefine( 'SIMPLE_JWT_AUTH_SECRET_KEY', 'your-secret-key' ); \/\/ Required for HS* algorithms (min 32 chars).\ndefine( 'SIMPLE_JWT_AUTH_PRIVATE_KEY', '-----BEGIN PRIVATE KEY-----...' ); \/\/ Required for RS*\/ES* signing.\ndefine( 'SIMPLE_JWT_AUTH_PUBLIC_KEY', '-----BEGIN PUBLIC KEY-----...' );   \/\/ Required for RS*\/ES* verification.\n<\/code><\/pre>\n\n<ul>\n<li><code>SIMPLE_JWT_AUTH_ALGORITHM<\/code> \u2014 overrides <code>algorithm<\/code>, the JWT signing algorithm.<\/li>\n<li><code>SIMPLE_JWT_AUTH_SECRET_KEY<\/code> \u2014 overrides <code>secret_key<\/code>, used for symmetric (HS256\/384\/512) signing and verification.<\/li>\n<li><code>SIMPLE_JWT_AUTH_PRIVATE_KEY<\/code> \u2014 overrides <code>private_key<\/code>, used for asymmetric (RSA\/EC) signing.<\/li>\n<li><code>SIMPLE_JWT_AUTH_PUBLIC_KEY<\/code> \u2014 overrides <code>public_key<\/code>, used for asymmetric (RSA\/EC) verification.<\/li>\n<\/ul>\n\n<p>When a constant is defined, the matching field on the Settings page is disabled and marked \"Defined in wp-config.php\".<\/p>\n\n<h4>Enabling authentication<\/h4>\n\n<p>For a fresh install, authentication is disabled by default. Turn on <strong>Enable JWT<\/strong> in the plugin settings, choose an algorithm, and provide the required signing key(s) before issuing tokens.<\/p>\n\n<h3>REST Endpoints<\/h3>\n\n<p>The plugin registers the <code>auth\/v1<\/code> namespace with five endpoints:<\/p>\n\n<ul>\n<li><code>POST \/wp-json\/auth\/v1\/token<\/code> \u2014 Authenticate credentials; return an access token and a refresh token.<\/li>\n<li><code>POST \/wp-json\/auth\/v1\/token\/refresh<\/code> \u2014 Rotate an access token (and refresh token) using a refresh token.<\/li>\n<li><code>POST \/wp-json\/auth\/v1\/token\/revoke<\/code> \u2014 Revoke a refresh token and its rotation family.<\/li>\n<li><code>POST \/wp-json\/auth\/v1\/token\/validate<\/code> \u2014 Validate an access token.<\/li>\n<li><code>GET \/wp-json\/auth\/v1\/me<\/code> \u2014 Return the authenticated user's profile.<\/li>\n<\/ul>\n\n<h4>Generate a token<\/h4>\n\n<p>Submit a <code>POST<\/code> request with <code>username<\/code> and <code>password<\/code>:<\/p>\n\n<pre><code>curl --location 'https:\/\/example.com\/wp-json\/auth\/v1\/token' \\\n--header 'Content-Type: application\/json' \\\n--data-raw '{\n    \"username\": \"wordpress_username\",\n    \"password\": \"wordpress_password\"\n}'\n<\/code><\/pre>\n\n<p>Success response:<\/p>\n\n<pre><code>{\n    \"code\": \"simplejwt_auth_credential\",\n    \"message\": \"Token created successfully\",\n    \"data\": {\n        \"status\": 200,\n        \"id\": \"2\",\n        \"email\": \"user@example.com\",\n        \"nicename\": \"username\",\n        \"display_name\": \"User Name\",\n        \"token\": \"eyJ0eXAiOiJKV1QiLCJhbGciOi...\",\n        \"token_expires_in\": 900,\n        \"refresh_token\": \"opaque-refresh-token\",\n        \"refresh_expires_in\": 1209600\n    }\n}\n<\/code><\/pre>\n\n<p>Store the access token and refresh token in your application (a secure cookie, <code>localStorage<\/code>, or a wrapper such as <a href=\"https:\/\/localforage.github.io\/localForage\/\">localForage<\/a>). Then pass the access token as a Bearer header on every protected request:<\/p>\n\n<pre><code>Authorization: Bearer your-access-token\n<\/code><\/pre>\n\n<p>For example, creating a post with an access token:<\/p>\n\n<pre><code>curl --location 'https:\/\/example.com\/wp-json\/wp\/v2\/posts' \\\n--header 'Content-Type: application\/json' \\\n--header 'Authorization: Bearer eyJ0eXAiOiJKV1QiLCJhbGciOi...' \\\n--data '{\n    \"title\": \"Hello headless\",\n    \"content\": \"Created through the REST API with JWT authentication.\",\n    \"status\": \"publish\"\n}'\n<\/code><\/pre>\n\n<h4>Refresh a token<\/h4>\n\n<p>Access tokens are short-lived. When one expires, send the refresh token to <code>\/token\/refresh<\/code> (in the body or as a Bearer header) to rotate it and receive a new access token and refresh token:<\/p>\n\n<pre><code>curl --location 'https:\/\/example.com\/wp-json\/auth\/v1\/token\/refresh' \\\n--header 'Content-Type: application\/json' \\\n--data-raw '{ \"refresh_token\": \"opaque-refresh-token\" }'\n<\/code><\/pre>\n\n<p>The response has the same shape as the token response. Each rotation invalidates the previous refresh token.<\/p>\n\n<h4>Revoke a token<\/h4>\n\n<p>To invalidate a session, send the refresh token to <code>\/token\/revoke<\/code>:<\/p>\n\n<pre><code>curl --location 'https:\/\/example.com\/wp-json\/auth\/v1\/token\/revoke' \\\n--header 'Content-Type: application\/json' \\\n--data-raw '{ \"refresh_token\": \"opaque-refresh-token\" }'\n<\/code><\/pre>\n\n<p>Success response:<\/p>\n\n<pre><code>{\n    \"code\": \"simplejwt_token_revoked\",\n    \"message\": \"Token has been revoked\",\n    \"data\": { \"status\": 200 }\n}\n<\/code><\/pre>\n\n<h4>Validate a token<\/h4>\n\n<p>Verify an access token with a <code>POST<\/code> request carrying the Bearer header:<\/p>\n\n<pre><code>curl --location --request POST 'https:\/\/example.com\/wp-json\/auth\/v1\/token\/validate' \\\n--header 'Authorization: Bearer eyJ0eXAiOiJKV1QiLCJhbGciOi...'\n<\/code><\/pre>\n\n<p>Success response:<\/p>\n\n<pre><code>{\n    \"code\": \"simplejwt_valid_token\",\n    \"message\": \"Token is valid\",\n    \"data\": { \"status\": 200 }\n}\n<\/code><\/pre>\n\n<h4>Current user<\/h4>\n\n<p>Get the authenticated user's profile:<\/p>\n\n<pre><code>curl --location 'https:\/\/example.com\/wp-json\/auth\/v1\/me' \\\n--header 'Authorization: Bearer eyJ0eXAiOiJKV1QiLCJhbGciOi...'\n<\/code><\/pre>\n\n<p>Success response:<\/p>\n\n<pre><code>{\n    \"code\": \"simplejwt_user\",\n    \"message\": \"User data retrieved successfully\",\n    \"data\": {\n        \"status\": 200,\n        \"id\": 2,\n        \"email\": \"user@example.com\",\n        \"nicename\": \"username\",\n        \"display_name\": \"User Name\",\n        \"roles\": [\"administrator\"]\n    }\n}\n<\/code><\/pre>\n\n<h3>REST Errors<\/h3>\n\n<p>Every error returns a consistent envelope with a stable <code>code<\/code>, a <code>message<\/code>, and a <code>data.status<\/code> HTTP status. Common codes include:<\/p>\n\n<ul>\n<li><code>simplejwt_missing_credentials<\/code> \u2014 Username or password is missing.<\/li>\n<li><code>simplejwt_invalid_username<\/code> \u2014 The username is not registered on this site.<\/li>\n<li><code>simplejwt_incorrect_password<\/code> \u2014 The password is incorrect.<\/li>\n<li><code>simplejwt_no_auth_header<\/code> \u2014 The Authorization header is missing.<\/li>\n<li><code>simplejwt_bad_auth_header<\/code> \u2014 The Authorization header is malformed.<\/li>\n<li><code>simplejwt_invalid_token<\/code> \u2014 The access token is invalid (bad signature, malformed, or not yet valid).<\/li>\n<li><code>simplejwt_expired_token<\/code> \u2014 The access or refresh token has expired.<\/li>\n<li><code>simplejwt_invalid_refresh_token<\/code> \u2014 The refresh token is unknown or invalid.<\/li>\n<li><code>simplejwt_reused_refresh_token<\/code> \u2014 A rotated refresh token was reused; the token family was revoked.<\/li>\n<li><code>simplejwt_revoked_token<\/code> \u2014 The token has been revoked.<\/li>\n<li><code>simplejwt_bad_issuer<\/code> \u2014 The token issuer does not match this server.<\/li>\n<li><code>simplejwt_unsupported_algorithm<\/code> \u2014 The configured signing algorithm is unsupported.<\/li>\n<li><code>simplejwt_rate_limited<\/code> \u2014 Too many requests; please try again later.<\/li>\n<li><code>simplejwt_bad_config<\/code> \u2014 JWT authentication is not configured or is disabled.<\/li>\n<li><code>simplejwt_bad_encryption_key<\/code> \u2014 The key-encryption-key is not configured.<\/li>\n<li><code>simplejwt_invalid_enckey_length<\/code> \u2014 The key-encryption-key is not exactly 32 characters.<\/li>\n<li><code>simplejwt_kek_mismatch<\/code> \u2014 The key-encryption-key was rotated; re-enter the signing keys.<\/li>\n<\/ul>\n\n<h3>Available Hooks<\/h3>\n\n<p>Simple JWT Auth is developer-friendly and exposes filter and action hooks to override its default behaviour.<\/p>\n\n<h4>simplejwt_cors_allow_headers (filter)<\/h4>\n\n<p>Modify the CORS <code>Access-Control-Allow-Headers<\/code> value. Default: <code>Access-Control-Allow-Headers, Content-Type, Authorization<\/code>.<\/p>\n\n<pre><code>add_filter( 'simplejwt_cors_allow_headers', function ( $headers ) {\n    return $headers;\n} );\n<\/code><\/pre>\n\n<h4>simplejwt_auth_iss (filter)<\/h4>\n\n<p>Change the token <code>iss<\/code> (issuer) claim. Default: <code>get_bloginfo( 'url' )<\/code>.<\/p>\n\n<pre><code>add_filter( 'simplejwt_auth_iss', function ( $iss ) {\n    return $iss;\n} );\n<\/code><\/pre>\n\n<h4>simplejwt_not_before (filter)<\/h4>\n\n<p>Change the token <code>nbf<\/code> (not-before) claim. Default: the issue time.<\/p>\n\n<pre><code>add_filter( 'simplejwt_not_before', function ( $not_before, $issued_at ) {\n    return $not_before;\n}, 10, 2 );\n<\/code><\/pre>\n\n<h4>simplejwt_auth_expire (filter)<\/h4>\n\n<p>Change the token <code>exp<\/code> (expiry) claim. Default: <code>time() + access token lifetime<\/code> (900 seconds by default).<\/p>\n\n<pre><code>add_filter( 'simplejwt_auth_expire', function ( $expire, $issued_at ) {\n    return $expire;\n}, 10, 2 );\n<\/code><\/pre>\n\n<h4>simplejwt_payload_before_sign (filter)<\/h4>\n\n<p>Modify the JWT payload before it is signed. The payload contains the <code>iss<\/code>, <code>iat<\/code>, <code>nbf<\/code>, <code>exp<\/code>, <code>sub<\/code>, and <code>jti<\/code> claims (plus the legacy <code>data.user.id<\/code>).<\/p>\n\n<pre><code>add_filter( 'simplejwt_payload_before_sign', function ( $payload, $user ) {\n    return $payload;\n}, 10, 2 );\n<\/code><\/pre>\n\n<h4>simplejwt_token_before_dispatch (filter)<\/h4>\n\n<p>Modify the token response before it is returned to the client. The response includes the access token, refresh token, and their lifetimes.<\/p>\n\n<pre><code>add_filter( 'simplejwt_token_before_dispatch', function ( $data, $user ) {\n    return $data;\n}, 10, 2 );\n<\/code><\/pre>\n\n<h4>simplejwt_auth_token_reuse_detected (action)<\/h4>\n\n<p>Fired when refresh-token reuse is detected and a token family is revoked. Arguments: <code>$user_id<\/code>, <code>$family_id<\/code>, <code>$ip<\/code>.<\/p>\n\n<pre><code>add_action( 'simplejwt_auth_token_reuse_detected', function ( $user_id, $family_id, $ip ) {\n    \/\/ Alert, log, or revoke further sessions here.\n}, 10, 3 );\n<\/code><\/pre>\n\n<h4>simplejwt_rate_limit_max (filter)<\/h4>\n\n<p>Change the maximum number of attempts allowed within the rate-limit window. Default: <code>10<\/code>.<\/p>\n\n<pre><code>add_filter( 'simplejwt_rate_limit_max', function ( $max ) {\n    return $max;\n} );\n<\/code><\/pre>\n\n<h4>simplejwt_rate_limit_window (filter)<\/h4>\n\n<p>Change the rate-limit window, in seconds. Default: <code>MINUTE_IN_SECONDS<\/code> (60).<\/p>\n\n<pre><code>add_filter( 'simplejwt_rate_limit_window', function ( $window ) {\n    return $window;\n} );\n<\/code><\/pre>\n\n<h3>Postman Collection<\/h3>\n\n<p>A ready-to-use Postman collection is bundled with the plugin. Open <strong>Simple JWT Auth \u2192 Documentation<\/strong> in your WordPress admin and click <strong>Download Postman Collection<\/strong>, then import the JSON into Postman. The collection preconfigures your site URL and includes the token, refresh, revoke, validate, and <code>\/me<\/code> requests.<\/p>\n\n<!--section=installation-->\n<h4>Using FTP<\/h4>\n\n<ol>\n<li>Download the plugin from <a href=\"https:\/\/downloads.wordpress.org\/plugin\/simple-jwt-auth.zip\">here<\/a>.<\/li>\n<li>Unzip the <code>simple-jwt-auth.zip<\/code> file.<\/li>\n<li>Upload the <code>simple-jwt-auth<\/code> folder to the <code>\/wp-content\/plugins\/<\/code> directory.<\/li>\n<li>Activate the plugin through the Plugins dashboard.<\/li>\n<\/ol>\n\n<h4>Uploading from the dashboard<\/h4>\n\n<ol>\n<li>Download the plugin from <a href=\"https:\/\/downloads.wordpress.org\/plugin\/simple-jwt-auth.zip\">here<\/a>.<\/li>\n<li>In the dashboard, go to Plugins \u2192 Add New Plugin.<\/li>\n<li>Click Upload Plugin.<\/li>\n<li>Select the <code>simple-jwt-auth.zip<\/code> file.<\/li>\n<li>Click Install Now.<\/li>\n<li>Activate the plugin through the Plugins dashboard.<\/li>\n<\/ol>\n\n<!--section=faq-->\n<dl>\n<dt id=\"where%20can%20i%20find%20the%20source%20code%3F\"><h3>Where can I find the source code?<\/h3><\/dt>\n<dd><p>Simple JWT Auth is open source. Visit the <a href=\"https:\/\/github.com\/sayandey18\/simple-jwt-auth\">GitHub repository<\/a> and consider giving it a star.<\/p><\/dd>\n<dt id=\"how%20can%20i%20contribute%3F\"><h3>How can I contribute?<\/h3><\/dt>\n<dd><p>Thank you \u2014 contributions are welcome. See the <a href=\"https:\/\/github.com\/sayandey18\/simple-jwt-auth\">GitHub repository<\/a> for details.<\/p><\/dd>\n<dt id=\"where%20can%20i%20report%20a%20bug%3F\"><h3>Where can I report a bug?<\/h3><\/dt>\n<dd><p>Submit a ticket in the <a href=\"https:\/\/wordpress.org\/support\/plugin\/simple-jwt-auth\/\">WordPress support forum<\/a> or, for developers, <a href=\"https:\/\/github.com\/sayandey18\/simple-jwt-auth\/issues\">create a GitHub issue<\/a>.<\/p><\/dd>\n<dt id=\"why%20do%20i%20get%20%22encryption%20key%20is%20not%20configured%20properly%22%3F\"><h3>Why do I get \"Encryption key is not configured properly\"?<\/h3><\/dt>\n<dd><p>The <code>SIMPLE_JWT_AUTH_ENCRYPT_KEY<\/code> constant is missing from <code>wp-config.php<\/code>. Add it with a value that is exactly 32 characters long.<\/p><\/dd>\n<dt id=\"does%20this%20plugin%20work%20with%20react%2C%20next.js%2C%20vue%2C%20or%20mobile%20apps%3F\"><h3>Does this plugin work with React, Next.js, Vue, or mobile apps?<\/h3><\/dt>\n<dd><p>Yes. Any client that can make HTTP requests and send a <code>Authorization: Bearer &lt;token&gt;<\/code> header can authenticate against the REST API.<\/p><\/dd>\n\n<\/dl>\n\n<!--section=changelog-->\n<h4>2.0.0<\/h4>\n\n<ul>\n<li>Added refresh tokens with rotation and revocation (POST \/token\/refresh, POST \/token\/revoke).<\/li>\n<li>Added the \/auth\/v1\/me endpoint to return the authenticated user profile.<\/li>\n<li>Added access token and refresh token lifetime settings.<\/li>\n<li>Added rate limiting to the token, refresh, and revoke endpoints.<\/li>\n<li>Enforced the enable_auth setting, auto-migrating existing configurations to stay enabled.<\/li>\n<li>Updated firebase\/php-jwt to ^7.1 and raised requirements to WordPress 7.0 \/ PHP 8.2.<\/li>\n<li>Corrected the supported algorithms list (HS256, HS384, HS512, RS256, RS384, RS512, ES256, ES384).<\/li>\n<li>Security hardening: opaque refresh tokens stored SHA-256 hashed, reuse detection, and revocation on logout and password reset.<\/li>\n<\/ul>\n\n<h4>1.0.2<\/h4>\n\n<ul>\n<li>Tested up to WordPress 6.7.<\/li>\n<\/ul>\n\n<h4>1.0.1<\/h4>\n\n<ul>\n<li>Disabled direct file access.<\/li>\n<li>Fixed the undefined variable notice in the admin area.<\/li>\n<li>Bug fixes and improvements.<\/li>\n<\/ul>\n\n<h4>1.0.0<\/h4>\n\n<ul>\n<li>Initial release.<\/li>\n<\/ul>","raw_excerpt":"JWT authentication for the WordPress REST API: access and refresh tokens with rotation, revocation, and validation for headless and decoupled apps.","jetpack_sharing_enabled":true,"_links":{"self":[{"href":"https:\/\/frp.wordpress.org\/plugins\/wp-json\/wp\/v2\/plugin\/207856","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/frp.wordpress.org\/plugins\/wp-json\/wp\/v2\/plugin"}],"about":[{"href":"https:\/\/frp.wordpress.org\/plugins\/wp-json\/wp\/v2\/types\/plugin"}],"replies":[{"embeddable":true,"href":"https:\/\/frp.wordpress.org\/plugins\/wp-json\/wp\/v2\/comments?post=207856"}],"author":[{"embeddable":true,"href":"https:\/\/frp.wordpress.org\/plugins\/wp-json\/wporg\/v1\/users\/sayandey18"}],"wp:attachment":[{"href":"https:\/\/frp.wordpress.org\/plugins\/wp-json\/wp\/v2\/media?parent=207856"}],"wp:term":[{"taxonomy":"plugin_section","embeddable":true,"href":"https:\/\/frp.wordpress.org\/plugins\/wp-json\/wp\/v2\/plugin_section?post=207856"},{"taxonomy":"plugin_tags","embeddable":true,"href":"https:\/\/frp.wordpress.org\/plugins\/wp-json\/wp\/v2\/plugin_tags?post=207856"},{"taxonomy":"plugin_category","embeddable":true,"href":"https:\/\/frp.wordpress.org\/plugins\/wp-json\/wp\/v2\/plugin_category?post=207856"},{"taxonomy":"plugin_contributors","embeddable":true,"href":"https:\/\/frp.wordpress.org\/plugins\/wp-json\/wp\/v2\/plugin_contributors?post=207856"},{"taxonomy":"plugin_business_model","embeddable":true,"href":"https:\/\/frp.wordpress.org\/plugins\/wp-json\/wp\/v2\/plugin_business_model?post=207856"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}