jwt_authentication
JWT Authentication replaces Drupal's session-based login with a stateless JSON Web Token flow.
Decoupled front-ends, mobile applications, and API consumers authenticate once to receive a
short-lived access token and a long-lived refresh token, then call protected Drupal endpoints
without cookies or CSRF headers.
Features
========
- Three-endpoint token API — POST /jwt-authentication/api/auth/tokens (login),
POST /jwt-authentication/api/auth/refresh (rotate pair),
POST /jwt-authentication/api/auth/logout (revoke).
- Signed access + refresh token pair — access tokens carry uid, iss, iat, exp, and a unique
jti claim; refresh tokens are opaque UUIDs stored as Drupal entities.
- JTI revocation (token blacklist) — logout writes the jti claim to a database revocation
table; every subsequent request is rejected even if the token has not yet expired.
- Multi-algorithm signing — HMAC (HS256 / HS384 / HS512), RSA (RS256 / RS384 / RS512), and
ECDSA (ES256 / ES384 / ES512). Algorithm and key are swappable without touching code.
- Key module integration — private/public key material is stored and rotated through the Key
module; raw key strings are also supported for development environments.
- Brute-force flood protection — per-IP (50 attempts / hour) and per-username
(5 attempts / 5 min) counters block credential-stuffing attacks. Counters clear
automatically on successful login.
- Timing-safe username enumeration guard — a dummy bcrypt hash is checked when the username
does not exist, ensuring the response time does not reveal whether an account is registered.
- Event system — jwt_authentication.on_jwt_created, on_jwt_decoded, and on_jwt_encoded events
allow other modules to enrich payloads, audit tokens, or reject invalid ones without
patching core logic.
- Automatic expired-token cleanup — a cron queue worker (jwt_authentication_token_cleanup)
prunes expired AccessToken and RefreshToken entities in 30-second cron chunks.
- Configurable token lifetime and audience — TTL and aud claim are set in the admin UI;
no hard-coded values.
Post-Installation
=================
1. Install the module
drush en jwt_authentication
or via the Extend page (/admin/modules).
2. Create a key
Navigate to Administration > Configuration > Security > Keys and create a key of type
"JWT RSA Key" (RS256, 2048-bit minimum) or "JWT HMAC Key" (HS256 / HS384 / HS512).
3. Configure the module
Go to Administration > Configuration > System > JWT Authentication
(/admin/config/system/jwt-authentication).
Select the algorithm, choose the key, set the access token TTL and refresh token TTL,
and optionally set an audience (aud) claim.
4. Assign permissions
Grant "access jwt authentication" to the roles that should obtain and refresh tokens
(typically Authenticated user and, for public APIs, Anonymous).
Grant "use jwt authentication" to roles that need to call the logout endpoint.
"administer jwt authentication" is restricted to administrators.
5. Test the flow
Send a POST request with Content-Type: application/json and body:
{"username": "...", "password": "..."}
to /jwt-authentication/api/auth/tokens.
A successful response returns: {"token": "...", "refresh_token": "..."}
6. Protect your routes
On any route that should require a valid JWT, add the following under options in your
routing YAML:
options:
_auth: [jwt_authentication]
The authentication provider (priority 100) will validate the Bearer token from the
Authorization header on every request.
Additional Requirements
=======================
Drupal core ^9.0 || ^10.0 || ^11.0
PHP >= 8.3 Constructor property promotion, array|false union types
Key module ^1.0 Manages private/public key storage and rotation
https://www.drupal.org/project/key
lcobucci/jwt ^5.0 JWT creation, parsing, and validation (via Composer)
symfony/clock ^6.0 || ^7.0 PSR-20 clock for time-sensitive validation
PHP openssl ext — Required for RSA and ECDSA algorithms
Install PHP dependencies via Composer:
composer require lcobucci/jwt symfony/clock
Recommended Modules/Libraries
==============================
- Key Asymmetric (https://www.drupal.org/project/key)
Provides RSA key pair generation directly from the Key module UI, removing the need
to generate PEM files manually.
- CORS (https://www.drupal.org/project/cors)
If your decoupled front-end runs on a different origin, configure CORS headers so
browsers can reach the token endpoints.
- Simple OAuth (https://www.drupal.org/project/simple_oauth)
If you need a full OAuth 2.0 authorization server on top of JWT, Simple OAuth can
complement this module.
Similar Projects
================
JWT (https://www.drupal.org/project/jwt)
The official Drupal JWT module. Broader ecosystem support but delegates token lifecycle
entirely to site builders; no built-in refresh/revoke endpoints.
Simple OAuth (https://www.drupal.org/project/simple_oauth)
Full OAuth 2.0 server with authorization code, client credentials, and refresh token
grant types. Heavier setup; better suited when third-party OAuth clients are involved.
jwt_authentication differs from both by providing its own stateful refresh token store,
JTI-based revocation, flood protection, and a self-contained three-endpoint REST API
out of the box.
Supporting This Module
======================
This module is developed and maintained internally. Contributions, bug reports, and
feature requests should be submitted through the project's issue queue.
Community Documentation
=======================
API quick-start
Authenticate via POST /jwt-authentication/api/auth/tokens, then pass
Authorization: Bearer
on subsequent requests to any route protected with _auth: [jwt_authentication].
Key rotation
To rotate keys without downtime, add the new public key as an additional public key
in the Key module; the provider will try both keys on verification before the old
one is retired.
Event subscribers
Implement EventSubscriberInterface and listen on Events::JWT_CREATED to add custom
claims, or Events::JWT_DECODED to reject tokens based on application-level rules
(e.g. device fingerprinting, IP binding).