API Platform 4 + Symfony 7 + JWT аутентификация = недействительные учетные данные

Вопрос или проблема

Попробуйте создать проект API с использованием последнего стека SF7, APIPlatform 4 и JWT.

API работает хорошо для генерации JWT токена в URL \auth

НО всегда возвращает мне ОШИБКУ: 401 Неверные учетные данные

Много попыток решить эту проблему в интернете и на stackOverflow, но ничего не помогает.

Возможно, проблема или ошибка связана с конкретным стеком…

Буду благодарен за ваши советы

Эти требования выполнены:

  • генерация JWT ключа

  • активация JWT lexik bundle в bundles.php

  • getUserIdentifier() установлен в User Entity

security.yaml

security:
password_hashers:
    Symfony\Component\Security\Core\User\PasswordAuthenticatedUserInterface: 'auto'

providers:
    app_user_provider:
        entity:
            class: App\Entity\Users
            property: email

firewalls:

    dev:
        pattern: ^/_(profiler|wdt)
        security: false

    main:
        stateless: true
        provider: app_user_provider
        json_login:
            check_path: auth
            username_path: email
            password_path: password
            success_handler: lexik_jwt_authentication.handler.authentication_success
            failure_handler: lexik_jwt_authentication.handler.authentication_failure
        jwt: ~

access_control:
    - { path: ^/$, roles: PUBLIC_ACCESS }
    - { path: ^/docs, roles: PUBLIC_ACCESS }
    - { path: ^/auth, roles: PUBLIC_ACCESS }
    - { path: ^/, roles: IS_AUTHENTICATED_FULLY }

lexik_jwt_authentication.yaml

lexik_jwt_authentication:

secret_key: '%env(resolve:JWT_SECRET_KEY)%'
public_key: '%env(resolve:JWT_PUBLIC_KEY)%'
pass_phrase: '%env(resolve:JWT_PASSPHRASE)%'

routes/api_platform.yaml

api_platform:
    resource: .
    type: api_platform
    prefix: /api

routes.yaml

auth:
  path: /auth
  methods: ['POST']

controllers:
    resource:
        path: ../src/Controller/
        namespace: App\Controller
    type: attribute

UserFixtures.php :

    $user1 = new Users();
    $user1->setCivility('mr');
    $user1->setEmail('[email protected]');
    $user1->setRoles(['ROLE_SUPER_ADMIN']);
    $hashedPassword = $this->hasher->hashPassword($user1, 'rere-toto');
    $user1->setPassword($hashedPassword);
    $user1->setUsername('jordy');
    $user1->setFirstName('michael');
    $user1->setLastName('jordan');

    $manager->persist($user1);
    $manager->flush();

Users.php (Сущность)

<?php

namespace App\Entity;

use ApiPlatform\Metadata\ApiResource;
use App\Repository\UsersRepository;
use Doctrine\ORM\Mapping as ORM;
use Doctrine\ORM\Mapping\JoinColumn;
use Doctrine\ORM\Mapping\ManyToOne;
use Symfony\Bridge\Doctrine\Validator\Constraints\UniqueEntity;
use Symfony\Component\Security\Core\User\PasswordAuthenticatedUserInterface;
use Symfony\Component\Security\Core\User\UserInterface;

#[ORM\Entity(repositoryClass: UsersRepository::class)]
#[ORM\UniqueConstraint(name: 'UNIQ_IDENTIFIER_EMAIL', fields: ['email'])]
#[UniqueEntity(fields: ['email'], message: 'Учетная запись с этим email уже существует')]
#[ApiResource]
class Users implements UserInterface, PasswordAuthenticatedUserInterface
{
    #[ORM\Id]
    #[ORM\GeneratedValue]
    #[ORM\Column]
    private ?int $id = null;

    #[ORM\Column(length: 180)]
    private ?string $email = null;

    #[ORM\Column]
    private array $roles = [];

    #[ORM\Column(type: 'string')]
    private ?string $password = null;

    #[ORM\Column(type: 'boolean')]
    private bool $isVerified = false;

    #[ORM\Column(type: 'string')]
    private string $civility;

    #[ORM\Column(type: 'string')]
    private string $firstName;

    #[ORM\Column(type: 'string')]
    private string $lastName;

    #[ORM\Column(type: 'string')]
    private string $username;

    #[ORM\Column(type: 'blob', nullable: true)]
    private string $picture;

    #[ORM\Column(type: 'string', length: 2)]
    private string $locale="fr";

    public function getId(): ?int
    {
        return $this->id;
    }

    public function getEmail(): ?string
    {
        return $this->email;
    }

    public function setEmail(string $email): static
    {
        $this->email = $email;

        return $this;
    }

    public function getUserIdentifier(): string
    {
        return (string) $this->email;
    }

    public function getRoles(): array
    {
        $roles = $this->roles;
        // гарантирует, что каждый пользователь имеет хотя бы ROLE_USER
        $roles[] = 'ROLE_USER';

        return array_unique($roles);
    }

    public function setRoles(array $roles): static
    {
        $this->roles = $roles;

        return $this;
    }

    public function getPassword(): string
    {
        return $this->password;
    }

    public function setPassword(string $password): static
    {
        $this->password = $password;

        return $this;
    }

    public function getCivility(): string
    {
        return $this->civility;
    }

    public function setCivility(string $civility): void
    {
        $this->civility = $civility;
    }

