I use ApiPlatform (PHP 8 / Symfony 6) to create a simple API with a JWT authentification. Authentification work correctly, I can generate a token. When I use PostMan to test a authenticated operation, no problem, I add manually the Bearer header with my token.
Now I would like to use the documentation auto generated to do this. I have created a JwtDecorator to add my login route and a security schema. Now I can add my token with the "Authorize" green button. But after, when I execute a authenticated operation, the token is not add in the header of the cUrl query. I don't understand why.
security.yaml
security:
enable_authenticator_manager: true
password_hashers:
App\Entity\User: 'auto'
providers:
app_user_provider:
entity:
class: App\Entity\User
property: email
firewalls:
dev:
pattern: ^/(_(profiler|wdt)|css|images|js)/
security: false
login:
pattern: ^/login
stateless: true
json_login:
check_path: /login
success_handler: lexik_jwt_authentication.handler.authentication_success
failure_handler: lexik_jwt_authentication.handler.authentication_failure
api:
pattern: ^/
stateless: true
jwt: ~
access_control:
- { path: ^/$, roles: PUBLIC_ACCESS } # Allows accessing the Swagger UI
- { path: ^/docs, roles: PUBLIC_ACCESS } # Allows accessing the Swagger UI docs
- { path: ^/login, roles: PUBLIC_ACCESS }
- { path: ^/, roles: PUBLIC_ACCESS }
JwtDecorator.php
<?php
namespace App\OpenApi;
use ApiPlatform\OpenApi\Factory\OpenApiFactoryInterface;
use ApiPlatform\OpenApi\OpenApi;
use ApiPlatform\OpenApi\Model;
final class JwtDecorator implements OpenApiFactoryInterface
{
public function __construct(
private OpenApiFactoryInterface $decorated
) {}
public function __invoke(array $context = []): OpenApi
{
$openApi = ($this->decorated)($context);
$schemas = $openApi->getComponents()->getSchemas();
$schemas['Token'] = new \ArrayObject([
'type' => 'object',
'properties' => [
'token' => [
'type' => 'string',
'readOnly' => true,
],
],
]);
$schemas['Credentials'] = new \ArrayObject([
'type' => 'object',
'properties' => [
'username' => [
'type' => 'string',
'example' => 'test@gmail.com',
],
'password' => [
'type' => 'string',
'example' => '123456',
],
],
]);
$schemas = $openApi->getComponents()->getSecuritySchemes() ?? [];
$schemas['JWT'] = new \ArrayObject([
'type' => 'http',
'scheme' => 'bearer',
'bearerFormat' => 'JWT',
]);
$pathItem = new Model\PathItem(
ref: 'JWT Token',
post: new Model\Operation(
operationId: 'postCredentialsItem',
tags: ['Token'],
responses: [
'200' => [
'description' => 'Get JWT token',
'content' => [
'application/json' => [
'schema' => [
'$ref' => '#/components/schemas/Token',
],
],
],
],
],
summary: 'Get JWT token to login.',
requestBody: new Model\RequestBody(
description: 'Generate new JWT Token',
content: new \ArrayObject([
'application/json' => [
'schema' => [
'$ref' => '#/components/schemas/Credentials',
],
],
]),
),
security: [],
),
);
$openApi->getPaths()->addPath('/login', $pathItem);
return $openApi;
}
}
NotificationCategory.php
<?php
namespace App\Entity;
use ...
#[ORM\Entity(repositoryClass: NotificationCategoryRepository::class)]
#[ApiResource(
openapiContext: ['security' => [['Bearer Authentication' => []]]],
security: "is_granted('ROLE_USER')"
)]
class NotificationCategory
{
#[ORM\Id]
#[ORM\GeneratedValue]
#[ORM\Column]
private ?int $id = null;
#[ORM\Column(length: 255)]
#[Groups(['read:Notification:get'])]
private ?string $name = null;
#[ORM\OneToMany(mappedBy: 'category', targetEntity: Notification::class)]
private Collection $notifications;
...
}
I finally found. I was not using the correct schema name in my entity's openapiContext. In JwtDecorator file, I name the schema JWT
, and in NotificationCategory entity : Bearer Authentication
.
So I replaced :
#[ApiResource(
openapiContext: ['security' => [['Bearer Authentication' => []]]],
security: "is_granted('ROLE_USER')"
)]
By :
#[ApiResource(
openapiContext: ['security' => [['JWT' => []]]],
security: "is_granted('ROLE_USER')"
)]
It work.