Skip to content

Commit 5afb940

Browse files
authored
Merge pull request #3 from simple-jwt-login/website-redesign
Website redesign V2
2 parents 635275a + e076e3b commit 5afb940

72 files changed

Lines changed: 8336 additions & 1555 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

api/simple-jwt-login.info.mdx

Lines changed: 24 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
---
22
id: simple-jwt-login
33
title: "Simple-JWT-Login"
4-
description: "This is the Simple-JWT-Login WordPress plugin API Documentation."
4+
description: "Complete REST API reference for Simple JWT Login — a free WordPress plugin for JWT authentication. Covers login, register, token refresh, auto-login, endpoint protection, and more."
55
sidebar_label: Introduction
66
sidebar_position: 0
77
hide_title: true
@@ -34,24 +34,36 @@ import Export from "@theme/ApiExplorer/Export";
3434

3535
</ApiLogo>
3636

37-
This is the Simple-JWT-Login WordPress plugin API Documentation.
37+
REST API reference for the **Simple-JWT-Login** WordPress plugin — add token-based authentication to your WordPress site without writing a single line of code.
3838

39-
## Introduction
39+
## Base URL
4040

41-
This API is documented in **OpenAPI format**. You can either open it in [swagger editor](https://editor.swagger.io/) or you can view it on this website.
41+
All endpoints are available via two URL formats:
4242

43-
Also, you open it in postman, and you will be able to do local tests.
43+
| Mode | Pattern |
44+
|------|---------|
45+
| Pretty permalinks | `https://{domain}/wp-json/simple-jwt-login/v1/{endpoint}` |
46+
| Query string | `https://{domain}/?rest_route=/simple-jwt-login/v1/{endpoint}` |
4447

45-
## Server
48+
## Authentication
4649

47-
You can access the Simple-JWT-Login API in two modes:
48-
- using permalink: https://\{domain\}/wp-json/simple-jwt-login/v1/\{endpoint\}
49-
- using rest_route: https://\{domain\}/?rest_route=/simple-jwt-login/v1/\{endpoint\}
50+
Most write endpoints accept a `JWT` token delivered in one of four ways:
51+
52+
- **Authorization header**`Authorization: Bearer <token>`
53+
- **Query parameter**`?JWT=<token>`
54+
- **Request body**`{"JWT": "<token>"}`
55+
- **Cookie / Session** — configurable in plugin settings
5056

5157
## Cross-Origin Resource Sharing
52-
This API features Cross-Origin Resource Sharing (CORS) implemented in compliance with [W3C spec](https://www.w3.org/TR/cors/).
53-
And that allows cross-domain communication from the browser.
54-
All responses have a wildcard same-origin which makes them completely public and accessible to everyone, including any code on any site.
58+
59+
This API is CORS-enabled in compliance with the [W3C spec](https://www.w3.org/TR/cors/).
60+
61+
All responses include a wildcard `Access-Control-Allow-Origin: *` header, making the API accessible from any browser, SPA, or mobile app.
62+
63+
## Try it out
64+
65+
- **Swagger Editor** — paste the spec at [editor.swagger.io](https://editor.swagger.io/)
66+
- **Postman** — import `openapi.yaml` directly for a ready-made collection with local test support
5567

5668

5769
<div
Lines changed: 268 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,268 @@
1+
---
2+
title: "Headless WordPress in 2026: JWT Authentication Done Right with Simple JWT Login"
3+
description: Learn how to add production-ready JWT authentication to your headless WordPress setup using Simple JWT Login — from token generation to protected endpoints and password resets.
4+
slug: /headless-wordpress-jwt-authentication/
5+
hide_table_of_contents: false
6+
authors: nicumicle
7+
tags: [tutorials, headless-wordpress, jwt-authentication, security]
8+
keywords: [headless WordPress, JWT authentication, WordPress REST API, Simple JWT Login, token-based auth, headless CMS, Next.js WordPress, React WordPress]
9+
image: /assets/favicons/android-chrome-192x192.png
10+
---
11+
12+
Headless WordPress has gone mainstream. Teams reach for it when they want WordPress's content management experience paired with a modern front-end — React, Next.js, Vue, or a mobile app. The REST API makes that possible, but it leaves one critical piece unresolved: **authentication**.
13+
14+
WordPress's built-in auth is cookie-based and browser-centric. It doesn't translate cleanly to API-first architectures. That's the gap Simple JWT Login fills, and in this article I'll walk through a complete, realistic setup.
15+
16+
<!--truncate-->
17+
18+
## What We're Building
19+
20+
A headless WordPress backend with:
21+
22+
- JWT-based login (email + password → token)
23+
- Token refresh before expiry
24+
- Protected REST endpoints that require a valid JWT
25+
- User registration via API
26+
- Password reset flow
27+
- Auto-login links for email campaigns
28+
29+
All of this is handled by [Simple JWT Login](https://wordpress.org/plugins/simple-jwt-login/) with zero custom PHP beyond optional hooks.
30+
31+
---
32+
33+
## Installation and Initial Configuration
34+
35+
Install Simple JWT Login from the WordPress plugin repository, then navigate to **Simple JWT Login** in your WordPress admin sidebar.
36+
37+
The first setting to configure is the **JWT Decryption Key** — this is the secret used to sign and verify tokens. Treat it like a database password: long, random, and stored in a secrets manager rather than hardcoded.
38+
39+
```
40+
Settings > Simple JWT Login > General > JWT Decryption Key
41+
```
42+
43+
Next, select your **algorithm**. For most setups `HS256` (HMAC SHA-256) is the right default. If you need asymmetric signing — for example, to let a third-party service verify tokens without knowing the secret — switch to `RS256` and configure your public/private key pair.
44+
45+
Set a reasonable **JWT expiration time**. Sixty minutes is a sensible starting point for most web apps; mobile apps often use longer windows paired with token refresh.
46+
47+
---
48+
49+
## Generating a Token
50+
51+
Once the plugin is active, your WordPress site immediately has an authentication endpoint:
52+
53+
```bash
54+
curl -X POST "https://example.com/wp-json/simple-jwt-login/v1/auth" \
55+
-H "Content-Type: application/json" \
56+
-d '{
57+
"email": "user@example.com",
58+
"password": "their_password"
59+
}'
60+
```
61+
62+
A successful response looks like this:
63+
64+
```json
65+
{
66+
"success": true,
67+
"data": {
68+
"jwt": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...",
69+
"user": {
70+
"ID": 42,
71+
"user_email": "user@example.com",
72+
"display_name": "Jane Doe"
73+
}
74+
}
75+
}
76+
```
77+
78+
Store this token client-side (memory or an `HttpOnly` cookie — avoid `localStorage` for sensitive apps) and attach it to subsequent requests.
79+
80+
The plugin also supports **username** and a combined **login field** (email or username) for the initial auth call, configurable under `General > Login by`.
81+
82+
---
83+
84+
## Making Authenticated Requests
85+
86+
Once you have a token, include it in the `Authorization` header on every protected request:
87+
88+
```bash
89+
curl "https://example.com/wp-json/wp/v2/users/me" \
90+
-H "Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9..."
91+
```
92+
93+
The plugin also accepts the token as a query parameter (`?JWT=<token>`) or in the request body (`{"JWT": "<token>"}`), which is handy for scenarios where setting custom headers is awkward — like certain webhook consumers.
94+
95+
---
96+
97+
## Refreshing Tokens
98+
99+
Short-lived tokens are more secure, but they require your client to handle expiry gracefully. Simple JWT Login provides a dedicated refresh endpoint:
100+
101+
```bash
102+
curl -X POST "https://example.com/wp-json/simple-jwt-login/v1/auth/refresh" \
103+
-H "Content-Type: application/json" \
104+
-d '{"JWT": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9..."}'
105+
```
106+
107+
The response is a new token with a fresh expiry. A typical front-end pattern: check the token's `exp` claim before each request, and if it's within a few minutes of expiry, refresh proactively rather than reacting to a 401.
108+
109+
---
110+
111+
## Protecting Your REST Endpoints
112+
113+
A headless WordPress site often exposes more REST routes than intended. Simple JWT Login's **Endpoint Protection** feature lets you require a valid JWT for any route.
114+
115+
The most secure posture for a private API: enable **"Protect all endpoints"** and then whitelist only the routes that must be public.
116+
117+
```
118+
Settings > Simple JWT Login > Protect Endpoints
119+
Mode: Protect all WordPress Endpoints
120+
```
121+
122+
For a typical blog-style API you might whitelist:
123+
124+
- `GET /wp-json/wp/v2/posts` — public post listing
125+
- `GET /wp-json/wp/v2/categories` — taxonomy data
126+
- `POST /wp-json/simple-jwt-login/v1/auth` — login itself
127+
128+
Every other route — user data, media uploads, post creation — requires a valid JWT.
129+
130+
You can also protect routes **by HTTP method**. If your front-end needs `GET` on posts to be public but `POST` (creating posts) to be authenticated, that's a single checkbox per method. No custom middleware needed.
131+
132+
---
133+
134+
## User Registration via API
135+
136+
If your application handles its own onboarding flow, you can create WordPress users directly through the API. First, enable registration under:
137+
138+
```
139+
Settings > Simple JWT Login > Register User
140+
```
141+
142+
For security, always pair registration with an **Auth Code** — an API key that must accompany registration requests. This prevents anyone with your API URL from creating arbitrary accounts.
143+
144+
```bash
145+
curl -X POST "https://example.com/wp-json/simple-jwt-login/v1/users" \
146+
-H "Content-Type: application/json" \
147+
-d '{
148+
"email": "newuser@example.com",
149+
"password": "initial_password",
150+
"first_name": "Jane",
151+
"last_name": "Doe",
152+
"auth_code": "YOUR_REGISTRATION_AUTH_CODE"
153+
}'
154+
```
155+
156+
You can also configure the plugin to **generate a random password** and return it in the response — useful if you want to issue a temporary password and immediately prompt the user to change it.
157+
158+
IP address restrictions and email domain allowlists add further layers of control over who can register.
159+
160+
---
161+
162+
## Password Reset Flow
163+
164+
The plugin ships a full password reset flow accessible via API, which is often missing from DIY JWT implementations.
165+
166+
**Step 1 — Request a reset code:**
167+
168+
```bash
169+
curl -X POST "https://example.com/wp-json/simple-jwt-login/v1/users/reset_password" \
170+
-H "Content-Type: application/json" \
171+
-d '{"email": "user@example.com"}'
172+
```
173+
174+
**Step 2 — Submit the new password with the code:**
175+
176+
```bash
177+
curl -X PUT "https://example.com/wp-json/simple-jwt-login/v1/users/reset_password" \
178+
-H "Content-Type: application/json" \
179+
-d '{
180+
"email": "user@example.com",
181+
"code": "RESET_CODE_FROM_EMAIL",
182+
"new_password": "new_secure_password"
183+
}'
184+
```
185+
186+
Three reset modes are available:
187+
188+
- **Silent**: The reset code is returned directly in the API response (for custom email delivery).
189+
- **Default WordPress email**: Uses WordPress's built-in email template.
190+
- **Custom email template**: You define the subject and body using variables like `{{CODE}}`, `{{NAME}}`, and `{{EMAIL}}`.
191+
192+
The custom template mode is ideal for headless apps that have their own transactional email design system and don't want WordPress's default styling.
193+
194+
---
195+
196+
## Auto-Login Links for Email Campaigns
197+
198+
One of the most underrated features: generate a URL that logs a user in automatically when clicked.
199+
200+
Enable auto-login under:
201+
202+
```
203+
Settings > Simple JWT Login > Auto Login
204+
```
205+
206+
Then generate a JWT for the target user and construct the URL:
207+
208+
```
209+
https://example.com/?JWT=<user_jwt>&redirectUrl={{site_url}}/account/dashboard
210+
```
211+
212+
When the user clicks that link, Simple JWT Login authenticates them silently and redirects them to their dashboard — already logged in.
213+
214+
The `redirectUrl` parameter supports dynamic variables (`{{user_id}}`, `{{user_email}}`, `{{user_first_name}}`, and more), so each link can route the user to a personalized destination. Pair this with the **MailPoet add-on** and you have one-click autologin directly inside email campaigns without a single line of custom code.
215+
216+
---
217+
218+
## Enriching the JWT with Custom Data
219+
220+
Out of the box, the JWT payload contains standard claims (`sub`, `iat`, `exp`). For most front-ends you'll want to include additional user data to avoid extra API calls after login.
221+
222+
Use the `simple_jwt_login_jwt_payload` filter:
223+
224+
```php
225+
add_filter('simple_jwt_login_jwt_payload', function($payload, $user) {
226+
$payload['display_name'] = $user->display_name;
227+
$payload['roles'] = $user->roles;
228+
$payload['avatar'] = get_avatar_url($user->ID);
229+
return $payload;
230+
}, 10, 2);
231+
```
232+
233+
Your front-end can now decode the JWT and immediately render the user's name and avatar without an additional `/users/me` request.
234+
235+
---
236+
237+
## Revoking Tokens
238+
239+
When a user logs out or changes their password, you'll want to invalidate their existing tokens. Simple JWT Login provides a revoke endpoint:
240+
241+
```bash
242+
curl -X DELETE "https://example.com/wp-json/simple-jwt-login/v1/auth/revoke" \
243+
-H "Authorization: Bearer <token>"
244+
```
245+
246+
Revoked tokens are blacklisted server-side and will be rejected on subsequent requests, even if they haven't technically expired yet.
247+
248+
---
249+
250+
## A Note on CORS
251+
252+
If your front-end is served from a different domain than WordPress — which is almost always the case in headless setups — you'll need CORS headers. Simple JWT Login can add `Access-Control-Allow-Origin: *` automatically:
253+
254+
```
255+
Settings > Simple JWT Login > General > Allow CORS
256+
```
257+
258+
For production, you'll typically want to restrict this to your front-end domain via your web server config (Apache or Nginx), using the plugin's CORS setting as a development convenience only.
259+
260+
---
261+
262+
## Conclusion
263+
264+
Simple JWT Login takes what would otherwise be hundreds of lines of custom authentication code and turns it into a configuration exercise. Token generation, refresh, revocation, endpoint protection, user registration, password resets, and auto-login are all covered out of the box.
265+
266+
For teams building headless WordPress — whether the front-end is React, Next.js, Vue, or a native mobile app — it's one of the highest-leverage plugins available. The [documentation](/docs/) is thorough, the plugin is actively maintained, and the hooks system means you're never painted into a corner when requirements get complex.
267+
268+
Install it, spend an hour on the settings, and your WordPress REST API will have authentication that actually fits modern development patterns.

0 commit comments

Comments
 (0)