Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Messages translation #684

Closed
gregurco opened this issue Oct 8, 2019 · 8 comments
Closed

Messages translation #684

gregurco opened this issue Oct 8, 2019 · 8 comments

Comments

@gregurco
Copy link

gregurco commented Oct 8, 2019

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.

@EresDev
Copy link

EresDev commented Oct 14, 2019

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.

@gregurco
Copy link
Author

@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.

@EresDev
Copy link

EresDev commented Oct 14, 2019

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

@gregurco
Copy link
Author

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

@EresDev
Copy link

EresDev commented Oct 16, 2019

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

chalasr added a commit that referenced this issue Oct 26, 2019
…is build env, and .idea to gitignore (EresDev)

This PR was squashed before being merged into the 2.x-dev branch (closes #687).

Discussion
----------

Authentication Exception Message from its key, Explicit Travis build env, and .idea to gitignore

There are 3 issues addressed here. I would have used different PR for each issue but there was problem with travis build env that wouldn't have produced accurate results if not combined.

1. Updated Travis build env as you can see php 5.5 build are failing. Travis has recently [changed it default build env to Xenial](https://blog.travis-ci.com/2019-04-15-xenial-default-build-environment) and I have explicitly specified it to be "Trusty".

2. Addresses [the concern](#650 (comment)) of Authentication Exception message to be retrieved by getMessage. I have changed it to getMessageKey method to meet symfony convention, help translation and prevent possible leak of sensitive info to end user. Also addresses #684

3. Adds .idea to .gitignore
Most of use PHPStorm and it creates .idea folder that is an issue while working. It should be in .gitignore to prevent its accidental addition to repository. It also helps to work on this library quickly as you don't have to deal with .idea first.

Commits
-------

abc1a01 Added explicit build environment for travis. Because Travis has changed its default build enviorment to Xenial and it does not compile php 5.5. Making dist: trusty
e1fcdb5 Getting exception message via getMessageKey method to meet Symfony convention, to help with translation, and to prevent possible leak of sensitive information.
a5d5109 Adding .idea to .gitignore to prevent accidental commit/push of IDE files.
@bravik
Copy link
Contributor

bravik commented May 24, 2020

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.': "Неверный адрес электронной почты или пароль"

@fd6130
Copy link

fd6130 commented Sep 18, 2021

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.': "Неверный адрес электронной почты или пароль"

Overhead is unavoidable because of customization.

I think this is the most acceptable answer.

@chalasr
Copy link
Collaborator

chalasr commented Dec 5, 2021

Duplicate of #820, PR still welcome!

@chalasr chalasr closed this as completed Dec 5, 2021
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

5 participants