Modern JWT authentication with refresh tokens for WordPress REST API - built for SPAs and mobile apps
Unlike basic JWT plugins that use single long-lived tokens, Juanma JWT Auth Pro implements modern OAuth 2.0 security best practices with short-lived access tokens and secure refresh tokens.
| Feature | Basic JWT Plugins | Juanma JWT Auth Pro |
|---|---|---|
| Token Lifetime | Long (hours/days) | Short (1 hour) |
| Refresh Tokens | None | Secure HTTP-only |
| XSS Protection | Limited | HTTP-only cookies |
| Token Revocation | Manual only | Automatic rotation |
| Session Management | None | Database tracking |
| Security Metadata | None | IP + User Agent |
- Long-lived tokens (24h+) = Higher security risk
- No refresh mechanism = Tokens live until expiry
- XSS vulnerable = Tokens stored in localStorage
- No revocation = Can't invalidate compromised tokens
- Short-lived access tokens (1h default) = Minimal attack window
- Secure refresh tokens = HTTP-only cookies, XSS protected
- Automatic token rotation = Fresh tokens on each refresh
- Complete session control = Revoke any user session instantly
π The most secure JWT authentication plugin for WordPress.
- Simple JWT Authentication - Clean, stateless token-based auth
- HTTPOnly Refresh Tokens - Secure refresh tokens in HTTP-only cookies
- Token Rotation - Automatic refresh token rotation for enhanced security
- CORS Support - Proper cross-origin request handling
- Clean Admin Interface - Simple configuration in WordPress admin
- Developer Friendly - Clear endpoints and documentation
- Upload the plugin to
/wp-content/plugins/ - Activate through WordPress admin
- Go to Settings β WP REST Auth JWT
- Generate a JWT Secret Key (or add to wp-config.php)
- Set token expiration times if needed (or leave the defaults)
- Configure CORS origins for your frontend
// Login
const response = await fetch('/wp-json/jwt/v1/token', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({
username: 'your_username',
password: 'your_password'
})
});
const { access_token } = await response.json();
// Use token for API calls
const posts = await fetch('/wp-json/wp/v2/posts', {
headers: { 'Authorization': `Bearer ${access_token}` }
});When developing with a separate frontend (React, Vue, etc.) on a different port, the WordPress server must use HTTPS for refresh tokens to work properly:
- Cross-origin cookies require
SameSite=Noneto work across different origins SameSite=NonerequiresSecure=true(HTTPS only) per browser specifications- Without HTTPS on the server, refresh tokens stored in HttpOnly cookies will not be sent on cross-origin requests
Note: Your frontend can use either HTTP or HTTPS - only the WordPress server needs HTTPS.
WordPress Studio is the easiest way to set up a local WordPress development environment with HTTPS enabled:
- Download WordPress Studio (free from Automattic)
- Create a new site or import your existing one
- Enable HTTPS in site settings (one click)
- Configure CORS in the plugin settings for your frontend URL
Example working setup:
WordPress API: https://localhost:8881 (WordPress Studio with HTTPS) β
React App: http://localhost:5173 (Can be HTTP or HTTPS) β
Add your frontend URL to the CORS allowed origins in the plugin settings:
https://localhost:5173
https://localhost:3000
https://your-app.com
| Method | Endpoint | Description |
|---|---|---|
POST |
/wp-json/jwt/v1/token |
Login and get access token |
POST |
/wp-json/jwt/v1/refresh |
Refresh access token |
GET |
/wp-json/jwt/v1/verify |
Verify token and get user info |
POST |
/wp-json/jwt/v1/logout |
Logout and revoke refresh token |
- Stateless Authentication - JWT tokens contain all necessary information
- HTTPOnly Cookies - Refresh tokens stored securely, inaccessible to JavaScript
- Token Rotation - Refresh tokens automatically rotate on use
- Configurable Expiration - Set custom expiration times
- IP & User Agent Tracking - Additional security metadata
define('JWT_AUTH_PRO_SECRET', 'your-super-secret-key-here');
define('JWT_AUTH_PRO_ACCESS_TTL', 3600); // 1 hour
define('JWT_AUTH_PRO_REFRESH_TTL', 2592000); // 30 days
// Cookie configuration (optional)
define('JWT_AUTH_COOKIE_SAMESITE', 'Strict'); // 'Strict', 'Lax', or 'None'
define('JWT_AUTH_COOKIE_SECURE', true); // Require HTTPS
define('JWT_AUTH_COOKIE_LIFETIME', 7 * DAY_IN_SECONDS);
define('JWT_AUTH_COOKIE_AUTO_DETECT', true); // Auto-configure based on environmentGo to Settings β WP REST Auth JWT to configure:
- JWT Secret Key
- Token expiration times
- Cookie security settings (with environment auto-detection)
- CORS allowed origins
- Debug logging
The plugin automatically detects your environment and adjusts cookie settings:
- Development: Relaxed settings for local testing
- Staging: Balanced settings for testing
- Production: Maximum security settings
See Cookie Configuration Guide for advanced options using constants and filters.
Symptom: Login works but refresh token returns 401 Unauthorized
Common Causes & Solutions:
-
WordPress server using HTTP instead of HTTPS
- Solution: Enable HTTPS on WordPress (use WordPress Studio or self-signed certificates)
- Check: The
Set-Cookieheader should includeSecureattribute
-
Wrong SameSite configuration
- For cross-origin: Needs
SameSite=NonewithSecure=true - Check browser DevTools β Network β Response Headers for
Set-Cookie
- For cross-origin: Needs
-
Browser blocking third-party cookies
- Some browsers block all third-party cookies regardless of SameSite
- Solution: Use same domain/subdomain or configure exceptions
Symptom: "Access to fetch at ... has been blocked by CORS policy"
Solutions:
- Add your frontend URL to CORS allowed origins in plugin settings
- Ensure the exact origin is listed (including protocol and port)
- Check that CORS headers are present in response
This is expected! Refresh tokens use HttpOnly cookies which are:
- Not accessible via JavaScript (security feature)
- Automatically sent by the browser with requests
- Only visible in DevTools β Application β Cookies
Perfect for:
- Single Page Applications (React, Vue, Angular)
- Mobile Applications (iOS, Android)
- API Integrations (Third-party services)
- Headless WordPress (Decoupled architecture)
- Login β Get access token + refresh token (HTTPOnly cookie)
- API Calls β Use access token in Authorization header
- Token Expires β Use refresh endpoint to get new access token
- Logout β Revoke refresh token
- Cookie Configuration Guide - Environment detection, constants, and filters
- JavaScript Client Example - Full client-side implementation
- CORS and Cookies Setup - Cross-origin configuration
Run tests using the NPM scripts which leverage wp-env:
# Start environment
npm run env:start
# Install Composer deps inside container (first run)
npm run composer:install
# Unit tests
npm run test:unit
# Integration tests
npm run test:integration
# Behat E2E tests
npm run test:behat
# All tests (unit + integration + behat)
npm run testDeveloping a frontend on a different port (e.g., React on :5173, WordPress on :8884)?
The plugin automatically configures cookies for cross-origin development!
Just add your frontend origin to Settings β JWT Auth Pro β CORS Allowed Origins:
http://localhost:5173
http://localhost:3000
The plugin will automatically:
- Set
SameSite=Noneto allow cross-origin cookies - Configure cookies for HTTP localhost development
- Handle CORS headers properly
π For detailed setup instructions, see DEVELOPMENT.md
This plugin provides simple JWT authentication. If you need:
- OAuth2 authorization flows
- Scoped permissions
- Third-party app authorization
- API Proxy for enhanced security
Check out our companion plugin: WP REST Auth OAuth2
- WordPress 5.6+
- PHP 7.4+
- HTTPS (recommended for production; localhost HTTP supported for development)
GPL v2 or later
Contributions are welcome! Please feel free to submit a Pull Request.
- OAuth 2.0 Security Best Current Practice (IETF): datatracker draft
- OAuth 2.0 for Browser-Based Apps (IETF): datatracker draft
- OAuth 2.0 Token Revocation (RFC 7009): RFC 7009
- JWT storage guidance (OWASP): OWASP JWT Cheat Sheet β Where to store JWTs
- Session management guidance (OWASP): OWASP Session Management Cheat Sheet
- HttpOnly cookies not readable by JS (MDN): MDN Set-Cookie β HttpOnly
- Refresh token rotation & reuse detection (Auth0): Auth0 Docs β Refresh Token Rotation
Simple. Secure. JWT. π