Bonjour tout le monde,
J'espère que vous allez bien ?
Depuis plusieurs jours, je me bats pour mettre en place l'authentification JWT sur une API construit sous Symfony 5.4 avec APIPlatform et je déprime, car je n'y arrive pas !
J'ai suivi entre autre le tutoriel suivant : https://www.youtube.com/watch?v=XPXrNI-fux4&t=59s malheureusement cela ne fonctionne pas comme attendu.
La requête api/login fonctionne bien (elle me renvoie un token) sur postman et sur ApiPlatform sauf que sur ApiPlatform, je n'arrive pas à intégrer le "bearerAuth" dans la partie Authorization. Ceci est à mon avis (je peux me tromper) dû à mon autre problème.
Le problème c'est que lorsque je fais appel à ma seconde méthode "api/apiMe cela me renvoie le message suivant :
{
"@context": "/api/contexts/Error",
"@type": "hydra:Error",
"hydra:title": "An error occurred",
"hydra:description": "Call to a member function getUser() on null",
"trace": [
{
"namespace": "",
"short_class": "",
"class": "",
"type": "",
"function": "",
"file": "/home/znbf0470/public_html/_dev-erp/src/Controller/ApiMeController.php",
"line": 14,
"args": []
},
{
"namespace": "App\\Controller",
"short_class": "ApiMeController",
"class": "App\\Controller\\ApiMeController",
"type": "->",
"function": "__invoke",
"file": "/home/znbf0470/public_html/_dev-erp/vendor/symfony/http-kernel/HttpKernel.php",
"line": 153,
"args": []
},
{
"namespace": "Symfony\\Component\\HttpKernel",
"short_class": "HttpKernel",
"class": "Symfony\\Component\\HttpKernel\\HttpKernel",
"type": "->",
"function": "handleRaw",
"file": "/home/znbf0470/public_html/_dev-erp/vendor/symfony/http-kernel/HttpKernel.php",
"line": 75,
"args": [
[
"object",
"Symfony\\Component\\HttpFoundation\\Request"
],
[
"integer",
1
]
]
},
{
"namespace": "Symfony\\Component\\HttpKernel",
"short_class": "HttpKernel",
"class": "Symfony\\Component\\HttpKernel\\HttpKernel",
"type": "->",
"function": "handle",
"file": "/home/znbf0470/public_html/_dev-erp/vendor/symfony/http-kernel/Kernel.php",
"line": 202,
"args": [
[
"object",
"Symfony\\Component\\HttpFoundation\\Request"
],
[
"integer",
1
],
[
"boolean",
true
]
]
},
{
"namespace": "Symfony\\Component\\HttpKernel",
"short_class": "Kernel",
"class": "Symfony\\Component\\HttpKernel\\Kernel",
"type": "->",
"function": "handle",
"file": "/home/znbf0470/public_html/_dev-erp/vendor/symfony/runtime/Runner/Symfony/HttpKernelRunner.php",
"line": 35,
"args": [
[
"object",
"Symfony\\Component\\HttpFoundation\\Request"
]
]
},
{
"namespace": "Symfony\\Component\\Runtime\\Runner\\Symfony",
"short_class": "HttpKernelRunner",
"class": "Symfony\\Component\\Runtime\\Runner\\Symfony\\HttpKernelRunner",
"type": "->",
"function": "run",
"file": "/home/znbf0470/public_html/_dev-erp/vendor/autoload_runtime.php",
"line": 35,
"args": []
},
{
"namespace": "",
"short_class": "",
"class": "",
"type": "",
"function": "require_once",
"file": "/home/znbf0470/public_html/_dev-erp/public/index.php",
"line": 5,
"args": [
[
"string",
"/home/znbf0470/public_html/_dev-erp/vendor/autoload_runtime.php"
]
]
}
]
}
Ci-dessous le code du fichier security.yaml :
firewalls:
dev:
pattern: ^/(_(profiler|wdt)|css|images|js)/
security: false
login:
pattern: ^/api/login$
stateless: true
json_login:
check_path: /api/login
success_handler: lexik_jwt_authentication.handler.authentication_success
failure_handler: lexik_jwt_authentication.handler.authentication_failure
main:
lazy: true
provider: app_user_provider
entry_point: App\Security\AppAuthenticator
custom_authenticator:
- App\Security\AppAuthenticator
form_login:
login_path: app_login
check_path: app_login
logout:
path: app_logout
target: app_login
api:
pattern: ^/api
stateless: true
guard:
authenticators:
- lexik_jwt_authentication.jwt_token_authenticator
Ci-dessous le code de la class OpenApiFactory :
<?php
namespace App\OpenApi;
use ApiPlatform\OpenApi\Factory\OpenApiFactoryInterface;
use ApiPlatform\OpenApi\OpenApi;
use ApiPlatform\OpenApi\Model;
class OpenApiFactory implements OpenApiFactoryInterface
{
private $decorated;
public function __construct(OpenApiFactoryInterface $decorated)
{
$this->decorated = $decorated;
}
public function __invoke(array $context = []): OpenApi
{
$openApi = $this->decorated->__invoke($context);
foreach($openApi->getPaths()->getPaths() as $key => $path) {
if($path->getGet() && $path->getGet->getSummary() === 'hidden') {
$openApi->getPaths()->addPath($key, $path->withGet(null));
}
}
$schemas = $openApi->getComponents()->getSecuritySchemes();
$schemas['bearerAuth'] = new \ArrayObject([
'type' => 'http',
'scheme' => 'bearer',
'bearerFormat' => 'JWT'
]);
$schemas = $openApi->getComponents()->getSchemas();
$schemas['Credentials'] = new \ArrayObject([
'type' => 'object',
'properties' => [
'username' => [
'type' => 'string',
'example' => 'john@doe.fr',
],
'password' => [
'type' => 'string',
'example' => 'mdp',
]
]
]);
return $openApi;
}
}
?>
Ci-dessous le code du controller ApiMeController :
<?php
namespace App\Controller;
use Symfony\Component\Security\Core\Security;
class ApiMeController
{
private $security;
public function __construct(Security $security){}
public function __invoke() {
$user = $this->security->getUser();
return $user;
}
}
Ci-dessous le code de l'entité User :
<?php
namespace App\Entity;
use App\Repository\UserRepository;
use App\Controller\ApiMeController;
use Doctrine\ORM\Mapping as ORM;
use Symfony\Component\Security\Core\User\PasswordAuthenticatedUserInterface;
use Symfony\Component\Security\Core\User\UserInterface;
use ApiPlatform\Core\Annotation\ApiResource;
use Doctrine\Common\Collections\ArrayCollection;
use Doctrine\Common\Collections\Collection;
use Symfony\Component\Serializer\Annotation\Groups;
/**
@ApiResource(
collectionOperations={
"apiMe"={
"pagination_enabled": false,
"path": "/apiMe",
"method": "get",
"controller": ApiMeController::class,
"read": false,
"openapi_context"={
"security"={"cookieAuth"={}}
},
}
},
itemOperations={
"get"={
"controller": NotFoundAction::class,
"openapi_context"={
"summary":"hidden"
},
"read":false,
"output":false
},
"apiMe"={
"pagination_enabled": false,
"path": "/apiMe",
"method": "get",
"controller": ApiMeController::class,
"read": false,
"openapi_context"={
"security"={"cookieAuth"={}}
},
}
},
normalizationContext={"groups"={"user:read"}}
)
@ORM\Entity(repositoryClass=UserRepository::class)
@ORM\Table(name="`user`")
class User implements UserInterface, PasswordAuthenticatedUserInterface {
...
}
Le problème vient du controller ApiMe. J'ai l'impression que Security ne fonctionne pas.
Ai-je oublié quelque chose ?
Avez-vous une idée pour m'aider à résoudre ce problème s'il vous plaît ?
Merci par avance pour votre aide !