Lexikjwtauthenticationbundle: Messages translation

Created on 8 Oct 2019  ·  6Comments  ·  Source: lexik/LexikJWTAuthenticationBundle

Hello.

It's quite hard to provide translated error message generated by this bundle. For example message "Bad credentials" is not passed through translator as it is with similar message in Security Bundle of the Symfony (link).
What do you think about this change? If it's acceptable, then I can prepare PR.

Thanks, Vlad.

Most helpful comment

Extending the auth failure handler is a bit overhead to me.
Just add an event listener or subscriber for lexik_jwt_authentication.on_authentication_failure event as described here for example:
https://github.com/lexik/LexikJWTAuthenticationBundle/blob/master/Resources/doc/2-data-customization.md#eventsauthentication_failure---customizing-the-failure-response-body

and then update the response message with translated one.

As an example:

namespace App\EventSubscriber;

use Lexik\Bundle\JWTAuthenticationBundle\Event\AuthenticationFailureEvent;
use Lexik\Bundle\JWTAuthenticationBundle\Response\JWTAuthenticationFailureResponse;
use Symfony\Component\EventDispatcher\EventSubscriberInterface;
use Symfony\Contracts\Translation\TranslatorInterface;

class TranslateJWTAuthenticationFailureResponse implements EventSubscriberInterface
{
    /** @var TranslatorInterface */
    private $translator;

    public function __construct(TranslatorInterface $translator)
    {
        $this->translator = $translator;
    }

    public static function getSubscribedEvents(): array
    {
        return [
            'lexik_jwt_authentication.on_authentication_failure' => 'onAuthenticationFailureResponse',
        ];
    }

    public function onAuthenticationFailureResponse(AuthenticationFailureEvent $event): void
    {
        /** @var JWTAuthenticationFailureResponse $response */
        $response = $event->getResponse();

        $response->setMessage(
            $this->translator->trans(
                $response->getMessage()
            )
        );
    }
}

and add required translations:

# translations/messages.ru.yml
'Invalid credentials.': "Неверный адрес электронной почты или пароль"

All 6 comments

I don't think there is a need for change in the bundle. The "Bad credentials" message usually comes from Exception and I think Exception messages are for developers and not for end users.

Maybe a decorator of AuthenticationFailureHandler will be the best solution among many others.

@EresDev I did it in this way, but for me, it's a little bit overhead to invent an decorator/listener to replace the message provided by bundle. For me, security bundle is the great example in this case, but you decide.

Here is one quick solution. Simple inheritance.

<?php

namespace App\ThirdParty\Security\Lexik;

use Lexik\Bundle\JWTAuthenticationBundle\Event\AuthenticationFailureEvent;
use Lexik\Bundle\JWTAuthenticationBundle\Events;
use Lexik\Bundle\JWTAuthenticationBundle\Response\JWTAuthenticationFailureResponse;
use Lexik\Bundle\JWTAuthenticationBundle\Security\Http\Authentication\AuthenticationFailureHandler;
use Symfony\Component\EventDispatcher\EventDispatcherInterface;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\Security\Core\Exception\AuthenticationException;
use Symfony\Contracts\EventDispatcher\EventDispatcherInterface as ContractsEventDispatcherInterface;
use Symfony\Contracts\Translation\TranslatorInterface;

class TranslatedAuthenticationFailureHandler extends AuthenticationFailureHandler
{
    private $translator;

    public function __construct(EventDispatcherInterface $dispatcher, TranslatorInterface $translator)
    {
        parent::__construct($dispatcher);

        $this->translator = $translator;
    }

    public function onAuthenticationFailure(Request $request, AuthenticationException $exception)
    {

//        $event = new AuthenticationFailureEvent(
//            $exception,
//            new JWTAuthenticationFailureResponse($exception->getMessage())
//        );

        $event = new AuthenticationFailureEvent(
            $exception,
            new JWTAuthenticationFailureResponse(
                $this->translator->trans('authentication_failure_error_message')
            )
        );

        if ($this->dispatcher instanceof ContractsEventDispatcherInterface) {
            $this->dispatcher->dispatch($event, Events::AUTHENTICATION_FAILURE);
        } else {
            $this->dispatcher->dispatch(Events::AUTHENTICATION_FAILURE, $event);
        }

        return $event->getResponse();
    }
}

security.yaml

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

        login:
            pattern:  ^/login
            stateless: true
            anonymous: true
            provider: custom_provider
            json_login:
                check_path:  /login_check
                username_path: email
                password_path: password
                success_handler: lexik_jwt_authentication.handler.authentication_success
                failure_handler: App\ThirdParty\Security\Lexik\TranslatedAuthenticationFailureHandler

Yep, I did this in the same way, but for me, it's still overhead just for one message.

Well, that is all I got. If you find some better way, don't forget to share.

Extending the auth failure handler is a bit overhead to me.
Just add an event listener or subscriber for lexik_jwt_authentication.on_authentication_failure event as described here for example:
https://github.com/lexik/LexikJWTAuthenticationBundle/blob/master/Resources/doc/2-data-customization.md#eventsauthentication_failure---customizing-the-failure-response-body

and then update the response message with translated one.

As an example:

namespace App\EventSubscriber;

use Lexik\Bundle\JWTAuthenticationBundle\Event\AuthenticationFailureEvent;
use Lexik\Bundle\JWTAuthenticationBundle\Response\JWTAuthenticationFailureResponse;
use Symfony\Component\EventDispatcher\EventSubscriberInterface;
use Symfony\Contracts\Translation\TranslatorInterface;

class TranslateJWTAuthenticationFailureResponse implements EventSubscriberInterface
{
    /** @var TranslatorInterface */
    private $translator;

    public function __construct(TranslatorInterface $translator)
    {
        $this->translator = $translator;
    }

    public static function getSubscribedEvents(): array
    {
        return [
            'lexik_jwt_authentication.on_authentication_failure' => 'onAuthenticationFailureResponse',
        ];
    }

    public function onAuthenticationFailureResponse(AuthenticationFailureEvent $event): void
    {
        /** @var JWTAuthenticationFailureResponse $response */
        $response = $event->getResponse();

        $response->setMessage(
            $this->translator->trans(
                $response->getMessage()
            )
        );
    }
}

and add required translations:

# translations/messages.ru.yml
'Invalid credentials.': "Неверный адрес электронной почты или пароль"
Was this page helpful?
0 / 5 - 0 ratings

Related issues

evaldoprestes picture evaldoprestes  ·  3Comments

tsdevelopment picture tsdevelopment  ·  4Comments

mflasquin picture mflasquin  ·  5Comments

ghost picture ghost  ·  5Comments

tahina4 picture tahina4  ·  4Comments