lexik / LexikJWTAuthenticationBundle

JWT authentication for your Symfony API
MIT License
2.53k stars 610 forks source link

Trying to catch entered password for an API call #372

Closed maximebourdel closed 11 months ago

maximebourdel commented 7 years ago

Hello,

I installed LexikJWTAuthenticationBundle and everything is ok thank you for the tool

Now I would like to change my provider and make the check from an external API.

the workflow would be the following :

right know i know how to make this but i have to enter the password by myself into the provider class (something like $password = 'mypassword'... )

Do you know how could I get the password entered in the LOGIN form and catch it into my UserProvider implementing UserProviderInterface or even in my WebserviceUser implementing JWTUserInterface ?

I started with this : https://symfony.com/doc/current/security/custom_provider.html

Thank you in advance. Cordially

maximebourdel commented 7 years ago

explanation it would look like something like this. The problem is i don't know how to catch that password for a test on my OTHER website

(sorry it is paint cacoo was crashing...)

maximebourdel commented 7 years ago

I found a solution to catch password,

i had to edit "create" method for Lexik\Bundle\JWTAuthenticationBundle\Services\JWTManager

and add this line ,'password' => $user->getPassword()

so my provider now has its password in payloads.

` public function create(UserInterface $user) { $payload = [ 'roles' => $user->getRoles() ,'password' => $user->getPassword() ]; $this->addUserIdentityToPayload($user, $payload);

    $jwtCreatedEvent = new JWTCreatedEvent($payload, $user);
    $this->dispatcher->dispatch(Events::JWT_CREATED, $jwtCreatedEvent);

    $jwtString = $this->jwtEncoder->encode($jwtCreatedEvent->getData());

    $jwtEncodedEvent = new JWTEncodedEvent($jwtString);
    $this->dispatcher->dispatch(Events::JWT_ENCODED, $jwtEncodedEvent);

    return $jwtString;
}

`

And edit the "retrieveUser" method in the class Symfony\Component\Security\Core\Authentication\Provider\DaoAuthenticationProvider

and add this line , ['password' => $token->getCredentials()]

`

protected function retrieveUser($username, UsernamePasswordToken $token) { $user = $token->getUser(); if ($user instanceof UserInterface) { return $user; }

    try {
        $user = $this->userProvider->loadUserByUsername($username, ['password' => $token->getCredentials()]);

        if (!$user instanceof UserInterface) {
            throw new AuthenticationServiceException('The user provider must return a UserInterface object.');
        }

        return $user;
    } catch (UsernameNotFoundException $e) {
        $e->setUsername($username);
        throw $e;
    } catch (\Exception $e) {
        $e = new AuthenticationServiceException($e->getMessage(), 0, $e);
        $e->setToken($token);
        throw $e;
    }
}`

It works fine but the problem is it is not really good to edit inside vendors.

So i would like to know how i could override those two methods.

chalasr commented 7 years ago

Hi @maximebourdel, apologies for my late answer. I would suggest to write your own FormLoginAuthenticator, you should find inspiration in https://knpuniversity.com/screencast/guard/login-form. Idea is that your custom user provider needs to be aware of username and password, which is not the most common, so you need some custom form login. Guard is the simplest approach for achieving this, and seems perfectly adapted since this is very specific to your project. Your form login authenticator would just gives both keys to the provider.

i had to edit "create" method for Lexik\Bundle\JWTAuthenticationBundle\Services\JWTManager and add this line ,'password' => $user->getPassword()

Putting an hardcoded password in the token payload is not a good idea. You should rollback, really :)

maximebourdel commented 7 years ago

Hi @chalasr, thank you for the answer, I will try to implement it tomorrow :) But, does that mean i do not need to use LexikJWT ? Will i still have a token system with this solution ?

chalasr commented 7 years ago

Sure, I mean that instead of using the traditional built-in symfony form_login, you need to write some custom logic. Do not hesitate to ask here if you have any doubt when implementing, I'll try to be more reactive.

maximebourdel commented 7 years ago

Aheum, Sorry but I asked two questions so I did not understand what you meant by "sure".

What I understand is that i will have to create my own logic with form_login but do i have to uninstall lexik ?

Another thing i did not said is that my own bundle has its own entities. These entities return json thanks to FOSRestBundle. I made a route called /api and i want this route to be only reachable by authenticated users. Is that solution still ok with this system ?

Thank you in advance.

maximebourdel commented 7 years ago

hum, it seems KnpUGuardBundle is requiered and it is deprecated since 2.8 and i use 3.2 for my project. can i replace some parts of the tutorial with that part i saw in your git ? https://github.com/lexik/LexikJWTAuthenticationBundle/blob/8386bd1b83e75aef497e024219ee24978db527d6/Resources/doc/6-extending-jwt-authenticator.md It looks very similar.

maximebourdel commented 7 years ago

Hi @chalasr

I made a project apart in symfony 2.8 and managed to create my authentication with this exemple. But now i would like to move it to my symfony 3.2 initial project.

It looks like after checkCredentials method, KnpU\Guard\Authenticator\AbstractFormLoginAuthenticator is called and this is the part i would like to replace.

Do you know how i could replace that call ?

Thank you in advance