Installing and Configuring the JWT Bundle
Installing and Configuring the JWT Bundle
~15 Min. Lesezeit Zuletzt aktualisiert am August 8, 2026
lexik/jwt-authentication-bundle is the API-Platform-typical path to JWT authentication – IN CONTRAST to the session-based authentication of the Symfony course (SENSIBLE there for a server-rendered frontend, UNSUITABLE here for a stateless API).
Installing the bundle
docker compose exec php composer require lexik/jwt-authentication-bundleThe api-platform distribution usually comes with this bundle ALREADY pre-installed – if NOT, the command installs it AFTERWARD.
Generating keys
docker compose exec php bin/console lexik:jwt:generate-keypairGenerates a PRIVATE/PUBLIC key pair under config/jwt/ – the PRIVATE key SIGNS issued tokens, the PUBLIC key VERIFIES them on every request.
Achtung: config/jwt/private.pem must NEVER go into version control – the api-platform distribution already excludes config/jwt/ in .gitignore by default.
Automating password hashing
<?php
declare(strict_types=1);
namespace App\State;
use ApiPlatform\Metadata\Operation;
use ApiPlatform\State\ProcessorInterface;
use App\Entity\User;
use Symfony\Component\PasswordHasher\Hasher\UserPasswordHasherInterface;
final class UserPasswordHasherProcessor implements ProcessorInterface
{
public function __construct(
private readonly ProcessorInterface $persistProcessor,
private readonly UserPasswordHasherInterface $passwordHasher,
) {
}
public function process(mixed $data, Operation $operation, array $uriVariables = [], array $context = []): mixed
{
if ($data instanceof User && null !== $data->getPlainPassword()) {
$data->setPassword(
$this->passwordHasher->hashPassword($data, $data->getPlainPassword())
);
$data->setPlainPassword(null);
}
return $this->persistProcessor->process($data, $operation, $uriVariables, $context);
}
}A STATE PROCESSOR (the WRITE counterpart to the state provider from chapter 5/27) – brought forward HERE as a PRACTICAL example, a COMPLETE explanation of the concept follows in block 7 (chapter 61).
// User.php
use App\State\UserPasswordHasherProcessor;
#[ApiResource(
normalizationContext: ['groups' => ['user:read']],
denormalizationContext: ['groups' => ['user:write']],
processor: UserPasswordHasherProcessor::class
)]
Tipp: $this->persistProcessor gets injected AUTOMATICALLY by API Platform with the DEFAULT Doctrine processor (dependency injection via autowiring, recognized via the interface) – the CUSTOM processor only WRAPS it, instead of REPLACING it.