    public function getFirstName(): string
    {
        return $this->firstName;
    }

    public function setFirstName(string $firstName): void
    {
        $this->firstName = $firstName;
    }

    public function getLastName(): string
    {
        return $this->lastName;
    }

    public function setLastName(string $lastName): void
    {
        $this->lastName = $lastName;
    }

    public function getUsername(): string
    {
        return $this->username;
//      return $this->email;
    }

    public function setUsername(string $username): void
    {
        $this->username = $username;
    }

    public function getLocale(): string
    {
        return $this->locale;
    }

    public function setLocale(string $locale): void
    {
        $this->locale = $locale;
    }

    public function eraseCredentials(): void
    {
        // Если у вас есть какие-либо временные, чувствительные данные о пользователе, очистите их здесь
        // $this->plainPassword = null;
    }

    public function getIsVerified(): bool
    {
        return $this->isVerified;
    }

    public function setIsVerified(bool $isVerified): static
    {
        $this->isVerified = $isVerified;

        return $this;
    }

    public function getPicture(): string
    {
        return $this->picture;
    }

    public function setPicture(string $picture): void
    {
        $this->picture = $picture;
    }
}

.env

JWT_SECRET_KEY=%kernel.project_dir%/config/jwt/private.pem
JWT_PUBLIC_KEY=%kernel.project_dir%/config/jwt/public.pem
JWT_PASSPHRASE=a0b919dGJxxxxxxcaa29fkh56bxxxxx2fde41f13381b7dac864

Ответ или решение

Разбор проблемы: API Platform 4 + Symfony 7 + JWT аутентификация — неверные учетные данные

Ваша проблема с получением ошибки 401 Invalid credentials при попытке аутентификации через JWT в стеке Symfony 7 и API Platform 4 может быть вызвана несколькими факторами. Давайте рассмотрим решение шаг за шагом.

1. Проверка настройки безопасности в security.yaml

Ваш файл конфигурации security.yaml выглядит в целом корректно. Однако важно убедиться, что все настроено как следует:

security:
    password_hashers:
        Symfony\Component\Security\Core\User\PasswordAuthenticatedUserInterface: 'auto'

    providers:
        app_user_provider:
            entity:
                class: App\Entity\Users
                property: email

    firewalls:
        dev:
            pattern: ^/_(profiler|wdt)
            security: false

        main:
            stateless: true
            provider: app_user_provider
            json_login:
                check_path: auth
                username_path: email
                password_path: password
                success_handler: lexik_jwt_authentication.handler.authentication_success
                failure_handler: lexik_jwt_authentication.handler.authentication_failure
            jwt: ~

    access_control:
        - { path: ^/$, roles: PUBLIC_ACCESS }
        - { path: ^/docs, roles: PUBLIC_ACCESS }
        - { path: ^/auth, roles: PUBLIC_ACCESS }
        - { path: ^/, roles: IS_AUTHENTICATED_FULLY }

Проверьте следующие моменты:

  • Убедитесь, что путь auth правильно настроен и доступен для POST-запросов. Также проверьте, что настройки CORS не блокируют ваш запрос, если вы используете его из браузера.
  • Убедитесь, что вы передаете правильные данные (email и password) в запросе.

2. Проверка формирования и валидации JWT

Ваша настройка для lexik_jwt_authentication.yaml:

lexik_jwt_authentication:
    secret_key: '%env(resolve:JWT_SECRET_KEY)%'
    public_key: '%env(resolve:JWT_PUBLIC_KEY)%'
    pass_phrase: '%env(resolve:JWT_PASSPHRASE)%'

Убедитесь, что:

  • Путь к файлам ключей (private.pem и public.pem) актуален и файлы существуют.
  • Пароль (passphrase) для ключа приватного API корректный.

Для генерации токена попробуйте с помощью команды Symfony:

php bin/console lexik:jwt:generate-token <user_id>

Это позволит удостовериться, что ключи и пароли настроены правильно.

3. Пользовательская сущность и протокол аутентификации

Ваш класс Users реализует интерфейсы UserInterface и PasswordAuthenticatedUserInterface, что является правильным. Однако, стоит обратить внимание на метод getUserIdentifier(). Он должен возвращать уникальный идентификатор пользователя, который вы используете для аутентификации. Убедитесь, что метод возвращает корректное значение.

public function getUserIdentifier(): string
{
    return (string) $this->email; // Убедитесь, что email действительно уникален
}

Также убедитесь, что пользователь с указанным email существует и его пароль захеширован корректно.

4. Проверка данных из файла .env

Ваш env файл должен содержать корректные значения ключей и паролей. Неправильная конфигурация может вести к ошибкам аутентификации. Например:

JWT_SECRET_KEY=%kernel.project_dir%/config/jwt/private.pem
JWT_PUBLIC_KEY=%kernel.project_dir%/config/jwt/public.pem
JWT_PASSPHRASE=ваш_пароль

Заключение

Если после выполнения всех вышеуказанных рекомендаций проблема сохраняется, попробуйте отладить процесс аутентификации, добавив временные сообщения в обработчики успешной и неуспешной аутентификации, чтобы лучше понять, где происходит сбой.

Также проверьте логи Symfony и консольные команды для обнаружения возможных ошибок. Успехов в решении проблемы!

Оцените материал
Добавить комментарий

Капча загружается...